diff options
242 files changed, 5843 insertions, 2770 deletions
diff --git a/Android.bp b/Android.bp index ecdc0827e851..8d25704883fd 100644 --- a/Android.bp +++ b/Android.bp @@ -746,6 +746,7 @@ java_defaults { "game-driver-protos", "mediaplayer2-protos", "android.hidl.base-V1.0-java", + "android.hardware.cas-V1.1-java", "android.hardware.cas-V1.0-java", "android.hardware.contexthub-V1.0-java", "android.hardware.health-V1.0-java-constants", diff --git a/api/current.txt b/api/current.txt index 417ae80b81a4..50ff701f1d52 100644 --- a/api/current.txt +++ b/api/current.txt @@ -24051,7 +24051,8 @@ package android.media { } public static interface MediaCas.EventListener { - method public void onEvent(android.media.MediaCas, int, int, @Nullable byte[]); + method public void onEvent(@NonNull android.media.MediaCas, int, int, @Nullable byte[]); + method public default void onSessionEvent(@NonNull android.media.MediaCas, @NonNull android.media.MediaCas.Session, int, int, @Nullable byte[]); } public static class MediaCas.PluginDescriptor { @@ -24063,6 +24064,7 @@ package android.media { method public void close(); method public void processEcm(@NonNull byte[], int, int) throws android.media.MediaCasException; method public void processEcm(@NonNull byte[]) throws android.media.MediaCasException; + method public void sendSessionEvent(int, int, @Nullable byte[]) throws android.media.MediaCasException; method public void setPrivateData(@NonNull byte[]) throws android.media.MediaCasException; } @@ -24140,8 +24142,10 @@ package android.media { field public static final int INFO_OUTPUT_FORMAT_CHANGED = -2; // 0xfffffffe field public static final int INFO_TRY_AGAIN_LATER = -1; // 0xffffffff field public static final String PARAMETER_KEY_HDR10_PLUS_INFO = "hdr10-plus-info"; + field public static final String PARAMETER_KEY_OFFSET_TIME = "time-offset-us"; field public static final String PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync"; field public static final String PARAMETER_KEY_SUSPEND = "drop-input-frames"; + field public static final String PARAMETER_KEY_SUSPEND_TIME = "drop-start-time-us"; field public static final String PARAMETER_KEY_VIDEO_BITRATE = "video-bitrate"; field public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT = 1; // 0x1 field public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2; // 0x2 @@ -24986,6 +24990,7 @@ package android.media { field public static final String KEY_COLOR_STANDARD = "color-standard"; field public static final String KEY_COLOR_TRANSFER = "color-transfer"; field public static final String KEY_COMPLEXITY = "complexity"; + field public static final String KEY_CREATE_INPUT_SURFACE_SUSPENDED = "create-input-buffers-suspended"; field public static final String KEY_DURATION = "durationUs"; field public static final String KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level"; field public static final String KEY_FRAME_RATE = "frame-rate"; @@ -44131,7 +44136,7 @@ package android.telephony { } public final class AvailableNetworkInfo implements android.os.Parcelable { - ctor public AvailableNetworkInfo(int, int, java.util.ArrayList<java.lang.String>); + ctor public AvailableNetworkInfo(int, int, java.util.List<java.lang.String>); method public int describeContents(); method public java.util.List<java.lang.String> getMccMncs(); method public int getPriority(); @@ -45093,7 +45098,7 @@ package android.telephony { method public int getNetworkType(); method public int getPhoneCount(); method public int getPhoneType(); - method public int getPreferredOpportunisticDataSubscription(); + method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.ServiceState getServiceState(); method @Nullable public android.telephony.SignalStrength getSignalStrength(); method public int getSimCarrierId(); @@ -45129,6 +45134,7 @@ package android.telephony { method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled(); method public boolean isHearingAidCompatibilitySupported(); method public boolean isNetworkRoaming(); + method public boolean isRttSupported(); method public boolean isSmsCapable(); method @Deprecated public boolean isTtyModeSupported(); method public boolean isVoiceCapable(); @@ -45661,9 +45667,9 @@ package android.telephony.mbms { } public interface GroupCallCallback { - method public void onBroadcastSignalStrengthUpdated(@IntRange(from=0xffffffff, to=4) int); - method public void onError(int, @Nullable String); - method public void onGroupCallStateChanged(int, int); + method public default void onBroadcastSignalStrengthUpdated(@IntRange(from=0xffffffff, to=4) int); + method public default void onError(int, @Nullable String); + method public default void onGroupCallStateChanged(int, int); field public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1; // 0xffffffff } @@ -45721,10 +45727,10 @@ package android.telephony.mbms { } public interface MbmsGroupCallSessionCallback { - method public void onAvailableSaisUpdated(@NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.util.List<java.lang.Integer>>); - method public void onError(int, @Nullable String); - method public void onMiddlewareReady(); - method public void onServiceInterfaceAvailable(@NonNull String, int); + method public default void onAvailableSaisUpdated(@NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.util.List<java.lang.Integer>>); + method public default void onError(int, @Nullable String); + method public default void onMiddlewareReady(); + method public default void onServiceInterfaceAvailable(@NonNull String, int); } public class MbmsStreamingSessionCallback { @@ -50663,7 +50669,7 @@ package android.view { method public void setClickable(boolean); method public void setClipBounds(android.graphics.Rect); method public void setClipToOutline(boolean); - method public void setContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSession); + method public void setContentCaptureSession(@Nullable android.view.contentcapture.ContentCaptureSession); method public void setContentDescription(CharSequence); method public void setContextClickable(boolean); method public void setDefaultFocusHighlightEnabled(boolean); @@ -53405,6 +53411,11 @@ package android.view.inputmethod { package android.view.inspector { + public class GeneratedInspectionCompanionProvider implements android.view.inspector.InspectionCompanionProvider { + ctor public GeneratedInspectionCompanionProvider(); + method @Nullable public <T> android.view.inspector.InspectionCompanion<T> provide(@NonNull Class<T>); + } + public interface InspectionCompanion<T> { method @Nullable public default String getNodeName(); method public void mapProperties(@NonNull android.view.inspector.PropertyMapper); @@ -53415,6 +53426,10 @@ package android.view.inspector { ctor public InspectionCompanion.UninitializedPropertyMapException(); } + public interface InspectionCompanionProvider { + method @Nullable public <T> android.view.inspector.InspectionCompanion<T> provide(@NonNull Class<T>); + } + public final class IntEnumMapping { method @Nullable public String get(int); } diff --git a/api/system-current.txt b/api/system-current.txt index f9403ffc3c79..a9ef144313a8 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3322,6 +3322,7 @@ package android.location { method public boolean isLocationControllerExtraPackageEnabled(); method public boolean isLocationEnabledForUser(android.os.UserHandle); method public boolean isProviderEnabledForUser(String, android.os.UserHandle); + method public boolean isProviderPackage(String); method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean registerGnssBatchedLocationCallback(long, boolean, android.location.BatchedLocationCallback, android.os.Handler); method @Deprecated public void removeGpsMeasurementListener(android.location.GpsMeasurementsEvent.Listener); method @Deprecated public void removeGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener); @@ -5769,6 +5770,10 @@ package android.provider { field public static final String NAMESPACE = "runtime_native"; } + public static interface DeviceConfig.RuntimeNativeBoot { + field public static final String NAMESPACE = "runtime_native_boot"; + } + public static interface DeviceConfig.Storage { field public static final String ISOLATED_STORAGE_ENABLED = "isolated_storage_enabled"; field public static final String NAMESPACE = "storage"; @@ -5962,7 +5967,6 @@ package android.provider { field public static final String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length"; field public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length"; field public static final String COMPLETED_CATEGORY_PREFIX = "suggested.completed_category."; - field public static final String CONTENT_CAPTURE_ENABLED = "content_capture_enabled"; field public static final String DOZE_ALWAYS_ON = "doze_always_on"; field public static final String HUSH_GESTURE_USED = "hush_gesture_used"; field public static final String INSTANT_APPS_ENABLED = "instant_apps_enabled"; @@ -6304,8 +6308,6 @@ package android.service.contentcapture { public abstract class ContentCaptureService extends android.app.Service { ctor public ContentCaptureService(); - method @NonNull public final java.util.Set<android.content.ComponentName> getContentCaptureDisabledActivities(); - method @NonNull public final java.util.Set<java.lang.String> getContentCaptureDisabledPackages(); method public void onActivitySnapshot(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.service.contentcapture.SnapshotData); method public void onConnected(); method public void onContentCaptureEvent(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.view.contentcapture.ContentCaptureEvent); @@ -6314,9 +6316,7 @@ package android.service.contentcapture { method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId); method public void onDisconnected(); method public void onUserDataRemovalRequest(@NonNull android.view.contentcapture.UserDataRemovalRequest); - method public final void setActivityContentCaptureEnabled(@NonNull android.content.ComponentName, boolean); method public final void setContentCaptureWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>); - method public final void setPackageContentCaptureEnabled(@NonNull String, boolean); field public static final String SERVICE_INTERFACE = "android.service.contentcapture.ContentCaptureService"; } @@ -9298,6 +9298,8 @@ package android.view.contentcapture { method @Nullable public android.view.contentcapture.ViewNode getViewNode(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR; + field public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; // 0x5 + field public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; // 0x4 field public static final int TYPE_VIEW_APPEARED = 1; // 0x1 field public static final int TYPE_VIEW_DISAPPEARED = 2; // 0x2 field public static final int TYPE_VIEW_TEXT_CHANGED = 3; // 0x3 diff --git a/api/test-current.txt b/api/test-current.txt index 2a9a149063a7..3a31a50287ca 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -322,6 +322,14 @@ package android.app.admin { } +package android.app.assist { + + public static class AssistStructure.ViewNode { + ctor public AssistStructure.ViewNode(); + } + +} + package android.app.backup { public class BackupManager { @@ -1761,6 +1769,7 @@ package android.provider { method @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA) public static long getContributedMediaSize(android.content.Context, String, android.os.UserHandle) throws java.io.IOException; method @NonNull public static java.io.File getVolumePath(@NonNull String) throws java.io.FileNotFoundException; method @NonNull public static java.util.Collection<java.io.File> getVolumeScanPaths(@NonNull String) throws java.io.FileNotFoundException; + field public static final String EXTRA_ORIGINATED_FROM_SHELL = "android.intent.extra.originated_from_shell"; field public static final String SCAN_FILE_CALL = "scan_file"; field public static final String SCAN_VOLUME_CALL = "scan_volume"; } @@ -2042,6 +2051,40 @@ package android.service.autofill.augmented { } +package android.service.contentcapture { + + @Deprecated public final class ContentCaptureEventsRequest implements android.os.Parcelable { + method @Deprecated public int describeContents(); + method @Deprecated @NonNull public java.util.List<android.view.contentcapture.ContentCaptureEvent> getEvents(); + method @Deprecated public void writeToParcel(android.os.Parcel, int); + field @Deprecated public static final android.os.Parcelable.Creator<android.service.contentcapture.ContentCaptureEventsRequest> CREATOR; + } + + public abstract class ContentCaptureService extends android.app.Service { + ctor public ContentCaptureService(); + method public void onActivitySnapshot(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.service.contentcapture.SnapshotData); + method public void onConnected(); + method public void onContentCaptureEvent(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.view.contentcapture.ContentCaptureEvent); + method @Deprecated public void onContentCaptureEventsRequest(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.service.contentcapture.ContentCaptureEventsRequest); + method public void onCreateContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureContext, @NonNull android.view.contentcapture.ContentCaptureSessionId); + method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId); + method public void onDisconnected(); + method public void onUserDataRemovalRequest(@NonNull android.view.contentcapture.UserDataRemovalRequest); + method public final void setContentCaptureWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>); + field public static final String SERVICE_INTERFACE = "android.service.contentcapture.ContentCaptureService"; + } + + public final class SnapshotData implements android.os.Parcelable { + method public int describeContents(); + method public android.app.assist.AssistContent getAssistContent(); + method public android.os.Bundle getAssistData(); + method public android.app.assist.AssistStructure getAssistStructure(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.service.contentcapture.SnapshotData> CREATOR; + } + +} + package android.service.notification { @Deprecated public abstract class ConditionProviderService extends android.app.Service { @@ -2122,7 +2165,6 @@ package android.telephony { public class TelephonyManager { method public int checkCarrierPrivilegesForPackage(String); method public int getCarrierIdListVersion(); - method public boolean isRttSupported(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile(); method public void setCarrierTestOverride(String, String, String, String, String, String, String); field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe @@ -2608,6 +2650,49 @@ package android.view.autofill { } +package android.view.contentcapture { + + public final class ContentCaptureContext implements android.os.Parcelable { + method @Nullable public String getAction(); + method @Nullable public android.content.ComponentName getActivityComponent(); + method public int getDisplayId(); + method @Nullable public android.os.Bundle getExtras(); + method public int getFlags(); + method @Nullable public android.view.contentcapture.ContentCaptureSessionId getParentSessionId(); + method public int getTaskId(); + method @Nullable public android.net.Uri getUri(); + field public static final int FLAG_DISABLED_BY_APP = 1; // 0x1 + field public static final int FLAG_DISABLED_BY_FLAG_SECURE = 2; // 0x2 + } + + public final class ContentCaptureEvent implements android.os.Parcelable { + method public int describeContents(); + method public long getEventTime(); + method @Nullable public android.view.autofill.AutofillId getId(); + method @Nullable public java.util.List<android.view.autofill.AutofillId> getIds(); + method @Nullable public CharSequence getText(); + method public int getType(); + method @Nullable public android.view.contentcapture.ViewNode getViewNode(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR; + field public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; // 0x5 + field public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; // 0x4 + field public static final int TYPE_VIEW_APPEARED = 1; // 0x1 + field public static final int TYPE_VIEW_DISAPPEARED = 2; // 0x2 + field public static final int TYPE_VIEW_TEXT_CHANGED = 3; // 0x3 + } + + public final class ContentCaptureManager { + method public boolean isContentCaptureFeatureEnabled(); + method public void setContentCaptureFeatureEnabled(boolean); + } + + public final class ViewNode extends android.app.assist.AssistStructure.ViewNode { + method @Nullable public android.view.autofill.AutofillId getParentAutofillId(); + } + +} + package android.view.inputmethod { public final class InputMethodManager { diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 5f47e06fb876..63f9b5954792 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -2144,30 +2144,24 @@ message HardwareFailed { } optional HardwareType hardware_type = 1; - /* hardware_location allows vendors to differentiate between multiple instances of + /** + * hardware_location allows vendors to differentiate between multiple instances of * the same hardware_type. The specific locations are vendor defined integers, * referring to board-specific numbering schemes. */ optional int32 hardware_location = 2; - /* failure_code is specific to the HardwareType of the failed hardware. - * It should use the enum values defined below. + /** + * failure_code is specific to the HardwareType of the failed hardware. + * It should use one of the enum values defined below. */ - enum MicrophoneFailureCode { - MICROPHONE_FAILURE_COMPLETE = 0; - } - enum CodecFailureCode { - CODEC_FAILURE_COMPLETE = 0; - } - enum SpeakerFailureCode { - SPEAKER_FAILURE_COMPLETE = 0; - SPEAKER_FAILURE_HIGH_Z = 1; - SPEAKER_FAILURE_SHORT = 2; - } - enum FingerprintFailureCode { - FINGERPRINT_FAILURE_COMPLETE = 0; - FINGERPRINT_SENSOR_BROKEN = 1; - FINGERPRINT_TOO_MANY_DEAD_PIXELS = 2; + enum HardwareErrorCode { + UNKNOWN = 0; + COMPLETE = 1; + SPEAKER_HIGH_Z = 2; + SPEAKER_SHORT = 3; + FINGERPRINT_SENSOR_BROKEN = 4; + FINGERPRINT_TOO_MANY_DEAD_PIXELS = 5; } optional int32 failure_code = 3; } diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index eaba9bee6bf0..40a4070d2974 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -559,7 +559,7 @@ void LogEvent::init(android_log_context context) { // Handles the oneof field in KeyValuePair atom. if (isKeyValuePairAtom && depth == 2) { - pos[depth] = 4; + pos[depth] = 5; } mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(elem.data.float32))); @@ -575,7 +575,7 @@ void LogEvent::init(android_log_context context) { // Handles the oneof field in KeyValuePair atom. if (isKeyValuePairAtom && depth == 2) { - pos[depth] = 3; + pos[depth] = 4; } mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(string(elem.data.string, elem.len)))); @@ -593,7 +593,7 @@ void LogEvent::init(android_log_context context) { } // Handles the oneof field in KeyValuePair atom. if (isKeyValuePairAtom && depth == 2) { - pos[depth] = 2; + pos[depth] = 3; } mValues.push_back( FieldValue(Field(mTagId, pos, depth), Value((int64_t)elem.data.int64))); diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp index 3a5be43ed695..eec3c735057c 100644 --- a/cmds/statsd/tests/LogEvent_test.cpp +++ b/cmds/statsd/tests/LogEvent_test.cpp @@ -155,7 +155,7 @@ TEST(LogEventTest, TestKeyValuePairsAtomParsing) { EXPECT_EQ(33, item5.mValue.int_value); const FieldValue& item6 = event1.getValues()[6]; - EXPECT_EQ(0x2010482, item6.mField.getField()); + EXPECT_EQ(0x2010483, item6.mField.getField()); EXPECT_EQ(Type::LONG, item6.mValue.getType()); EXPECT_EQ(678L, item6.mValue.int_value); @@ -165,7 +165,7 @@ TEST(LogEventTest, TestKeyValuePairsAtomParsing) { EXPECT_EQ(44, item7.mValue.int_value); const FieldValue& item8 = event1.getValues()[8]; - EXPECT_EQ(0x2010582, item8.mField.getField()); + EXPECT_EQ(0x2010583, item8.mField.getField()); EXPECT_EQ(Type::LONG, item8.mValue.getType()); EXPECT_EQ(890L, item8.mValue.int_value); @@ -175,7 +175,7 @@ TEST(LogEventTest, TestKeyValuePairsAtomParsing) { EXPECT_EQ(1, item9.mValue.int_value); const FieldValue& item10 = event1.getValues()[10]; - EXPECT_EQ(0x2010683, item10.mField.getField()); + EXPECT_EQ(0x2010684, item10.mField.getField()); EXPECT_EQ(Type::STRING, item10.mValue.getType()); EXPECT_EQ("test2", item10.mValue.str_value); @@ -185,7 +185,7 @@ TEST(LogEventTest, TestKeyValuePairsAtomParsing) { EXPECT_EQ(2, item11.mValue.int_value); const FieldValue& item12 = event1.getValues()[12]; - EXPECT_EQ(0x2010783, item12.mField.getField()); + EXPECT_EQ(0x2010784, item12.mField.getField()); EXPECT_EQ(Type::STRING, item12.mValue.getType()); EXPECT_EQ("test1", item12.mValue.str_value); @@ -195,7 +195,7 @@ TEST(LogEventTest, TestKeyValuePairsAtomParsing) { EXPECT_EQ(111, item13.mValue.int_value); const FieldValue& item14 = event1.getValues()[14]; - EXPECT_EQ(0x2010884, item14.mField.getField()); + EXPECT_EQ(0x2010885, item14.mField.getField()); EXPECT_EQ(Type::FLOAT, item14.mValue.getType()); EXPECT_EQ(2.2f, item14.mValue.float_value); @@ -205,7 +205,7 @@ TEST(LogEventTest, TestKeyValuePairsAtomParsing) { EXPECT_EQ(222, item15.mValue.int_value); const FieldValue& item16 = event1.getValues()[16]; - EXPECT_EQ(0x2018984, item16.mField.getField()); + EXPECT_EQ(0x2018985, item16.mField.getField()); EXPECT_EQ(Type::FLOAT, item16.mValue.getType()); EXPECT_EQ(1.1f, item16.mValue.float_value); } diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java index 5b3813d9c3af..c7537102d40c 100644 --- a/core/java/android/animation/LayoutTransition.java +++ b/core/java/android/animation/LayoutTransition.java @@ -17,6 +17,7 @@ package android.animation; import android.annotation.UnsupportedAppUsage; +import android.os.Build; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; @@ -1071,7 +1072,7 @@ public class LayoutTransition { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public void cancel() { if (currentChangingAnimations.size() > 0) { LinkedHashMap<View, Animator> currentAnimCopy = @@ -1107,7 +1108,7 @@ public class LayoutTransition { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public void cancel(int transitionType) { switch (transitionType) { case CHANGE_APPEARING: diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index aac8f0855ca9..0eadd1dcd903 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -7155,7 +7155,8 @@ public class Activity extends ContextThemeWrapper mInstrumentation.onEnterAnimationComplete(); onEnterAnimationComplete(); if (getWindow() != null && getWindow().getDecorView() != null) { - getWindow().getDecorView().getViewTreeObserver().dispatchOnEnterAnimationComplete(); + View decorView = getWindow().getDecorView(); + decorView.getViewTreeObserver().dispatchOnEnterAnimationComplete(); } } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 9ee2f039ce3f..ea145f0b9d21 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -4485,6 +4485,7 @@ public class AppOpsManager { * @hide */ public int noteProxyOpNoThrow(int op, String proxiedPackageName) { + logOperationIfNeeded(op, mContext.getOpPackageName(), proxiedPackageName); try { return mService.noteProxyOperation(op, Process.myUid(), mContext.getOpPackageName(), Binder.getCallingUid(), proxiedPackageName); @@ -4500,7 +4501,7 @@ public class AppOpsManager { */ @UnsupportedAppUsage public int noteOpNoThrow(int op, int uid, String packageName) { - logNoteOpIfNeeded(op, packageName); + logOperationIfNeeded(op, packageName, null); try { return mService.noteOperation(op, uid, packageName); } catch (RemoteException e) { @@ -4608,6 +4609,7 @@ public class AppOpsManager { * @hide */ public int startOpNoThrow(int op, int uid, String packageName, boolean startIfModeDefault) { + logOperationIfNeeded(op, packageName, null); try { return mService.startOperation(getToken(mService), op, uid, packageName, startIfModeDefault); @@ -4624,6 +4626,7 @@ public class AppOpsManager { * @hide */ public void finishOp(int op, int uid, String packageName) { + logOperationIfNeeded(op, packageName, null); try { mService.finishOperation(getToken(mService), op, uid, packageName); } catch (RemoteException e) { @@ -4870,7 +4873,7 @@ public class AppOpsManager { return AppOpsManager.MODE_DEFAULT; } - private static void logNoteOpIfNeeded(int op, String callingPackage) { + private static void logOperationIfNeeded(int op, String callingPackage, String proxiedPackage) { // Check if debug logging propety is enabled. if (!SystemProperties.getBoolean(DEBUG_LOGGING_ENABLE_PROP, false)) { return; @@ -4908,6 +4911,6 @@ public class AppOpsManager { // Log a stack trace Exception here = new Exception("HERE!"); android.util.Log.i(DEBUG_LOGGING_TAG, "Note operation package= " + callingPackage - + " op= " + opStr, here); + + " proxied= " + proxiedPackage + " op= " + opStr, here); } } diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 6006ad2f5ed3..2b1e7cd9a26e 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -3,6 +3,7 @@ package android.app.assist; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.app.Activity; import android.content.ComponentName; import android.content.Context; @@ -715,9 +716,10 @@ public class AssistStructure implements Parcelable { ViewNode[] mChildren; // TODO(b/111276913): temporarily made public / @hide until we decide what will be used by - // ScreenObservation. + // COntent Capture. /** @hide */ @SystemApi + @TestApi public ViewNode() { } diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index 8cbe7a33d5c6..fafea340ccc3 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -52,6 +52,14 @@ public final class UsageEvents implements Parcelable { public static final int NONE = 0; /** + * A device level event like {@link #DEVICE_SHUTDOWN} does not have package name, but some + * user code always expect a non-null {@link #mPackage} for every event. Use + * {@link #DEVICE_EVENT_PACKAGE_NAME} as packageName for these device level events. + * @hide + */ + public static final String DEVICE_EVENT_PACKAGE_NAME = "android"; + + /** * @deprecated by {@link #ACTIVITY_RESUMED} */ @Deprecated diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index e2907e219b1f..6e52b33692c8 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -866,6 +866,14 @@ public abstract class PackageManager { */ public static final int INSTALL_ENABLE_ROLLBACK = 0x00040000; + /** + * Flag parameter for {@link #installPackage} to indicate that package verification should be + * disabled for this package. + * + * @hide + */ + public static final int INSTALL_DISABLE_VERIFICATION = 0x00080000; + /** @hide */ @IntDef(flag = true, prefix = { "DONT_KILL_APP" }, value = { DONT_KILL_APP diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index c443d2a09b9a..cfe35b061151 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -137,6 +137,32 @@ public abstract class PackageManagerInternal { } /** + * Provider for default browser + */ + public interface DefaultBrowserProvider { + + /** + * Get the package name of the default browser. + * + * @param userId the user id + * + * @return the package name of the default browser, or {@code null} if none + */ + @Nullable + String getDefaultBrowser(@UserIdInt int userId); + + /** + * Set the package name of the default browser. + * + * @param packageName package name of the default browser, or {@code null} to remove + * @param userId the user id + * + * @return whether the default browser was successfully set. + */ + boolean setDefaultBrowser(@Nullable String packageName, @UserIdInt int userId); + } + + /** * Sets the location provider packages provider. * @param provider The packages provider. */ @@ -853,4 +879,11 @@ public abstract class PackageManagerInternal { */ @Nullable public abstract String removeLegacyDefaultBrowserPackageName(int userId); + + /** + * Sets the default browser provider. + * + * @param provider the provider + */ + public abstract void setDefaultBrowserProvider(@NonNull DefaultBrowserProvider provider); } diff --git a/core/java/android/content/rollback/IRollbackManager.aidl b/core/java/android/content/rollback/IRollbackManager.aidl index 104661f12e8d..d951756bb513 100644 --- a/core/java/android/content/rollback/IRollbackManager.aidl +++ b/core/java/android/content/rollback/IRollbackManager.aidl @@ -40,4 +40,11 @@ interface IRollbackManager { // Exposed for test purposes only. void expireRollbackForPackage(String packageName); + + // Used by the staging manager to notify the RollbackManager that a session is + // being staged. In the case of multi-package sessions, the specified sessionId + // is that of the parent session. + // + // NOTE: This call is synchronous. + boolean notifyStagedSession(int sessionId); } diff --git a/core/java/android/content/rollback/PackageRollbackInfo.java b/core/java/android/content/rollback/PackageRollbackInfo.java index d4ed35a9cb71..0ec40183c319 100644 --- a/core/java/android/content/rollback/PackageRollbackInfo.java +++ b/core/java/android/content/rollback/PackageRollbackInfo.java @@ -69,6 +69,11 @@ public final class PackageRollbackInfo implements Parcelable { private final ArrayList<RestoreInfo> mPendingRestores; /** + * Whether this instance represents the PackageRollbackInfo for an APEX module. + */ + private final boolean mIsApex; + + /** * Returns the name of the package to roll back from. */ public String getPackageName() { @@ -116,18 +121,26 @@ public final class PackageRollbackInfo implements Parcelable { } /** @hide */ + public boolean isApex() { + return mIsApex; + } + + /** @hide */ public PackageRollbackInfo(VersionedPackage packageRolledBackFrom, VersionedPackage packageRolledBackTo, - @NonNull IntArray pendingBackups, @NonNull ArrayList<RestoreInfo> pendingRestores) { + @NonNull IntArray pendingBackups, @NonNull ArrayList<RestoreInfo> pendingRestores, + boolean isApex) { this.mVersionRolledBackFrom = packageRolledBackFrom; this.mVersionRolledBackTo = packageRolledBackTo; this.mPendingBackups = pendingBackups; this.mPendingRestores = pendingRestores; + this.mIsApex = isApex; } private PackageRollbackInfo(Parcel in) { this.mVersionRolledBackFrom = VersionedPackage.CREATOR.createFromParcel(in); this.mVersionRolledBackTo = VersionedPackage.CREATOR.createFromParcel(in); + this.mIsApex = in.readBoolean(); this.mPendingRestores = null; this.mPendingBackups = null; } @@ -141,6 +154,7 @@ public final class PackageRollbackInfo implements Parcelable { public void writeToParcel(Parcel out, int flags) { mVersionRolledBackFrom.writeToParcel(out, flags); mVersionRolledBackTo.writeToParcel(out, flags); + out.writeBoolean(mIsApex); } public static final Parcelable.Creator<PackageRollbackInfo> CREATOR = diff --git a/core/java/android/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java index b2288fc4492d..87568e857d6d 100644 --- a/core/java/android/ddm/DdmHandleHello.java +++ b/core/java/android/ddm/DdmHandleHello.java @@ -16,14 +16,16 @@ package android.ddm; -import org.apache.harmony.dalvik.ddmc.Chunk; -import org.apache.harmony.dalvik.ddmc.ChunkHandler; -import org.apache.harmony.dalvik.ddmc.DdmServer; -import android.util.Log; import android.os.Debug; import android.os.UserHandle; +import android.util.Log; + import dalvik.system.VMRuntime; +import org.apache.harmony.dalvik.ddmc.Chunk; +import org.apache.harmony.dalvik.ddmc.ChunkHandler; +import org.apache.harmony.dalvik.ddmc.DdmServer; + import java.nio.ByteBuffer; /** @@ -35,6 +37,8 @@ public class DdmHandleHello extends ChunkHandler { public static final int CHUNK_WAIT = type("WAIT"); public static final int CHUNK_FEAT = type("FEAT"); + private static final int CLIENT_PROTOCOL_VERSION = 1; + private static DdmHandleHello mInstance = new DdmHandleHello(); private static final String[] FRAMEWORK_FEATURES = new String[] { @@ -145,7 +149,7 @@ public class DdmHandleHello extends ChunkHandler { + vmFlags.length() * 2 + 1); out.order(ChunkHandler.CHUNK_ORDER); - out.putInt(DdmServer.CLIENT_PROTOCOL_VERSION); + out.putInt(CLIENT_PROTOCOL_VERSION); out.putInt(android.os.Process.myPid()); out.putInt(vmIdent.length()); out.putInt(appName.length()); diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java index 843db6d28d30..ffae361e76d4 100644 --- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java @@ -51,6 +51,7 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub private static final int DO_TOGGLE_SOFT_INPUT = 105; private static final int DO_FINISH_SESSION = 110; private static final int DO_VIEW_CLICKED = 115; + private static final int DO_NOTIFY_IME_HIDDEN = 120; HandlerCaller mCaller; InputMethodSession mInputMethodSession; @@ -129,6 +130,10 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub mInputMethodSession.viewClicked(msg.arg1 == 1); return; } + case DO_NOTIFY_IME_HIDDEN: { + mInputMethodSession.notifyImeHidden(); + return; + } } Log.w(TAG, "Unhandled message code: " + msg.what); } @@ -172,6 +177,11 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub } @Override + public void notifyImeHidden() { + mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_NOTIFY_IME_HIDDEN)); + } + + @Override public void updateCursor(Rect newCursor) { mCaller.executeOrSendMessage( mCaller.obtainMessageO(DO_UPDATE_CURSOR, newCursor)); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 333cfbd400dd..ab630fd7467b 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -592,7 +592,6 @@ public class InputMethodService extends AbstractInputMethodService { final boolean wasVisible = mIsPreRendered ? mDecorViewVisible && mWindowVisible : isInputViewShown(); if (mIsPreRendered) { - // TODO: notify visibility to insets consumer. if (DEBUG) { Log.v(TAG, "Making IME window invisible"); } @@ -658,6 +657,11 @@ public class InputMethodService extends AbstractInputMethodService { } } + private void notifyImeHidden() { + setImeWindowStatus(IME_ACTIVE | IME_INVISIBLE, mBackDisposition); + onPreRenderedWindowVisibilityChanged(false /* setVisible */); + } + private void setImeWindowStatus(int visibilityFlags, int backDisposition) { mPrivOps.setImeWindowStatus(visibilityFlags, backDisposition); } @@ -760,6 +764,14 @@ public class InputMethodService extends AbstractInputMethodService { } InputMethodService.this.onUpdateCursorAnchorInfo(info); } + + /** + * Notify IME that window is hidden. + * @hide + */ + public final void notifyImeHidden() { + InputMethodService.this.notifyImeHidden(); + } } /** diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java index b4b541dc5cd0..31c948a14698 100644 --- a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java +++ b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java @@ -290,6 +290,12 @@ final class MultiClientInputMethodClientCallbackAdaptor { CallbackImpl::updateCursorAnchorInfo, mCallbackImpl, info)); } } + + @Override + public final void notifyImeHidden() { + // no-op for multi-session since IME is responsible controlling navigation bar buttons. + reportNotSupported(); + } } private static final class MultiClientInputMethodSessionImpl diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java index 141d33bc4145..1f3369376b10 100644 --- a/core/java/android/os/AsyncTask.java +++ b/core/java/android/os/AsyncTask.java @@ -19,6 +19,7 @@ package android.os; import android.annotation.MainThread; import android.annotation.Nullable; import android.annotation.WorkerThread; + import java.util.ArrayDeque; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; @@ -70,7 +71,7 @@ import java.util.concurrent.atomic.AtomicInteger; * protected Long doInBackground(URL... urls) { * int count = urls.length; * long totalSize = 0; - * for (int i = 0; i < count; i++) { + * for (int i = 0; i < count; i++) { * totalSize += Downloader.downloadFile(urls[i]); * publishProgress((int) ((i / (float) count) * 100)); * // Escape early if cancel() is called @@ -158,13 +159,22 @@ import java.util.concurrent.atomic.AtomicInteger; * </ul> * * <h2>Memory observability</h2> - * <p>AsyncTask guarantees that all callback calls are synchronized in such a way that the following - * operations are safe without explicit synchronizations.</p> + * <p>AsyncTask guarantees that all callback calls are synchronized to ensure the following + * without explicit synchronizations.</p> * <ul> - * <li>Set member fields in the constructor or {@link #onPreExecute}, and refer to them - * in {@link #doInBackground}. - * <li>Set member fields in {@link #doInBackground}, and refer to them in - * {@link #onProgressUpdate} and {@link #onPostExecute}. + * <li>The memory effects of {@link #onPreExecute}, and anything else + * executed before the call to {@link #execute}, including the construction + * of the AsyncTask object, are visible to {@link #doInBackground}. + * <li>The memory effects of {@link #doInBackground} are visible to + * {@link #onPostExecute}. + * <li>Any memory effects of {@link #doInBackground} preceding a call + * to {@link #publishProgress} are visible to the corresponding + * {@link #onProgressUpdate} call. (But {@link #doInBackground} continues to + * run, and care needs to be taken that later updates in {@link #doInBackground} + * do not interfere with an in-progress {@link #onProgressUpdate} call.) + * <li>Any memory effects preceding a call to {@link #cancel} are visible + * after a call to {@link #isCancelled} that returns true as a result, or + * during and after a resulting call to {@link #onCancelled}. * </ul> * * <h2>Order of execution</h2> @@ -388,6 +398,10 @@ public abstract class AsyncTask<Params, Progress, Result> { * specified parameters are the parameters passed to {@link #execute} * by the caller of this task. * + * This will normally run on a background thread. But to better + * support testing frameworks, it is recommended that this also tolerates + * direct execution on the foreground thread, as part of the {@link #execute} call. + * * This method can call {@link #publishProgress} to publish updates * on the UI thread. * @@ -404,6 +418,8 @@ public abstract class AsyncTask<Params, Progress, Result> { /** * Runs on the UI thread before {@link #doInBackground}. + * Invoked directly by {@link #execute} or {@link #executeOnExecutor}. + * The default version does nothing. * * @see #onPostExecute * @see #doInBackground @@ -414,7 +430,10 @@ public abstract class AsyncTask<Params, Progress, Result> { /** * <p>Runs on the UI thread after {@link #doInBackground}. The - * specified result is the value returned by {@link #doInBackground}.</p> + * specified result is the value returned by {@link #doInBackground}. + * To better support testing frameworks, it is recommended that this be + * written to tolerate direct execution as part of the execute() call. + * The default version does nothing.</p> * * <p>This method won't be invoked if the task was cancelled.</p> * @@ -432,6 +451,7 @@ public abstract class AsyncTask<Params, Progress, Result> { /** * Runs on the UI thread after {@link #publishProgress} is invoked. * The specified values are the values passed to {@link #publishProgress}. + * The default version does nothing. * * @param values The values indicating progress. * @@ -466,7 +486,8 @@ public abstract class AsyncTask<Params, Progress, Result> { /** * <p>Applications should preferably override {@link #onCancelled(Object)}. * This method is invoked by the default implementation of - * {@link #onCancelled(Object)}.</p> + * {@link #onCancelled(Object)}. + * The default version does nothing.</p> * * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and * {@link #doInBackground(Object[])} has finished.</p> @@ -504,12 +525,16 @@ public abstract class AsyncTask<Params, Progress, Result> { * an attempt to stop the task.</p> * * <p>Calling this method will result in {@link #onCancelled(Object)} being - * invoked on the UI thread after {@link #doInBackground(Object[])} - * returns. Calling this method guarantees that {@link #onPostExecute(Object)} - * is never invoked. After invoking this method, you should check the - * value returned by {@link #isCancelled()} periodically from - * {@link #doInBackground(Object[])} to finish the task as early as - * possible.</p> + * invoked on the UI thread after {@link #doInBackground(Object[])} returns. + * Calling this method guarantees that onPostExecute(Object) is never + * subsequently invoked, even if <tt>cancel</tt> returns false, but + * {@link #onPostExecute} has not yet run. To finish the + * task as early as possible, check {@link #isCancelled()} periodically from + * {@link #doInBackground(Object[])}.</p> + * + * <p>This only requests cancellation. It never waits for a running + * background task to terminate, even if <tt>mayInterruptIfRunning</tt> is + * true.</p> * * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this * task should be interrupted; otherwise, in-progress tasks are allowed diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java index 3a5b8a86204e..27f7e2296e7f 100644 --- a/core/java/android/os/BugreportManager.java +++ b/core/java/android/os/BugreportManager.java @@ -24,7 +24,8 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; -import android.os.IBinder.DeathRecipient; + +import com.android.internal.util.Preconditions; import java.io.FileDescriptor; import java.lang.annotation.Retention; @@ -127,13 +128,16 @@ public class BugreportManager { @NonNull BugreportParams params, @NonNull @CallbackExecutor Executor executor, @NonNull BugreportCallback callback) { - // TODO(b/111441001): Enforce android.Manifest.permission.DUMP if necessary. + Preconditions.checkNotNull(bugreportFd); + Preconditions.checkNotNull(params); + Preconditions.checkNotNull(executor); + Preconditions.checkNotNull(callback); DumpstateListener dsListener = new DumpstateListener(executor, callback); try { // Note: mBinder can get callingUid from the binder transaction. mBinder.startBugreport(-1 /* callingUid */, mContext.getOpPackageName(), - (bugreportFd != null ? bugreportFd.getFileDescriptor() : new FileDescriptor()), + bugreportFd.getFileDescriptor(), (screenshotFd != null ? screenshotFd.getFileDescriptor() : new FileDescriptor()), params.getMode(), dsListener); @@ -154,8 +158,7 @@ public class BugreportManager { } } - private final class DumpstateListener extends IDumpstateListener.Stub - implements DeathRecipient { + private final class DumpstateListener extends IDumpstateListener.Stub { private final Executor mExecutor; private final BugreportCallback mCallback; @@ -165,11 +168,6 @@ public class BugreportManager { } @Override - public void binderDied() { - // TODO(b/111441001): implement - } - - @Override public void onProgress(int progress) throws RemoteException { final long identity = Binder.clearCallingIdentity(); try { diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index ddec688931cb..68f9288a93cf 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -103,7 +103,7 @@ public class GraphicsEnvironment { * Return the debug layer app's on-disk and in-APK lib directories */ private static String getDebugLayerAppPaths(Context context, String app) { - ApplicationInfo appInfo; + final ApplicationInfo appInfo; try { appInfo = context.getPackageManager().getApplicationInfo( app, PackageManager.MATCH_ALL); @@ -113,15 +113,15 @@ public class GraphicsEnvironment { return null; } - String abi = chooseAbi(appInfo); + final String abi = chooseAbi(appInfo); - StringBuilder sb = new StringBuilder(); + final StringBuilder sb = new StringBuilder(); sb.append(appInfo.nativeLibraryDir) .append(File.pathSeparator); sb.append(appInfo.sourceDir) .append("!/lib/") .append(abi); - String paths = sb.toString(); + final String paths = sb.toString(); if (DEBUG) Log.v(TAG, "Debug layer app libs: " + paths); @@ -143,13 +143,13 @@ public class GraphicsEnvironment { if (isDebuggable(context) || (getCanLoadSystemLibraries() == 1)) { - int enable = coreSettings.getInt(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0); + final int enable = coreSettings.getInt(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0); if (enable != 0) { - String gpuDebugApp = coreSettings.getString(Settings.Global.GPU_DEBUG_APP); + final String gpuDebugApp = coreSettings.getString(Settings.Global.GPU_DEBUG_APP); - String packageName = context.getPackageName(); + final String packageName = context.getPackageName(); if ((gpuDebugApp != null && packageName != null) && (!gpuDebugApp.isEmpty() && !packageName.isEmpty()) @@ -163,12 +163,12 @@ public class GraphicsEnvironment { // If there is a debug layer app specified, add its path. - String gpuDebugLayerApp = + final String gpuDebugLayerApp = coreSettings.getString(Settings.Global.GPU_DEBUG_LAYER_APP); if (gpuDebugLayerApp != null && !gpuDebugLayerApp.isEmpty()) { Log.i(TAG, "GPU debug layer app: " + gpuDebugLayerApp); - String paths = getDebugLayerAppPaths(context, gpuDebugLayerApp); + final String paths = getDebugLayerAppPaths(context, gpuDebugLayerApp); if (paths != null) { // Append the path so files placed in the app's base directory will // override the external path @@ -176,14 +176,14 @@ public class GraphicsEnvironment { } } - String layers = coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS); + final String layers = coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS); Log.i(TAG, "Vulkan debug layer list: " + layers); if (layers != null && !layers.isEmpty()) { setDebugLayers(layers); } - String layersGLES = + final String layersGLES = coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS_GLES); Log.i(TAG, "GLES debug layer list: " + layersGLES); @@ -208,7 +208,7 @@ public class GraphicsEnvironment { private static final Map<OpenGlDriverChoice, String> sDriverMap = buildMap(); private static Map<OpenGlDriverChoice, String> buildMap() { - Map<OpenGlDriverChoice, String> map = new HashMap<>(); + final Map<OpenGlDriverChoice, String> map = new HashMap<>(); map.put(OpenGlDriverChoice.DEFAULT, "default"); map.put(OpenGlDriverChoice.ANGLE, "angle"); map.put(OpenGlDriverChoice.NATIVE, "native"); @@ -219,7 +219,7 @@ public class GraphicsEnvironment { private static List<String> getGlobalSettingsString(Bundle bundle, String globalSetting) { List<String> valueList = null; - String settingsValue = bundle.getString(globalSetting); + final String settingsValue = bundle.getString(globalSetting); if (settingsValue != null) { valueList = new ArrayList<>(Arrays.asList(settingsValue.split(","))); @@ -242,18 +242,16 @@ public class GraphicsEnvironment { } private static String getDriverForPkg(Bundle bundle, String packageName) { - String allUseAngle = + final String allUseAngle = bundle.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE); if ((allUseAngle != null) && allUseAngle.equals("1")) { return sDriverMap.get(OpenGlDriverChoice.ANGLE); } - List<String> globalSettingsDriverPkgs = - getGlobalSettingsString(bundle, - Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS); - List<String> globalSettingsDriverValues = - getGlobalSettingsString(bundle, - Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES); + final List<String> globalSettingsDriverPkgs = getGlobalSettingsString( + bundle, Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS); + final List<String> globalSettingsDriverValues = getGlobalSettingsString( + bundle, Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES); // Make sure we have a good package name if ((packageName == null) || (packageName.isEmpty())) { @@ -270,7 +268,7 @@ public class GraphicsEnvironment { return sDriverMap.get(OpenGlDriverChoice.DEFAULT); } - int pkgIndex = getGlobalSettingsPkgIndex(packageName, globalSettingsDriverPkgs); + final int pkgIndex = getGlobalSettingsPkgIndex(packageName, globalSettingsDriverPkgs); if (pkgIndex < 0) { return sDriverMap.get(OpenGlDriverChoice.DEFAULT); @@ -283,10 +281,10 @@ public class GraphicsEnvironment { * Get the ANGLE package name. */ private String getAnglePackageName(Context context) { - Intent intent = new Intent(ACTION_ANGLE_FOR_ANDROID); + final Intent intent = new Intent(ACTION_ANGLE_FOR_ANDROID); - List<ResolveInfo> resolveInfos = context.getPackageManager() - .queryIntentActivities(intent, PackageManager.MATCH_SYSTEM_ONLY); + final List<ResolveInfo> resolveInfos = context.getPackageManager().queryIntentActivities( + intent, PackageManager.MATCH_SYSTEM_ONLY); if (resolveInfos.size() != 1) { Log.e(TAG, "Invalid number of ANGLE packages. Required: 1, Found: " + resolveInfos.size()); @@ -315,8 +313,8 @@ public class GraphicsEnvironment { * - devices that are running a userdebug build (ro.debuggable) or can inject libraries for * debugging (PR_SET_DUMPABLE). */ - boolean appIsDebuggable = isDebuggable(context); - boolean deviceIsDebuggable = getCanLoadSystemLibraries() == 1; + final boolean appIsDebuggable = isDebuggable(context); + final boolean deviceIsDebuggable = getCanLoadSystemLibraries() == 1; if (!(appIsDebuggable || deviceIsDebuggable)) { Log.v(TAG, "Skipping loading temporary rules file: " + "appIsDebuggable = " + appIsDebuggable + ", " @@ -324,7 +322,7 @@ public class GraphicsEnvironment { return false; } - String angleTempRules = SystemProperties.get(ANGLE_TEMP_RULES); + final String angleTempRules = SystemProperties.get(ANGLE_TEMP_RULES); if ((angleTempRules == null) || angleTempRules.isEmpty()) { Log.v(TAG, "System property '" + ANGLE_TEMP_RULES + "' is not set or is empty"); @@ -333,16 +331,16 @@ public class GraphicsEnvironment { Log.i(TAG, "Detected system property " + ANGLE_TEMP_RULES + ": " + angleTempRules); - File tempRulesFile = new File(angleTempRules); + final File tempRulesFile = new File(angleTempRules); if (tempRulesFile.exists()) { Log.i(TAG, angleTempRules + " exists, loading file."); try { - FileInputStream stream = new FileInputStream(angleTempRules); + final FileInputStream stream = new FileInputStream(angleTempRules); try { - FileDescriptor rulesFd = stream.getFD(); - long rulesOffset = 0; - long rulesLength = stream.getChannel().size(); + final FileDescriptor rulesFd = stream.getFD(); + final long rulesOffset = 0; + final long rulesLength = stream.getChannel().size(); Log.i(TAG, "Loaded temporary ANGLE rules from " + angleTempRules); setAngleInfo(paths, packageName, devOptIn, rulesFd, rulesOffset, rulesLength); @@ -377,11 +375,11 @@ public class GraphicsEnvironment { String devOptIn) { // Pass the rules file to loader for ANGLE decisions try { - AssetManager angleAssets = + final AssetManager angleAssets = context.getPackageManager().getResourcesForApplication(angleInfo).getAssets(); try { - AssetFileDescriptor assetsFd = angleAssets.openFd(ANGLE_RULES_FILE); + final AssetFileDescriptor assetsFd = angleAssets.openFd(ANGLE_RULES_FILE); setAngleInfo(paths, packageName, devOptIn, assetsFd.getFileDescriptor(), assetsFd.getStartOffset(), assetsFd.getLength()); @@ -404,9 +402,8 @@ public class GraphicsEnvironment { * Pull ANGLE whitelist from GlobalSettings and compare against current package */ private boolean checkAngleWhitelist(Bundle bundle, String packageName) { - List<String> angleWhitelist = - getGlobalSettingsString(bundle, - Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST); + final List<String> angleWhitelist = + getGlobalSettingsString(bundle, Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST); return angleWhitelist.contains(packageName); } @@ -420,7 +417,7 @@ public class GraphicsEnvironment { return; } - String devOptIn = getDriverForPkg(bundle, packageName); + final String devOptIn = getDriverForPkg(bundle, packageName); if (DEBUG) { Log.v(TAG, "ANGLE Developer option for '" + packageName + "' " + "set to: '" + devOptIn + "'"); @@ -438,9 +435,9 @@ public class GraphicsEnvironment { // load a driver, GraphicsEnv::shouldUseAngle() has seen the package name before // and can confidently answer yes/no based on the previously set developer // option value. - boolean whitelisted = checkAngleWhitelist(bundle, packageName); - boolean defaulted = devOptIn.equals(sDriverMap.get(OpenGlDriverChoice.DEFAULT)); - boolean rulesCheck = (whitelisted || !defaulted); + final boolean whitelisted = checkAngleWhitelist(bundle, packageName); + final boolean defaulted = devOptIn.equals(sDriverMap.get(OpenGlDriverChoice.DEFAULT)); + final boolean rulesCheck = (whitelisted || !defaulted); if (!rulesCheck) { return; } @@ -452,13 +449,13 @@ public class GraphicsEnvironment { Log.v(TAG, "ANGLE developer option for " + packageName + ": " + devOptIn); } - String anglePkgName = getAnglePackageName(context); + final String anglePkgName = getAnglePackageName(context); if (anglePkgName.isEmpty()) { Log.e(TAG, "Failed to find ANGLE package."); return; } - ApplicationInfo angleInfo; + final ApplicationInfo angleInfo; try { angleInfo = context.getPackageManager().getApplicationInfo(anglePkgName, PackageManager.MATCH_SYSTEM_ONLY); @@ -467,10 +464,10 @@ public class GraphicsEnvironment { return; } - String abi = chooseAbi(angleInfo); + final String abi = chooseAbi(angleInfo); // Build a path that includes installed native libs and APK - String paths = angleInfo.nativeLibraryDir + final String paths = angleInfo.nativeLibraryDir + File.pathSeparator + angleInfo.sourceDir + "!/lib/" @@ -493,12 +490,12 @@ public class GraphicsEnvironment { * Choose whether the current process should use the builtin or an updated driver. */ private static void chooseDriver(Context context, Bundle coreSettings) { - String driverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER); + final String driverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER); if (driverPackageName == null || driverPackageName.isEmpty()) { return; } - ApplicationInfo driverInfo; + final ApplicationInfo driverInfo; try { driverInfo = context.getPackageManager().getApplicationInfo(driverPackageName, PackageManager.MATCH_SYSTEM_ONLY); @@ -519,7 +516,7 @@ public class GraphicsEnvironment { // To minimize risk of driver updates crippling the device beyond user repair, never use an // updated driver for privileged or non-updated system apps. Presumably pre-installed apps // were tested thoroughly with the pre-installed driver. - ApplicationInfo ai = context.getApplicationInfo(); + final ApplicationInfo ai = context.getApplicationInfo(); if (ai.isPrivilegedApp() || (ai.isSystemApp() && !ai.isUpdatedSystemApp())) { if (DEBUG) Log.v(TAG, "ignoring driver package for privileged/non-updated system app"); return; @@ -529,7 +526,7 @@ public class GraphicsEnvironment { // 0: Default (Invalid values fallback to default as well) // 1: All apps use Game Driver // 2: All apps use system graphics driver - int gameDriverAllApps = coreSettings.getInt(Settings.Global.GAME_DRIVER_ALL_APPS, 0); + final int gameDriverAllApps = coreSettings.getInt(Settings.Global.GAME_DRIVER_ALL_APPS, 0); if (gameDriverAllApps == 2) { if (DEBUG) { Log.w(TAG, "Game Driver is turned off on this device"); @@ -546,7 +543,7 @@ public class GraphicsEnvironment { } return; } - boolean isOptIn = + final boolean isOptIn = getGlobalSettingsString(coreSettings, Settings.Global.GAME_DRIVER_OPT_IN_APPS) .contains(ai.packageName); if (!isOptIn @@ -563,13 +560,13 @@ public class GraphicsEnvironment { // on the blacklist, terminate early when it's on the blacklist. try { // TODO(b/121350991) Switch to DeviceConfig with property listener. - String base64String = + final String base64String = coreSettings.getString(Settings.Global.GAME_DRIVER_BLACKLIST); if (base64String != null && !base64String.isEmpty()) { - Blacklists blacklistsProto = Blacklists.parseFrom( - Base64.decode(base64String, BASE64_FLAGS)); - List<Blacklist> blacklists = blacklistsProto.getBlacklistsList(); - long driverVersionCode = driverInfo.longVersionCode; + final Blacklists blacklistsProto = + Blacklists.parseFrom(Base64.decode(base64String, BASE64_FLAGS)); + final List<Blacklist> blacklists = blacklistsProto.getBlacklistsList(); + final long driverVersionCode = driverInfo.longVersionCode; for (Blacklist blacklist : blacklists) { if (blacklist.getVersionCode() == driverVersionCode) { for (String packageName : blacklist.getPackageNamesList()) { @@ -589,7 +586,7 @@ public class GraphicsEnvironment { } } - String abi = chooseAbi(driverInfo); + final String abi = chooseAbi(driverInfo); if (abi == null) { if (DEBUG) { // This is the normal case for the pre-installed empty driver package, don't spam @@ -600,13 +597,13 @@ public class GraphicsEnvironment { return; } - StringBuilder sb = new StringBuilder(); + final StringBuilder sb = new StringBuilder(); sb.append(driverInfo.nativeLibraryDir) .append(File.pathSeparator); sb.append(driverInfo.sourceDir) .append("!/lib/") .append(abi); - String paths = sb.toString(); + final String paths = sb.toString(); if (DEBUG) Log.v(TAG, "gfx driver package libs: " + paths); setDriverPath(paths); @@ -623,7 +620,7 @@ public class GraphicsEnvironment { * Should only be called after chooseDriver(). */ public static void earlyInitEGL() { - Thread eglInitThread = new Thread( + final Thread eglInitThread = new Thread( () -> { EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); }, @@ -632,7 +629,7 @@ public class GraphicsEnvironment { } private static String chooseAbi(ApplicationInfo ai) { - String isa = VMRuntime.getCurrentInstructionSet(); + final String isa = VMRuntime.getCurrentInstructionSet(); if (ai.primaryCpuAbi != null && isa.equals(VMRuntime.getInstructionSet(ai.primaryCpuAbi))) { return ai.primaryCpuAbi; diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 92650e114a66..ed3026c03810 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -160,6 +160,17 @@ public final class DeviceConfig { } /** + * Namespace for all runtime native boot related features. Boot in this case refers to the + * fact that the properties only take affect after rebooting the device. + * + * @hide + */ + @SystemApi + public interface RuntimeNativeBoot { + String NAMESPACE = "runtime_native_boot"; + } + + /** * Namespace for all activity manager related features that are used at the native level. * These features are applied at reboot. * diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 0743c23080fd..67c840006948 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -110,6 +110,16 @@ public final class MediaStore { public static final String SCAN_VOLUME_CALL = "scan_volume"; /** + * Extra used with {@link #SCAN_FILE_CALL} or {@link #SCAN_VOLUME_CALL} to indicate that + * the file path originated from shell. + * + * {@hide} + */ + @TestApi + public static final String EXTRA_ORIGINATED_FROM_SHELL = + "android.intent.extra.originated_from_shell"; + + /** * The method name used by the media scanner and mtp to tell the media provider to * rescan and reclassify that have become unhidden because of renaming folders or * removing nomedia files diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index e14bb66828a4..3a02e85eee3b 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5704,7 +5704,6 @@ public final class Settings { * Defines whether Content Capture is enabled for the user. * @hide */ - @SystemApi @TestApi public static final String CONTENT_CAPTURE_ENABLED = "content_capture_enabled"; diff --git a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java index db242a2e265e..ca6676d87479 100644 --- a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java +++ b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java @@ -17,6 +17,7 @@ package android.service.contentcapture; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.view.contentcapture.ContentCaptureEvent; @@ -32,6 +33,7 @@ import java.util.List; * @hide */ @SystemApi +@TestApi @Deprecated public final class ContentCaptureEventsRequest implements Parcelable { // TODO(b/121051220): remove .java and .aidl once service implementation doesn't use it anymore diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index 020de7f24048..c98f09e13d97 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -21,6 +21,7 @@ import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.app.Service; import android.content.ComponentName; import android.content.Intent; @@ -48,9 +49,7 @@ import com.android.internal.os.IResultReceiver; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.Collections; import java.util.List; -import java.util.Set; /** * A service used to capture the content of the screen to provide contextual data in other areas of @@ -59,6 +58,7 @@ import java.util.Set; * @hide */ @SystemApi +@TestApi public abstract class ContentCaptureService extends Service { private static final String TAG = ContentCaptureService.class.getSimpleName(); @@ -166,10 +166,6 @@ public abstract class ContentCaptureService extends Service { /** * Explicitly limits content capture to the given packages and activities. * - * <p>When the whitelist is set, it overrides the values passed to - * {@link #setActivityContentCaptureEnabled(ComponentName, boolean)} - * and {@link #setPackageContentCaptureEnabled(String, boolean)}. - * * <p>To reset the whitelist, call it passing {@code null} to both arguments. * * <p>Useful when the service wants to restrict content capture to a category of apps, like @@ -194,76 +190,6 @@ public abstract class ContentCaptureService extends Service { } /** - * Defines whether content capture should be enabled for activities with such - * {@link android.content.ComponentName}. - * - * <p>Useful to blacklist a particular activity. - */ - public final void setActivityContentCaptureEnabled(@NonNull ComponentName activity, - boolean enabled) { - final IContentCaptureServiceCallback callback = mCallback; - if (callback == null) { - Log.w(TAG, "setActivityContentCaptureEnabled(): no server callback"); - return; - } - try { - callback.setActivityContentCaptureEnabled(activity, enabled); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); - } - } - - /** - * Defines whether content capture should be enabled for activities of the app with such - * {@code packageName}. - * - * <p>Useful to blacklist any activity from a particular app. - */ - public final void setPackageContentCaptureEnabled(@NonNull String packageName, - boolean enabled) { - final IContentCaptureServiceCallback callback = mCallback; - if (callback == null) { - Log.w(TAG, "setPackageContentCaptureEnabled(): no server callback"); - return; - } - try { - callback.setPackageContentCaptureEnabled(packageName, enabled); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); - } - } - - /** - * Gets the activities where content capture was disabled by - * {@link #setActivityContentCaptureEnabled(ComponentName, boolean)}. - */ - @NonNull - public final Set<ComponentName> getContentCaptureDisabledActivities() { - final IContentCaptureServiceCallback callback = mCallback; - if (callback == null) { - Log.w(TAG, "getContentCaptureDisabledActivities(): no server callback"); - return Collections.emptySet(); - } - //TODO(b/122595322): implement (using SyncResultReceiver) - return null; - } - - /** - * Gets the apps where content capture was disabled by - * {@link #setPackageContentCaptureEnabled(String, boolean)}. - */ - @NonNull - public final Set<String> getContentCaptureDisabledPackages() { - final IContentCaptureServiceCallback callback = mCallback; - if (callback == null) { - Log.w(TAG, "getContentCaptureDisabledPackages(): no server callback"); - return Collections.emptySet(); - } - //TODO(b/122595322): implement (using SyncResultReceiver) - return null; - } - - /** * Called when the Android system connects to service. * * <p>You should generally do initialization here rather than in {@link #onCreate}. diff --git a/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl b/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl index e84bd6fdf2b6..2a729b6dc874 100644 --- a/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl +++ b/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl @@ -28,8 +28,4 @@ import java.util.List; */ oneway interface IContentCaptureServiceCallback { void setContentCaptureWhitelist(in List<String> packages, in List<ComponentName> activities); - void setActivityContentCaptureEnabled(in ComponentName activity, boolean enabled); - void setPackageContentCaptureEnabled(in String packageName, boolean enabled); - void getContentCaptureDisabledActivities(in IResultReceiver receiver); - void getContentCaptureDisabledPackages(in IResultReceiver receiver); } diff --git a/core/java/android/service/contentcapture/SnapshotData.java b/core/java/android/service/contentcapture/SnapshotData.java index bc2116a441aa..c3af1f0cc608 100644 --- a/core/java/android/service/contentcapture/SnapshotData.java +++ b/core/java/android/service/contentcapture/SnapshotData.java @@ -19,6 +19,7 @@ package android.service.contentcapture; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.app.assist.AssistContent; import android.app.assist.AssistStructure; import android.os.Bundle; @@ -31,6 +32,7 @@ import android.os.Parcelable; * @hide */ @SystemApi +@TestApi public final class SnapshotData implements Parcelable { private final @NonNull Bundle mAssistData; diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java index 7026d2b1389c..2ba1e016e03d 100644 --- a/core/java/android/view/ImeInsetsSourceConsumer.java +++ b/core/java/android/view/ImeInsetsSourceConsumer.java @@ -18,10 +18,10 @@ package android.view; import static android.view.InsetsState.TYPE_IME; +import android.inputmethodservice.InputMethodService; 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; @@ -73,11 +73,7 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { return; } - if (setVisible) { - mController.show(Type.IME); - } else { - mController.hide(Type.IME); - } + mController.applyImeVisibility(setVisible); } @Override @@ -91,6 +87,30 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { mHasWindowFocus = false; } + /** + * Request {@link InputMethodManager} to show the IME. + * @return @see {@link android.view.InsetsSourceConsumer.ShowResult}. + */ + @Override + @ShowResult int requestShow(boolean fromIme) { + // TODO: ResultReceiver for IME. + // TODO: Set mShowOnNextImeRender to automatically show IME and guard it with a flag. + if (fromIme) { + return ShowResult.SHOW_IMMEDIATELY; + } + + return getImm().requestImeShow(null /* resultReceiver */) + ? ShowResult.SHOW_DELAYED : ShowResult.SHOW_FAILED; + } + + /** + * Notify {@link InputMethodService} that IME window is hidden. + */ + @Override + void notifyHidden() { + getImm().notifyImeHidden(); + } + 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); diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java index 625ddc2549cc..583651dee379 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -180,9 +180,10 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll for (int i = items.size() - 1; i >= 0; i--) { final InsetsSourceConsumer consumer = items.valueAt(i); final InsetsSource source = mInitialInsetsState.getSource(consumer.getType()); + final InsetsSourceControl control = consumer.getControl(); final SurfaceControl leash = consumer.getControl().getLeash(); - mTmpMatrix.setTranslate(source.getFrame().left, source.getFrame().top); + mTmpMatrix.setTranslate(control.getSurfacePosition().x, control.getSurfacePosition().y); mTmpFrame.set(source.getFrame()); addTranslationToMatrix(side, offset, mTmpMatrix, mTmpFrame); diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 8e773799d188..7ad97a6d393e 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -29,11 +29,16 @@ import android.graphics.Rect; import android.os.RemoteException; import android.util.ArraySet; import android.util.Log; +import android.util.Pair; import android.util.Property; import android.util.SparseArray; +import android.view.InsetsSourceConsumer.ShowResult; import android.view.InsetsState.InternalInsetType; import android.view.SurfaceControl.Transaction; +import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetType; +import android.view.animation.Interpolator; +import android.view.animation.PathInterpolator; import com.android.internal.annotations.VisibleForTesting; @@ -46,11 +51,13 @@ import java.util.ArrayList; */ public class InsetsController implements WindowInsetsController { - // TODO: Use animation scaling and more optimal duration. - private static final int ANIMATION_DURATION_MS = 400; + private static final int ANIMATION_DURATION_SHOW_MS = 275; + private static final int ANIMATION_DURATION_HIDE_MS = 340; + private static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f); private static final int DIRECTION_NONE = 0; private static final int DIRECTION_SHOW = 1; private static final int DIRECTION_HIDE = 2; + @IntDef ({DIRECTION_NONE, DIRECTION_SHOW, DIRECTION_HIDE}) private @interface AnimationDirection{} @@ -103,6 +110,8 @@ public class InsetsController implements WindowInsetsController { private ObjectAnimator mAnimator; private @AnimationDirection int mAnimationDirection; + private int mPendingTypesToShow; + public InsetsController(ViewRootImpl viewRoot) { mViewRoot = viewRoot; mAnimCallback = () -> { @@ -193,6 +202,12 @@ public class InsetsController implements WindowInsetsController { @Override public void show(@InsetType int types) { + show(types, false /* fromIme */); + } + + private void show(@InsetType int types, boolean fromIme) { + // TODO: Support a ResultReceiver for IME. + // TODO(b/123718661): Make show() work for multi-session IME. int typesReady = 0; final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types); for (int i = internalTypes.size() - 1; i >= 0; i--) { @@ -201,15 +216,18 @@ public class InsetsController implements WindowInsetsController { // Only one animator (with multiple InsetType) can run at a time. // previous one should be cancelled for simplicity. cancelExistingAnimation(); - } else if (consumer.isVisible() || mAnimationDirection == DIRECTION_SHOW) { - // no-op: already shown or animating in. + } else if (consumer.isVisible() + && (mAnimationDirection == DIRECTION_NONE + || mAnimationDirection == DIRECTION_HIDE)) { + // no-op: already shown or animating in (because window visibility is + // applied before starting animation). // TODO: When we have more than one types: handle specific case when // show animation is going on, but the current type is not becoming visible. continue; } typesReady |= InsetsState.toPublicType(consumer.getType()); } - applyAnimation(typesReady, true /* show */); + applyAnimation(typesReady, true /* show */, fromIme); } @Override @@ -220,35 +238,114 @@ public class InsetsController implements WindowInsetsController { InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i)); if (mAnimationDirection == DIRECTION_SHOW) { cancelExistingAnimation(); - } else if (!consumer.isVisible() || mAnimationDirection == DIRECTION_HIDE) { + } else if (!consumer.isVisible() + && (mAnimationDirection == DIRECTION_NONE + || mAnimationDirection == DIRECTION_HIDE)) { // no-op: already hidden or animating out. continue; } typesReady |= InsetsState.toPublicType(consumer.getType()); } - applyAnimation(typesReady, false /* show */); + applyAnimation(typesReady, false /* show */, false /* fromIme */); } @Override public void controlWindowInsetsAnimation(@InsetType int types, WindowInsetsAnimationControlListener listener) { + controlWindowInsetsAnimation(types, listener, false /* fromIme */); + } + + private void controlWindowInsetsAnimation(@InsetType int types, + WindowInsetsAnimationControlListener listener, boolean fromIme) { + if (types == 0) { + // nothing to animate. + return; + } // TODO: Check whether we already have a controller. final ArraySet<Integer> internalTypes = mState.toInternalType(types); final SparseArray<InsetsSourceConsumer> consumers = new SparseArray<>(); + + Pair<Integer, Boolean> typesReadyPair = collectConsumers(fromIme, internalTypes, consumers); + int typesReady = typesReadyPair.first; + boolean isReady = typesReadyPair.second; + if (!isReady) { + // IME isn't ready, all requested types would be shown once IME is ready. + mPendingTypesToShow = typesReady; + // TODO: listener for pending types. + return; + } + + // pending types from previous request. + typesReady = collectPendingConsumers(typesReady, consumers); + + if (typesReady == 0) { + listener.onCancelled(); + return; + } + + final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(consumers, + mFrame, mState, listener, typesReady, + () -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView), this); + mAnimationControls.add(controller); + } + + /** + * @return Pair of (types ready to animate, is ready to animate). + */ + private Pair<Integer, Boolean> collectConsumers(boolean fromIme, + ArraySet<Integer> internalTypes, SparseArray<InsetsSourceConsumer> consumers) { + int typesReady = 0; + boolean isReady = true; for (int i = internalTypes.size() - 1; i >= 0; i--) { InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i)); if (consumer.getControl() != null) { + if (!consumer.isVisible()) { + // Show request + switch(consumer.requestShow(fromIme)) { + case ShowResult.SHOW_IMMEDIATELY: + typesReady |= InsetsState.toPublicType(TYPE_IME); + break; + case ShowResult.SHOW_DELAYED: + isReady = false; + break; + case ShowResult.SHOW_FAILED: + // IME cannot be shown (since it didn't have focus), proceed + // with animation of other types. + if (mPendingTypesToShow != 0) { + // remove IME from pending because view no longer has focus. + mPendingTypesToShow &= ~InsetsState.toPublicType(TYPE_IME); + } + break; + } + } else { + // Hide request + // TODO: Move notifyHidden() to beginning of the hide animation + // (when visibility actually changes using hideDirectly()). + consumer.notifyHidden(); + typesReady |= InsetsState.toPublicType(consumer.getType()); + } consumers.put(consumer.getType(), consumer); } else { // TODO: Let calling app know it's not possible, or wait // TODO: Remove it from types } } - final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(consumers, - mFrame, mState, listener, types, - () -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView), this); - mAnimationControls.add(controller); + return new Pair<>(typesReady, isReady); + } + + private int collectPendingConsumers(@InsetType int typesReady, + SparseArray<InsetsSourceConsumer> consumers) { + if (mPendingTypesToShow != 0) { + typesReady |= mPendingTypesToShow; + final ArraySet<Integer> internalTypes = mState.toInternalType(mPendingTypesToShow); + for (int i = internalTypes.size() - 1; i >= 0; i--) { + InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i)); + consumers.put(consumer.getType(), consumer); + } + mPendingTypesToShow = 0; + } + return typesReady; } private void applyLocalVisibilityOverride() { @@ -293,6 +390,19 @@ public class InsetsController implements WindowInsetsController { return mViewRoot; } + /** + * Used by {@link ImeInsetsSourceConsumer} when IME decides to be shown/hidden. + * @hide + */ + @VisibleForTesting + public void applyImeVisibility(boolean setVisible) { + if (setVisible) { + show(Type.IME, true /* fromIme */); + } else { + hide(Type.IME); + } + } + private InsetsSourceConsumer createConsumerOfType(int type) { if (type == TYPE_IME) { return new ImeInsetsSourceConsumer(mState, Transaction::new, this); @@ -321,7 +431,7 @@ public class InsetsController implements WindowInsetsController { } } - private void applyAnimation(@InsetType final int types, boolean show) { + private void applyAnimation(@InsetType final int types, boolean show, boolean fromIme) { if (types == 0) { // nothing to animate. return; @@ -341,7 +451,10 @@ public class InsetsController implements WindowInsetsController { show ? controller.getHiddenStateInsets() : controller.getShownStateInsets(), show ? controller.getShownStateInsets() : controller.getHiddenStateInsets() ); - mAnimator.setDuration(ANIMATION_DURATION_MS); + mAnimator.setDuration(show + ? ANIMATION_DURATION_SHOW_MS + : ANIMATION_DURATION_HIDE_MS); + mAnimator.setInterpolator(INTERPOLATOR); mAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationCancel(Animator animation) { @@ -366,7 +479,7 @@ public class InsetsController implements WindowInsetsController { // TODO: Instead of clearing this here, properly wire up // InsetsAnimationControlImpl.finish() to remove this from mAnimationControls. mAnimationControls.clear(); - controlWindowInsetsAnimation(types, listener); + controlWindowInsetsAnimation(types, listener, fromIme); } private void hideDirectly(@InsetType int types) { diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index cccfd870a3e4..eab83ce34708 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -16,12 +16,15 @@ package android.view; +import android.annotation.IntDef; import android.annotation.Nullable; import android.view.InsetsState.InternalInsetType; import android.view.SurfaceControl.Transaction; import com.android.internal.annotations.VisibleForTesting; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.function.Supplier; /** @@ -30,6 +33,25 @@ import java.util.function.Supplier; */ public class InsetsSourceConsumer { + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = {ShowResult.SHOW_IMMEDIATELY, ShowResult.SHOW_DELAYED, ShowResult.SHOW_FAILED}) + @interface ShowResult { + /** + * Window type is ready to be shown, will be shown immidiately. + */ + int SHOW_IMMEDIATELY = 0; + /** + * Result will be delayed. Window needs to be prepared or request is not from controller. + * Request will be delegated to controller and may or may not be shown. + */ + int SHOW_DELAYED = 1; + /** + * Window will not be shown because one of the conditions couldn't be met. + * (e.g. in IME's case, when no editor is focused.) + */ + int SHOW_FAILED = 2; + } + protected final InsetsController mController; protected boolean mVisible; private final Supplier<Transaction> mTransactionSupplier; @@ -104,6 +126,25 @@ public class InsetsSourceConsumer { return mVisible; } + /** + * Request to show current window type. + * + * @param fromController {@code true} if request is coming from controller. + * (e.g. in IME case, controller is + * {@link android.inputmethodservice.InputMethodService}). + * @return @see {@link ShowResult}. + */ + @ShowResult int requestShow(boolean fromController) { + return ShowResult.SHOW_IMMEDIATELY; + } + + /** + * Notify listeners that window is now hidden. + */ + void notifyHidden() { + // no-op for types that always return ShowResult#SHOW_IMMEDIATELY. + } + private void setVisible(boolean visible) { if (mVisible == visible) { return; diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java index 9383e6c57854..9fb6bdba48e3 100644 --- a/core/java/android/view/InsetsSourceControl.java +++ b/core/java/android/view/InsetsSourceControl.java @@ -29,10 +29,13 @@ public class InsetsSourceControl implements Parcelable { private final @InternalInsetType int mType; private final SurfaceControl mLeash; + private final Point mSurfacePosition; - public InsetsSourceControl(@InternalInsetType int type, SurfaceControl leash) { + public InsetsSourceControl(@InternalInsetType int type, SurfaceControl leash, + Point surfacePosition) { mType = type; mLeash = leash; + mSurfacePosition = surfacePosition; } public int getType() { @@ -46,6 +49,19 @@ public class InsetsSourceControl implements Parcelable { public InsetsSourceControl(Parcel in) { mType = in.readInt(); mLeash = in.readParcelable(null /* loader */); + mSurfacePosition = in.readParcelable(null /* loader */); + } + + public boolean setSurfacePosition(int left, int top) { + if (mSurfacePosition.equals(left, top)) { + return false; + } + mSurfacePosition.set(left, top); + return true; + } + + public Point getSurfacePosition() { + return mSurfacePosition; } @Override @@ -57,6 +73,7 @@ public class InsetsSourceControl implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mType); dest.writeParcelable(mLeash, 0 /* flags*/); + dest.writeParcelable(mSurfacePosition, 0 /* flags*/); } public static final Creator<InsetsSourceControl> CREATOR diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index ecbec652fcf0..cd5207c50d1d 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 & SurfaceControl.OPAQUE) != 0) { mBackgroundControl.show(); mBackgroundControl.setLayer(Integer.MIN_VALUE); } else { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 992b99617064..0295dc8d2de1 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -82,6 +82,7 @@ import android.os.Trace; import android.sysprop.DisplayProperties; import android.text.InputType; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.AttributeSet; import android.util.FloatProperty; import android.util.LayoutDirection; @@ -126,7 +127,6 @@ import android.widget.FrameLayout; import android.widget.ScrollBarDrawable; import com.android.internal.R; -import com.android.internal.util.Preconditions; import com.android.internal.view.TooltipPopup; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.widget.ScrollBarUtils; @@ -813,6 +813,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private static final String CONTENT_CAPTURE_LOG_TAG = "View.ContentCapture"; + private static final boolean DEBUG_CONTENT_CAPTURE = false; + /** * When set to true, this view will save its attribute data. * @@ -3393,9 +3395,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Masks for mPrivateFlags4, as generated by dumpFlags(): * * |-------|-------|-------|-------| - * 1111 PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK - * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED - * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED + * 1111 PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK + * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED + * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED + * 1 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED + * 1 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE + * 11 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK * |-------|-------|-------|-------| */ @@ -3422,6 +3427,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED = 0x10; private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED = 0x20; + /* + * Flags used to cache the value returned by isImportantForContentCapture while the view + * hierarchy is being traversed. + */ + private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED = 0x40; + private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE = 0x80; + + private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK = + PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED + | PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE; + /* End of masks for mPrivateFlags4 */ /** @hide */ @@ -4140,9 +4156,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * The layout parameters associated with this view and used by the parent * {@link android.view.ViewGroup} to determine how this view should be * laid out. + * + * The field should not be used directly. Instead {@link #getLayoutParams()} and {@link + * #setLayoutParams(ViewGroup.LayoutParams)} should be used. The setter guarantees internal + * state correctness of the class. * {@hide} */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) protected ViewGroup.LayoutParams mLayoutParams; /** @@ -5042,12 +5062,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * view (through {@link #setContentCaptureSession(ContentCaptureSession)}. */ @Nullable - private WeakReference<ContentCaptureSession> mContentCaptureSession; + private ContentCaptureSession mContentCaptureSession; @LayoutRes private int mSourceLayoutId = ID_NULL; /** + * Cached reference to the {@link ContentCaptureSession}, is reset on {@link #invalidate()}. + */ + private ContentCaptureSession mCachedContentCaptureSession; + + /** * Simple constructor to use when creating a view from code. * * @param context The Context the view is running in, through which it can @@ -8233,15 +8258,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * </ul> */ public void onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) { - if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { - Trace.traceBegin(Trace.TRACE_TAG_VIEW, - "onProvideContentCaptureStructure() for " + getClass().getSimpleName()); - } - try { - onProvideStructure(structure, VIEW_STRUCTURE_FOR_CONTENT_CAPTURE, flags); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_VIEW); - } + onProvideStructure(structure, VIEW_STRUCTURE_FOR_CONTENT_CAPTURE, flags); } /** @hide */ @@ -8952,6 +8969,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS */ public final boolean isImportantForContentCapture() { + boolean isImportant; + if ((mPrivateFlags4 & PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED) != 0) { + isImportant = (mPrivateFlags4 & PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE) != 0; + return isImportant; + } + + isImportant = calculateIsImportantForContentCapture(); + + mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE; + if (isImportant) { + mPrivateFlags4 |= PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE; + } + mPrivateFlags4 |= PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED; + return isImportant; + } + + /** + * Calculates whether the flag is important for content capture so it can be used by + * {@link #isImportantForContentCapture()} while the tree is traversed. + */ + private boolean calculateIsImportantForContentCapture() { // Check parent mode to ensure we're important ViewParent parent = mParent; while (parent instanceof View) { @@ -8992,9 +9030,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } // View group is important if at least one children also is - //TODO(b/111276913): decide if we really need to send the relevant parents or just the - // leaves (with absolute coordinates). If it's the latter, then we need to update this - // javadoc and ViewGroup's implementation. if (this instanceof ViewGroup) { final ViewGroup group = (ViewGroup) this; for (int i = 0; i < group.getChildCount(); i++) { @@ -9031,6 +9066,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * </ol> */ private void notifyAppearedOrDisappearedForContentCaptureIfNeeded(boolean appeared) { + AttachInfo ai = mAttachInfo; + // Skip it while the view is being laided out for the first time + if (ai != null && !ai.mReadyForContentCaptureUpdates) return; + if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCapture(" + appeared + ") for " + getClass().getSimpleName()); @@ -9043,24 +9082,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } private void notifyAppearedOrDisappearedForContentCaptureIfNeededNoTrace(boolean appeared) { + AttachInfo ai = mAttachInfo; + // First check if context has client, so it saves a service lookup when it doesn't if (!mContext.isContentCaptureSupported()) return; // Then check if it's enabled in the context... - final ContentCaptureManager ccm = mContext.getSystemService(ContentCaptureManager.class); + final ContentCaptureManager ccm = ai != null ? ai.getContentCaptureManager(mContext) + : mContext.getSystemService(ContentCaptureManager.class); if (ccm == null || !ccm.isContentCaptureEnabled()) return; // ... and finally at the view level // NOTE: isImportantForContentCapture() is more expensive than cm.isContentCaptureEnabled() if (!isImportantForContentCapture()) return; - final ContentCaptureSession session = getContentCaptureSession(ccm); + ContentCaptureSession session = getContentCaptureSession(); if (session == null) return; if (appeared) { if (!isLaidOut() || getVisibility() != VISIBLE || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) { - if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) { + if (DEBUG_CONTENT_CAPTURE) { Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'appeared' on " + this + ": laid=" + isLaidOut() + ", visibleToUser=" + isVisibleToUser() + ", visible=" + (getVisibility() == VISIBLE) @@ -9071,8 +9113,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } return; } - mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED; - mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED; + setNotifiedContentCaptureAppeared(); + + // TODO(b/123307965): instead of post, we should queue it on AttachInfo and then + // dispatch on RootImpl, as we're doing with the removed ones (in that case, we should + // merge the delayNotifyContentCaptureDisappeared() into a more generic method that + // takes a session and a command, where the command is either view added or removed // The code below doesn't take much for a unique view, but it's called for all views // the first time the view hiearchy is laid off, which could acccumulative delay the @@ -9086,7 +9132,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } else { if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) == 0 || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0) { - if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) { + if (DEBUG_CONTENT_CAPTURE) { Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'disappeared' on " + this + ": laid=" + isLaidOut() + ", visibleToUser=" + isVisibleToUser() + ", visible=" + (getVisibility() == VISIBLE) @@ -9099,11 +9145,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED; mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED; - Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT, - () -> session.notifyViewDisappeared(getAutofillId()), /* token= */ null); + + if (ai != null) { + ai.delayNotifyContentCaptureDisappeared(session, getAutofillId()); + } else { + if (DEBUG_CONTENT_CAPTURE) { + Log.v(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on gone for " + this); + } + Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT, + () -> session.notifyViewDisappeared(getAutofillId()), /* token= */ null); + } } } + private void setNotifiedContentCaptureAppeared() { + mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED; + mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED; + } + /** * Sets the (optional) {@link ContentCaptureSession} associated with this view. * @@ -9127,9 +9186,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link ContentCaptureSession#createContentCaptureSession( * android.view.contentcapture.ContentCaptureContext)}. */ - public void setContentCaptureSession(@NonNull ContentCaptureSession contentCaptureSession) { - mContentCaptureSession = new WeakReference<>( - Preconditions.checkNotNull(contentCaptureSession)); + public void setContentCaptureSession(@Nullable ContentCaptureSession contentCaptureSession) { + mContentCaptureSession = contentCaptureSession; } /** @@ -9141,8 +9199,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @Nullable public final ContentCaptureSession getContentCaptureSession() { + if (mCachedContentCaptureSession != null) { + return mCachedContentCaptureSession; + } + + mCachedContentCaptureSession = getAndCacheContentCaptureSession(); + return mCachedContentCaptureSession; + } + + @Nullable + private ContentCaptureSession getAndCacheContentCaptureSession() { // First try the session explicitly set by setContentCaptureSession() - if (mContentCaptureSession != null) return mContentCaptureSession.get(); + if (mContentCaptureSession != null) return mContentCaptureSession; // Then the session explicitly set in an ancestor ContentCaptureSession session = null; @@ -9159,21 +9227,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return session; } - /** - * Optimized version of {@link #getContentCaptureSession()} that avoids a service lookup. - */ - @Nullable - private ContentCaptureSession getContentCaptureSession(@NonNull ContentCaptureManager ccm) { - if (mContentCaptureSession != null) return mContentCaptureSession.get(); - - ContentCaptureSession session = null; - if (mParent instanceof View) { - session = ((View) mParent).getContentCaptureSession(ccm); - } - - return session != null ? session : ccm.getMainContentCaptureSession(); - } - @Nullable private AutofillManager getAutofillManager() { return mContext.getSystemService(AutofillManager.class); @@ -9346,6 +9399,62 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Dispatches the initial Content Capture events for a view structure. + * + * @hide + */ + public void dispatchInitialProvideContentCaptureStructure(@NonNull ContentCaptureManager ccm) { + AttachInfo ai = mAttachInfo; + if (ai == null) { + Log.w(CONTENT_CAPTURE_LOG_TAG, + "dispatchProvideContentCaptureStructure(): no AttachInfo for " + this); + return; + } + + // We must set it before checkign if the view itself is important, because it might + // initially not be (for example, if it's empty), although that might change later (for + // example, if important views are added) + ai.mReadyForContentCaptureUpdates = true; + + if (!isImportantForContentCapture()) { + if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.DEBUG)) { + Log.d(CONTENT_CAPTURE_LOG_TAG, + "dispatchProvideContentCaptureStructure(): decorView is not important"); + } + return; + } + + ai.mContentCaptureManager = ccm; + + ContentCaptureSession session = getContentCaptureSession(); + if (session == null) { + if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.DEBUG)) { + Log.d(CONTENT_CAPTURE_LOG_TAG, + "dispatchProvideContentCaptureStructure(): no session for " + this); + } + return; + } + + session.internalNotifyViewHierarchyEvent(/* started= */ true); + try { + dispatchProvideContentCaptureStructure(); + } finally { + session.internalNotifyViewHierarchyEvent(/* started= */ false); + } + } + + /** @hide */ + void dispatchProvideContentCaptureStructure() { + ContentCaptureSession session = getContentCaptureSession(); + if (session != null) { + ViewStructure structure = session.newViewStructure(this); + onProvideContentCaptureStructure(structure, /* flags= */ 0); + setNotifiedContentCaptureAppeared(); + session.notifyViewAppeared(structure); + } + } + + /** * @see #onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo) * * Note: Called from the default {@link AccessibilityDelegate}. @@ -17457,6 +17566,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return; } + // Reset content capture caches + mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK; + mCachedContentCaptureSession = null; + if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS) || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED @@ -27963,6 +28076,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, View mTooltipHost; /** + * The initial structure has been reported so the view is ready to report updates. + */ + boolean mReadyForContentCaptureUpdates; + + /** + * Map of ids (per session) that need to be notified after as gone the view hierchy is + * traversed. + */ + // TODO(b/121197119): use SparseArray once session id becomes integer + ArrayMap<String, ArrayList<AutofillId>> mContentCaptureRemovedIds; + + /** + * Cached reference to the {@link ContentCaptureManager}. + */ + ContentCaptureManager mContentCaptureManager; + + /** * Creates a new set of attachment information with the specified * events handler and thread. * @@ -27980,6 +28110,31 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mRootCallbacks = effectPlayer; mTreeObserver = new ViewTreeObserver(context); } + + private void delayNotifyContentCaptureDisappeared(@NonNull ContentCaptureSession session, + @NonNull AutofillId id) { + if (mContentCaptureRemovedIds == null) { + // Most of the time there will be just one session, so intial capacity is 1 + mContentCaptureRemovedIds = new ArrayMap<>(1); + } + String sessionId = session.getId(); + // TODO: life would be much easier if we provided a MultiMap implementation somwhere... + ArrayList<AutofillId> ids = mContentCaptureRemovedIds.get(sessionId); + if (ids == null) { + ids = new ArrayList<>(); + mContentCaptureRemovedIds.put(sessionId, ids); + } + ids.add(id); + } + + @Nullable + private ContentCaptureManager getContentCaptureManager(@NonNull Context context) { + if (mContentCaptureManager != null) { + return mContentCaptureManager; + } + mContentCaptureManager = context.getSystemService(ContentCaptureManager.class); + return mContentCaptureManager; + } } /** diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 0986cfa454b6..e0e784368bbb 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -3603,7 +3603,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return; } - final ChildListForAutoFill children = getChildrenForAutofill(flags); + final ChildListForAutoFillOrContentCapture children = getChildrenForAutofill(flags); final int childrenCount = children.size(); structure.setChildCount(childrenCount); for (int i = 0; i < childrenCount; i++) { @@ -3614,13 +3614,31 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager children.recycle(); } + /** @hide */ + @Override + public void dispatchProvideContentCaptureStructure() { + super.dispatchProvideContentCaptureStructure(); + + if (!isLaidOut()) return; + + final ChildListForAutoFillOrContentCapture children = getChildrenForContentCapture(); + final int childrenCount = children.size(); + for (int i = 0; i < childrenCount; i++) { + final View child = children.get(i); + child.dispatchProvideContentCaptureStructure(); + } + children.recycle(); + } + /** * Gets the children for autofill. Children for autofill are the first * level descendants that are important for autofill. The returned * child list object is pooled and the caller must recycle it once done. * @hide */ - private @NonNull ChildListForAutoFill getChildrenForAutofill(@AutofillFlags int flags) { - final ChildListForAutoFill children = ChildListForAutoFill.obtain(); + private @NonNull ChildListForAutoFillOrContentCapture getChildrenForAutofill( + @AutofillFlags int flags) { + final ChildListForAutoFillOrContentCapture children = ChildListForAutoFillOrContentCapture + .obtain(); populateChildrenForAutofill(children, flags); return children; } @@ -3647,6 +3665,34 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } + private @NonNull ChildListForAutoFillOrContentCapture getChildrenForContentCapture() { + final ChildListForAutoFillOrContentCapture children = ChildListForAutoFillOrContentCapture + .obtain(); + populateChildrenForContentCapture(children); + return children; + } + + /** @hide */ + private void populateChildrenForContentCapture(ArrayList<View> list) { + final int childrenCount = mChildrenCount; + if (childrenCount <= 0) { + return; + } + final ArrayList<View> preorderedList = buildOrderedChildList(); + final boolean customOrder = preorderedList == null + && isChildrenDrawingOrderEnabled(); + for (int i = 0; i < childrenCount; i++) { + final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); + final View child = (preorderedList == null) + ? mChildren[childIndex] : preorderedList.get(childIndex); + if (child.isImportantForContentCapture()) { + list.add(child); + } else if (child instanceof ViewGroup) { + ((ViewGroup) child).populateChildrenForContentCapture(list); + } + } + } + private static View getAndVerifyPreorderedView(ArrayList<View> preorderedList, View[] children, int childIndex) { final View child; @@ -8544,16 +8590,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * Pooled class that to hold the children for autifill. */ - static class ChildListForAutoFill extends ArrayList<View> { + private static class ChildListForAutoFillOrContentCapture extends ArrayList<View> { private static final int MAX_POOL_SIZE = 32; - private static final Pools.SimplePool<ChildListForAutoFill> sPool = + private static final Pools.SimplePool<ChildListForAutoFillOrContentCapture> sPool = new Pools.SimplePool<>(MAX_POOL_SIZE); - public static ChildListForAutoFill obtain() { - ChildListForAutoFill list = sPool.acquire(); + public static ChildListForAutoFillOrContentCapture obtain() { + ChildListForAutoFillOrContentCapture list = sPool.acquire(); if (list == null) { - list = new ChildListForAutoFill(); + list = new ChildListForAutoFillOrContentCapture(); } return list; } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 9a317db84f5d..47528a05f5a2 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -103,7 +103,10 @@ import android.view.accessibility.IAccessibilityInteractionConnection; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.Interpolator; +import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; +import android.view.contentcapture.ContentCaptureManager; +import android.view.contentcapture.MainContentCaptureSession; import android.view.inputmethod.InputMethodManager; import android.widget.Scroller; @@ -154,6 +157,7 @@ public final class ViewRootImpl implements ViewParent, private static final boolean DEBUG_FPS = false; private static final boolean DEBUG_INPUT_STAGES = false || LOCAL_LOGV; private static final boolean DEBUG_KEEP_SCREEN_ON = false || LOCAL_LOGV; + private static final boolean DEBUG_CONTENT_CAPTURE = false || LOCAL_LOGV; /** * Set to false if we do not want to use the multi threaded renderer even though @@ -180,7 +184,7 @@ public final class ViewRootImpl implements ViewParent, * @see #USE_NEW_INSETS_PROPERTY * @hide */ - public static final int sNewInsetsMode = + public static int sNewInsetsMode = SystemProperties.getInt(USE_NEW_INSETS_PROPERTY, 0); /** @@ -412,6 +416,7 @@ public final class ViewRootImpl implements ViewParent, boolean mApplyInsetsRequested; boolean mLayoutRequested; boolean mFirst; + boolean mPerformContentCapture; boolean mReportNextDraw; boolean mFullRedrawNeeded; boolean mNewSurfaceNeeded; @@ -608,6 +613,7 @@ public final class ViewRootImpl implements ViewParent, mTransparentRegion = new Region(); mPreviousTransparentRegion = new Region(); mFirst = true; // true for the first time the view is added + mPerformContentCapture = true; // also true for the first time the view is added mAdded = false; mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this, context); @@ -2756,6 +2762,24 @@ public final class ViewRootImpl implements ViewParent, } } + if (mAttachInfo.mContentCaptureRemovedIds != null) { + MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager + .getMainContentCaptureSession(); + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureViewsGone"); + try { + for (int i = 0; i < mAttachInfo.mContentCaptureRemovedIds.size(); i++) { + String sessionId = mAttachInfo.mContentCaptureRemovedIds + .keyAt(i); + ArrayList<AutofillId> ids = mAttachInfo.mContentCaptureRemovedIds + .valueAt(i); + mainSession.notifyViewsDisappeared(sessionId, ids); + } + mAttachInfo.mContentCaptureRemovedIds = null; + } finally { + Trace.traceEnd(Trace.TRACE_TAG_VIEW); + } + } + mIsInTraversal = false; } @@ -3451,6 +3475,35 @@ public final class ViewRootImpl implements ViewParent, pendingDrawFinished(); } } + if (mPerformContentCapture) { + performContentCapture(); + } + } + + private void performContentCapture() { + mPerformContentCapture = false; // One-time offer! + final View rootView = mView; + if (DEBUG_CONTENT_CAPTURE) { + Log.v(mTag, "dispatchContentCapture() on " + rootView); + } + if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchContentCapture() for " + + getClass().getSimpleName()); + } + try { + // First check if context supports it, so it saves a service lookup when it doesn't + if (!mContext.isContentCaptureSupported()) return; + + // Then check if it's enabled in the contex itself. + final ContentCaptureManager ccm = mContext + .getSystemService(ContentCaptureManager.class); + if (ccm == null || !ccm.isContentCaptureEnabled()) return; + + // Content capture is a go! + rootView.dispatchInitialProvideContentCaptureStructure(ccm); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_VIEW); + } } private boolean draw(boolean fullRedrawNeeded) { diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java index 63c21f352e74..acb81e086461 100644 --- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java @@ -93,6 +93,11 @@ final class ChildContentCaptureSession extends ContentCaptureSession { } @Override + public void internalNotifyViewHierarchyEvent(boolean started) { + getMainCaptureSession().notifyInitialViewHierarchyEvent(mId, started); + } + + @Override boolean isContentCaptureEnabled() { return getMainCaptureSession().isContentCaptureEnabled(); } diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java index 5814759d00df..6a9759db78a0 100644 --- a/core/java/android/view/contentcapture/ContentCaptureContext.java +++ b/core/java/android/view/contentcapture/ContentCaptureContext.java @@ -19,6 +19,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.app.TaskInfo; import android.content.ComponentName; import android.content.Context; @@ -57,6 +58,7 @@ public final class ContentCaptureContext implements Parcelable { * @hide */ @SystemApi + @TestApi public static final int FLAG_DISABLED_BY_APP = 0x1; /** @@ -67,6 +69,7 @@ public final class ContentCaptureContext implements Parcelable { * @hide */ @SystemApi + @TestApi public static final int FLAG_DISABLED_BY_FLAG_SECURE = 0x2; /** @hide */ @@ -136,6 +139,7 @@ public final class ContentCaptureContext implements Parcelable { * @hide */ @SystemApi + @TestApi @Nullable public Bundle getExtras() { return mExtras; @@ -147,6 +151,7 @@ public final class ContentCaptureContext implements Parcelable { * @hide */ @SystemApi + @TestApi @Nullable public Uri getUri() { return mUri; @@ -158,6 +163,7 @@ public final class ContentCaptureContext implements Parcelable { * @hide */ @SystemApi + @TestApi @Nullable public String getAction() { return mAction; @@ -169,6 +175,7 @@ public final class ContentCaptureContext implements Parcelable { * @hide */ @SystemApi + @TestApi public int getTaskId() { return mTaskId; } @@ -179,6 +186,7 @@ public final class ContentCaptureContext implements Parcelable { * @hide */ @SystemApi + @TestApi public @Nullable ComponentName getActivityComponent() { return mComponentName; } @@ -191,6 +199,7 @@ public final class ContentCaptureContext implements Parcelable { * @hide */ @SystemApi + @TestApi public @Nullable ContentCaptureSessionId getParentSessionId() { return mParentSessionId == null ? null : new ContentCaptureSessionId(mParentSessionId); } @@ -207,6 +216,7 @@ public final class ContentCaptureContext implements Parcelable { * @hide */ @SystemApi + @TestApi public int getDisplayId() { return mDisplayId; } @@ -220,7 +230,8 @@ public final class ContentCaptureContext implements Parcelable { * @hide */ @SystemApi - public @ContextCreationFlags int getFlags() { + @TestApi + public @ContextCreationFlags int getFlags() { return mFlags; } diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java index a6d44729aee5..dfac35ddf58b 100644 --- a/core/java/android/view/contentcapture/ContentCaptureEvent.java +++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java @@ -21,6 +21,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; @@ -36,6 +37,7 @@ import java.util.List; /** @hide */ @SystemApi +@TestApi public final class ContentCaptureEvent implements Parcelable { private static final String TAG = ContentCaptureEvent.class.getSimpleName(); @@ -69,13 +71,33 @@ public final class ContentCaptureEvent implements Parcelable { */ public static final int TYPE_VIEW_TEXT_CHANGED = 3; - // TODO(b/111276913): add event to indicate when FLAG_SECURE was changed? + /** + * Called before events (such as {@link #TYPE_VIEW_APPEARED}) representing the initial view + * hierarchy are sent. + * + * <p><b>NOTE</b>: there is no guarantee this event will be sent. For example, it's not sent + * if the initial view hierarchy doesn't initially have any view that's important for content + * capture. + */ + public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; + + /** + * Called after events (such as {@link #TYPE_VIEW_APPEARED}) representing the initial view + * hierarchy are sent. + * + * <p><b>NOTE</b>: there is no guarantee this event will be sent. For example, it's not sent + * if the initial view hierarchy doesn't initially have any view that's important for content + * capture. + */ + public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; /** @hide */ @IntDef(prefix = { "TYPE_" }, value = { TYPE_VIEW_APPEARED, TYPE_VIEW_DISAPPEARED, - TYPE_VIEW_TEXT_CHANGED + TYPE_VIEW_TEXT_CHANGED, + TYPE_INITIAL_VIEW_TREE_APPEARING, + TYPE_INITIAL_VIEW_TREE_APPEARED }) @Retention(RetentionPolicy.SOURCE) public @interface EventType{} @@ -108,8 +130,10 @@ public final class ContentCaptureEvent implements Parcelable { return this; } - private void setAutofillIds(@NonNull ArrayList<AutofillId> ids) { + /** @hide */ + public ContentCaptureEvent setAutofillIds(@NonNull ArrayList<AutofillId> ids) { mIds = Preconditions.checkNotNull(ids); + return this; } /** @@ -193,7 +217,8 @@ public final class ContentCaptureEvent implements Parcelable { * Gets the type of the event. * * @return one of {@link #TYPE_VIEW_APPEARED}, {@link #TYPE_VIEW_DISAPPEARED}, - * or {@link #TYPE_VIEW_TEXT_CHANGED}. + * {@link #TYPE_VIEW_TEXT_CHANGED}, {@link #TYPE_INITIAL_VIEW_TREE_APPEARING}, or + * {@link #TYPE_INITIAL_VIEW_TREE_APPEARED}. */ public @EventType int getType() { return mType; @@ -372,6 +397,10 @@ public final class ContentCaptureEvent implements Parcelable { return "VIEW_DISAPPEARED"; case TYPE_VIEW_TEXT_CHANGED: return "VIEW_TEXT_CHANGED"; + case TYPE_INITIAL_VIEW_TREE_APPEARING: + return "INITIAL_VIEW_HIERARCHY_STARTED"; + case TYPE_INITIAL_VIEW_TREE_APPEARED: + return "INITIAL_VIEW_HIERARCHY_FINISHED"; default: return "UKNOWN_TYPE: " + type; } diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index f31856c80477..67e81033e989 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.SystemService; +import android.annotation.TestApi; import android.annotation.UiThread; import android.content.ComponentName; import android.content.Context; @@ -46,7 +47,7 @@ import java.io.PrintWriter; * of every method. */ /** - * TODO(b/111276913): add javadocs / implement + * TODO(b/123577059): add javadocs / implement */ @SystemService(Context.CONTENT_CAPTURE_MANAGER_SERVICE) public final class ContentCaptureManager { @@ -217,6 +218,7 @@ public final class ContentCaptureManager { * @hide */ @SystemApi + @TestApi public boolean isContentCaptureFeatureEnabled() { if (mService == null) return false; @@ -249,6 +251,7 @@ public final class ContentCaptureManager { * @hide */ @SystemApi + @TestApi public void setContentCaptureFeatureEnabled(boolean enabled) { if (DEBUG) Log.d(TAG, "setContentCaptureFeatureEnabled(): setting to " + enabled); diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index 68a3e8a1eb32..e028961692f9 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -204,6 +204,12 @@ public abstract class ContentCaptureSession implements AutoCloseable { return mId.hashCode(); } + /** @hide */ + @NonNull + public String getId() { + return mId; + } + /** * Creates a new {@link ContentCaptureSession}. * @@ -362,6 +368,9 @@ public abstract class ContentCaptureSession implements AutoCloseable { abstract void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text); + /** @hide */ + public abstract void internalNotifyViewHierarchyEvent(boolean started); + /** * Creates a {@link ViewStructure} for a "standard" view. * diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index 034c8fae0843..0605fa3f39a2 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -15,6 +15,8 @@ */ package android.view.contentcapture; +import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARED; +import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARING; import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED; @@ -66,6 +68,8 @@ public final class MainContentCaptureSession extends ContentCaptureSession { private static final String TAG = MainContentCaptureSession.class.getSimpleName(); + private static final boolean FORCE_FLUSH = true; + /** * Handler message used to flush the buffer. */ @@ -270,6 +274,10 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } } + private void handleSendEvent(@NonNull ContentCaptureEvent event) { + handleSendEvent(event, /* forceFlush= */ false); + } + private void handleSendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) { final int eventType = event.getType(); if (VERBOSE) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event); @@ -518,6 +526,11 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } @Override + public void internalNotifyViewHierarchyEvent(boolean started) { + notifyInitialViewHierarchyEvent(mId, started); + } + + @Override boolean isContentCaptureEnabled() { return super.isContentCaptureEnabled() && mManager.isContentCaptureEnabled(); } @@ -536,33 +549,57 @@ public final class MainContentCaptureSession extends ContentCaptureSession { new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED) .setParentSessionId(parentSessionId) .setClientContext(clientContext), - /* forceFlush= */ true)); + FORCE_FLUSH)); } void notifyChildSessionFinished(@NonNull String parentSessionId, @NonNull String childSessionId) { mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this, new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED) - .setParentSessionId(parentSessionId), /* forceFlush= */ true)); + .setParentSessionId(parentSessionId), FORCE_FLUSH)); } void notifyViewAppeared(@NonNull String sessionId, @NonNull ViewStructureImpl node) { mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this, - new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED) - .setViewNode(node.mNode), /* forceFlush= */ false)); + new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED).setViewNode(node.mNode))); } void notifyViewDisappeared(@NonNull String sessionId, @NonNull AutofillId id) { mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this, - new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED).setAutofillId(id), - /* forceFlush= */ false)); + new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED).setAutofillId(id))); + } + + /** @hide */ + public void notifyViewsDisappeared(@NonNull String sessionId, + @NonNull ArrayList<AutofillId> ids) { + final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED); + if (ids.size() == 1) { + event.setAutofillId(ids.get(0)); + } else { + event.setAutofillIds(ids); + } + + mHandler.sendMessage( + obtainMessage(MainContentCaptureSession::handleSendEvent, this, event)); } void notifyViewTextChanged(@NonNull String sessionId, @NonNull AutofillId id, @Nullable CharSequence text) { mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this, new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED).setAutofillId(id) - .setText(text), /* forceFlush= */ false)); + .setText(text))); + } + + void notifyInitialViewHierarchyEvent(@NonNull String sessionId, boolean started) { + if (started) { + mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this, + new ContentCaptureEvent(sessionId, TYPE_INITIAL_VIEW_TREE_APPEARING))); + } else { + mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this, + new ContentCaptureEvent(sessionId, TYPE_INITIAL_VIEW_TREE_APPEARED), + FORCE_FLUSH)); + + } } @Override diff --git a/core/java/android/view/contentcapture/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java index 0cabafa21b17..eef841da3bc5 100644 --- a/core/java/android/view/contentcapture/ViewNode.java +++ b/core/java/android/view/contentcapture/ViewNode.java @@ -18,6 +18,7 @@ package android.view.contentcapture; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.app.assist.AssistStructure; import android.graphics.Matrix; import android.graphics.Rect; @@ -42,6 +43,7 @@ import com.android.internal.util.Preconditions; // instead /** @hide */ @SystemApi +@TestApi public final class ViewNode extends AssistStructure.ViewNode { private static final String TAG = ViewNode.class.getSimpleName(); diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 7fee3ef29a09..18f757c07369 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -16,6 +16,7 @@ package android.view.inputmethod; +import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.Manifest.permission.WRITE_SECURE_SETTINGS; import android.annotation.DrawableRes; @@ -26,6 +27,7 @@ import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; +import android.annotation.UserIdInt; import android.app.ActivityThread; import android.content.ComponentName; import android.content.ContentResolver; @@ -46,6 +48,7 @@ import android.os.ResultReceiver; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; import android.os.Trace; +import android.os.UserHandle; import android.provider.Settings; import android.text.style.SuggestionSpan; import android.util.Log; @@ -954,6 +957,13 @@ public final class InputMethodManager { return mIInputContext; } + /** + * Returns the list of installed input methods. + * + * <p>On multi user environment, this API returns a result for the calling process user.</p> + * + * @return {@link List} of {@link InputMethodInfo}. + */ public List<InputMethodInfo> getInputMethodList() { try { return mService.getInputMethodList(); @@ -962,9 +972,35 @@ public final class InputMethodManager { } } + /** + * Returns the list of enabled input methods. + * + * <p>On multi user environment, this API returns a result for the calling process user.</p> + * + * @return {@link List} of {@link InputMethodInfo}. + */ public List<InputMethodInfo> getEnabledInputMethodList() { try { - return mService.getEnabledInputMethodList(); + // We intentionally do not use UserHandle.getCallingUserId() here because for system + // services InputMethodManagerInternal.getEnabledInputMethodListAsUser() should be used + // instead. + return mService.getEnabledInputMethodList(UserHandle.myUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns the list of enabled input methods for the specified user. + * + * @param userId user ID to query + * @return {@link List} of {@link InputMethodInfo}. + * @hide + */ + @RequiresPermission(INTERACT_ACROSS_USERS_FULL) + public List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId) { + try { + return mService.getEnabledInputMethodList(userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -972,6 +1008,9 @@ public final class InputMethodManager { /** * Returns a list of enabled input method subtypes for the specified input method info. + * + * <p>On multi user environment, this API returns a result for the calling process user.</p> + * * @param imi An input method info whose subtypes list will be returned. * @param allowsImplicitlySelectedSubtypes A boolean flag to allow to return the implicitly * selected subtypes. If an input method info doesn't have enabled subtypes, the framework @@ -1887,6 +1926,36 @@ public final class InputMethodManager { } /** + * Call showSoftInput with currently focused view. + * @return {@code true} if IME can be shown. + * @hide + */ + public boolean requestImeShow(ResultReceiver resultReceiver) { + synchronized (mH) { + if (mServedView == null) { + return false; + } + showSoftInput(mServedView, 0 /* flags */, resultReceiver); + return true; + } + } + + /** + * Notify IME directly that it is no longer visible. + * @hide + */ + public void notifyImeHidden() { + synchronized (mH) { + try { + if (mCurMethod != null) { + mCurMethod.notifyImeHidden(); + } + } catch (RemoteException re) { + } + } + } + + /** * Report the current selection range. * * <p><strong>Editor authors</strong>, you need to call this method whenever diff --git a/core/java/android/view/inputmethod/InputMethodSession.java b/core/java/android/view/inputmethod/InputMethodSession.java index de15f332d51d..eb81628f9e27 100644 --- a/core/java/android/view/inputmethod/InputMethodSession.java +++ b/core/java/android/view/inputmethod/InputMethodSession.java @@ -184,4 +184,11 @@ public interface InputMethodSession { * insertion point and composition string. */ public void updateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo); + + /** + * Notifies {@link android.inputmethodservice.InputMethodService} that IME has been + * hidden from user. + * @hide + */ + public void notifyImeHidden(); } diff --git a/core/java/android/view/inspector/GeneratedInspectionCompanionProvider.java b/core/java/android/view/inspector/GeneratedInspectionCompanionProvider.java new file mode 100644 index 000000000000..8faae1f1da98 --- /dev/null +++ b/core/java/android/view/inspector/GeneratedInspectionCompanionProvider.java @@ -0,0 +1,47 @@ +/* + * 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.view.inspector; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +/** + * An inspection companion provider that loads pre-generated inspection companions + * + * @see android.processor.view.inspector.PlatformInspectableProcessor + */ +public class GeneratedInspectionCompanionProvider implements InspectionCompanionProvider { + /** + * The suffix used for the generated class + */ + private static final String COMPANION_SUFFIX = "$$InspectionCompanion"; + + @Override + @Nullable + @SuppressWarnings("unchecked") + public <T> InspectionCompanion<T> provide(@NonNull Class<T> cls) { + final String companionName = cls.getName() + COMPANION_SUFFIX; + + try { + final Class<InspectionCompanion<T>> companionClass = + (Class<InspectionCompanion<T>>) cls.getClassLoader().loadClass(companionName); + return companionClass.newInstance(); + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { + return null; + } + } +} diff --git a/core/java/android/view/inspector/InspectionCompanionProvider.java b/core/java/android/view/inspector/InspectionCompanionProvider.java new file mode 100644 index 000000000000..c08f49c611ee --- /dev/null +++ b/core/java/android/view/inspector/InspectionCompanionProvider.java @@ -0,0 +1,37 @@ +/* + * 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.view.inspector; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +/** + * An interface for services that can provide inspection companions for a class. + */ +public interface InspectionCompanionProvider { + /** + * Provide an {@link InspectionCompanion} for the supplied class. + * + * Implementing classes must not cache companion instances, and should instantiate a new one + * for each request. + * + * @param cls A {@link Class} representing the inspectable type + * @param <T> The type to find the companion for + * @return The inspection companion for the supplied type + */ + @Nullable + <T> InspectionCompanion<T> provide(@NonNull Class<T> cls); +} diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index c82177407b61..26185272097d 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -273,7 +273,7 @@ public class Editor { boolean mDiscardNextActionUp; boolean mIgnoreActionUpEvent; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769485) private long mShowCursor; private boolean mRenderCursorRegardlessTiming; private Blink mBlink; diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index c256d57a6af6..eef40e1f6e2e 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -341,10 +341,10 @@ public class Toast { // the proper ordering of these system-wide. // ======================================================================================= - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private static INotificationManager sService; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) static private INotificationManager getService() { if (sService != null) { return sService; diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java index cd80d53a7546..429c6187dfe0 100644 --- a/core/java/com/android/internal/policy/DecorContext.java +++ b/core/java/com/android/internal/policy/DecorContext.java @@ -22,6 +22,7 @@ import android.content.res.Resources; import android.view.ContextThemeWrapper; import android.view.WindowManager; import android.view.WindowManagerImpl; +import android.view.contentcapture.ContentCaptureManager; import java.lang.ref.WeakReference; @@ -36,6 +37,7 @@ class DecorContext extends ContextThemeWrapper { private PhoneWindow mPhoneWindow; private WindowManager mWindowManager; private Resources mActivityResources; + private ContentCaptureManager mContentCaptureManager; private WeakReference<Context> mActivityContext; @@ -60,6 +62,16 @@ class DecorContext extends ContextThemeWrapper { } return mWindowManager; } + if (Context.CONTENT_CAPTURE_MANAGER_SERVICE.equals(name)) { + if (mContentCaptureManager == null) { + Context activityContext = mActivityContext.get(); + if (activityContext != null) { + mContentCaptureManager = (ContentCaptureManager) activityContext + .getSystemService(name); + } + } + return mContentCaptureManager; + } return super.getSystemService(name); } @@ -79,4 +91,9 @@ class DecorContext extends ContextThemeWrapper { public AssetManager getAssets() { return mActivityResources.getAssets(); } + + @Override + public boolean isContentCaptureSupported() { + return true; + } } diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index 9df37ada7701..bfb50848df26 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -69,8 +69,9 @@ interface IStatusBarService void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded, in int notificationLocation); void onNotificationDirectReplied(String key); void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount, int smartActionCount, - boolean generatedByAsssistant); - void onNotificationSmartReplySent(in String key, in int replyIndex, in CharSequence reply, boolean generatedByAssistant, in int notificationLocation); + boolean generatedByAsssistant, boolean editBeforeSending); + void onNotificationSmartReplySent(in String key, in int replyIndex, in CharSequence reply, + in int notificationLocation, boolean modifiedBeforeSending); void onNotificationSettingsViewed(String key); void setSystemUiVisibility(int displayId, int vis, int mask, String cause); diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java index f91b837410ba..78fdfe422712 100644 --- a/core/java/com/android/internal/util/CollectionUtils.java +++ b/core/java/com/android/internal/util/CollectionUtils.java @@ -335,6 +335,13 @@ public class CollectionUtils { } /** + * @return the first element if not empty/null, null otherwise + */ + public static @Nullable <T> T firstOrNull(@Nullable Collection<T> cur) { + return isEmpty(cur) ? null : cur.iterator().next(); + } + + /** * @return list of single given element if it's not null, empty list otherwise */ public static @NonNull <T> List<T> singletonOrEmpty(@Nullable T item) { diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 0752efe562a9..98e854cc40a2 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -36,7 +36,7 @@ interface IInputMethodManager { // TODO: Use ParceledListSlice instead List<InputMethodInfo> getInputMethodList(); // TODO: Use ParceledListSlice instead - List<InputMethodInfo> getEnabledInputMethodList(); + List<InputMethodInfo> getEnabledInputMethodList(int userId); List<InputMethodSubtype> getEnabledInputMethodSubtypeList(in String imiId, boolean allowsImplicitlySelectedSubtypes); InputMethodSubtype getLastInputMethodSubtype(); diff --git a/core/java/com/android/internal/view/IInputMethodSession.aidl b/core/java/com/android/internal/view/IInputMethodSession.aidl index 794238a3826e..664643cc9b4d 100644 --- a/core/java/com/android/internal/view/IInputMethodSession.aidl +++ b/core/java/com/android/internal/view/IInputMethodSession.aidl @@ -48,4 +48,6 @@ oneway interface IInputMethodSession { void finishSession(); void updateCursorAnchorInfo(in CursorAnchorInfo cursorAnchorInfo); + + void notifyImeHidden(); } diff --git a/core/java/com/android/server/backup/PermissionBackupHelper.java b/core/java/com/android/server/backup/PermissionBackupHelper.java index ff0e63da8289..c0ba18159639 100644 --- a/core/java/com/android/server/backup/PermissionBackupHelper.java +++ b/core/java/com/android/server/backup/PermissionBackupHelper.java @@ -19,7 +19,6 @@ package com.android.server.backup; import android.app.AppGlobals; import android.app.backup.BlobBackupHelper; import android.content.pm.IPackageManager; -import android.os.UserHandle; import android.util.Slog; public class PermissionBackupHelper extends BlobBackupHelper { @@ -32,8 +31,12 @@ public class PermissionBackupHelper extends BlobBackupHelper { // key under which the permission-grant state blob is committed to backup private static final String KEY_PERMISSIONS = "permissions"; - public PermissionBackupHelper() { + private final int mUserId; + + public PermissionBackupHelper(int userId) { super(STATE_VERSION, KEY_PERMISSIONS); + + mUserId = userId; } @Override @@ -45,7 +48,7 @@ public class PermissionBackupHelper extends BlobBackupHelper { try { switch (key) { case KEY_PERMISSIONS: - return pm.getPermissionGrantBackup(UserHandle.USER_SYSTEM); + return pm.getPermissionGrantBackup(mUserId); default: Slog.w(TAG, "Unexpected backup key " + key); @@ -65,7 +68,7 @@ public class PermissionBackupHelper extends BlobBackupHelper { try { switch (key) { case KEY_PERMISSIONS: - pm.restorePermissionGrants(payload, UserHandle.USER_SYSTEM); + pm.restorePermissionGrants(payload, mUserId); break; default: diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java index 47e7a0e74563..8878421a8016 100644 --- a/core/java/com/android/server/backup/SystemBackupAgent.java +++ b/core/java/com/android/server/backup/SystemBackupAgent.java @@ -19,7 +19,7 @@ package com.android.server.backup; import android.app.IWallpaperManager; import android.app.backup.BackupAgentHelper; import android.app.backup.BackupDataInput; -import android.app.backup.BackupDataOutput; +import android.app.backup.BackupHelper; import android.app.backup.FullBackup; import android.app.backup.FullBackupDataOutput; import android.app.backup.WallpaperBackupHelper; @@ -31,8 +31,11 @@ import android.os.ServiceManager; import android.os.UserHandle; import android.util.Slog; +import com.google.android.collect.Sets; + import java.io.File; import java.io.IOException; +import java.util.Set; /** * Backup agent for various system-managed data. Wallpapers are now handled by a @@ -77,20 +80,25 @@ public class SystemBackupAgent extends BackupAgentHelper { // Use old keys to keep legacy data compatibility and avoid writing two wallpapers private static final String WALLPAPER_IMAGE_KEY = WallpaperBackupHelper.WALLPAPER_IMAGE_KEY; - private WallpaperBackupHelper mWallpaperHelper = null; + private static final Set<String> sEligibleForMultiUser = Sets.newArraySet( + PERMISSION_HELPER); + + private int mUserId = UserHandle.USER_SYSTEM; @Override - public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, - ParcelFileDescriptor newState) throws IOException { + public void onCreate(UserHandle user) { + super.onCreate(user); + + mUserId = user.getIdentifier(); + addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this)); addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper()); addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this)); - addHelper(PERMISSION_HELPER, new PermissionBackupHelper()); + addHelper(PERMISSION_HELPER, new PermissionBackupHelper(mUserId)); addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this)); addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper()); addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper()); addHelper(SLICES_HELPER, new SliceBackupHelper(this)); - super.onBackup(oldState, data, newState); } @Override @@ -103,26 +111,25 @@ public class SystemBackupAgent extends BackupAgentHelper { throws IOException { // Slot in a restore helper for the older wallpaper backup schema to support restore // from devices still generating data in that format. - mWallpaperHelper = new WallpaperBackupHelper(this, - new String[] { WALLPAPER_IMAGE_KEY} ); - addHelper(WALLPAPER_HELPER, mWallpaperHelper); + addHelper(WALLPAPER_HELPER, new WallpaperBackupHelper(this, + new String[] { WALLPAPER_IMAGE_KEY})); // On restore, we also support a long-ago wallpaper data schema "system_files" addHelper("system_files", new WallpaperBackupHelper(this, new String[] { WALLPAPER_IMAGE_KEY} )); - addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this)); - addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper()); - addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this)); - addHelper(PERMISSION_HELPER, new PermissionBackupHelper()); - addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this)); - addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper()); - addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper()); - addHelper(SLICES_HELPER, new SliceBackupHelper(this)); - super.onRestore(data, appVersionCode, newState); } + @Override + public void addHelper(String keyPrefix, BackupHelper helper) { + if (mUserId != UserHandle.USER_SYSTEM && !sEligibleForMultiUser.contains(keyPrefix)) { + return; + } + + super.addHelper(keyPrefix, helper); + } + /** * Support for 'adb restore' of legacy archives */ diff --git a/core/jni/Android.bp b/core/jni/Android.bp index e7a1c49e7a99..345058bd4f6f 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -276,6 +276,7 @@ cc_library_shared { "libmediametrics", "libmeminfo", "libaudioclient", + "libaudiopolicy", "libjpeg", "libusbhost", "libharfbuzz_ng", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 49598bbb5ada..d69d416e8401 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -230,6 +230,11 @@ extern int register_com_android_internal_os_Zygote(JNIEnv *env); extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env); extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env); +// Namespace for Android Runtime flags applied during boot time. +static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot"; +// Feature flag name for Garbage Collector type. +static const char* GCTYPE = "gctype"; + static AndroidRuntime* gCurRuntime = NULL; /* @@ -776,7 +781,9 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote) } std::string gc_type_override = - server_configurable_flags::GetServerConfigurableFlag("runtime_native", "gctype", ""); + server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE, + GCTYPE, + /*default_value=*/ ""); std::string gc_type_override_temp; if (gc_type_override.empty()) { parseRuntimeOption("dalvik.vm.gctype", gctypeOptsBuf, "-Xgc:"); diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk index 6a8105088b88..40ebd44b9bd0 100644 --- a/core/tests/coretests/Android.mk +++ b/core/tests/coretests/Android.mk @@ -36,12 +36,11 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ frameworks-core-util-lib \ mockwebserver \ guava \ - androidx.test.runner \ + androidx.test.espresso.core \ androidx.test.ext.junit \ + androidx.test.runner \ androidx.test.rules \ - androidx.test.espresso.core \ mockito-target-minus-junit4 \ - espresso-core \ ub-uiautomator \ platform-test-annotations \ truth-prebuilt \ diff --git a/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java b/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java index be1d44cec6a6..c71bc7395d4e 100644 --- a/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java +++ b/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java @@ -16,22 +16,28 @@ package android.animation; -import android.test.ActivityInstrumentationTestCase2; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import androidx.test.filters.LargeTest; +import androidx.test.rule.ActivityTestRule; import com.android.frameworks.coretests.R; +import org.junit.Rule; +import org.junit.Test; + import java.util.HashSet; import java.util.Set; @LargeTest -public class AnimatorInflaterTest extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> { - Set<Integer> identityHashes = new HashSet<Integer>(); +public class AnimatorInflaterTest { + @Rule + public final ActivityTestRule<BasicAnimatorActivity> mActivityRule = + new ActivityTestRule<>(BasicAnimatorActivity.class); - public AnimatorInflaterTest() { - super(BasicAnimatorActivity.class); - } + private final Set<Integer> mIdentityHashes = new HashSet<>(); private void assertUnique(Object object) { assertUnique(object, ""); @@ -39,15 +45,16 @@ public class AnimatorInflaterTest extends ActivityInstrumentationTestCase2<Basic private void assertUnique(Object object, String msg) { final int code = System.identityHashCode(object); - assertTrue("object should be unique " + msg + ", obj:" + object, identityHashes.add(code)); - + assertTrue("object should be unique " + msg + ", obj:" + object, mIdentityHashes.add(code)); } + @Test public void testLoadStateListAnimator() { - StateListAnimator sla1 = AnimatorInflater.loadStateListAnimator(getActivity(), + final BasicAnimatorActivity activity = mActivityRule.getActivity(); + StateListAnimator sla1 = AnimatorInflater.loadStateListAnimator(activity, R.anim.test_state_anim); - sla1.setTarget(getActivity().mAnimatingButton); - StateListAnimator sla2 = AnimatorInflater.loadStateListAnimator(getActivity(), + sla1.setTarget(activity.mAnimatingButton); + StateListAnimator sla2 = AnimatorInflater.loadStateListAnimator(activity, R.anim.test_state_anim); assertNull(sla2.getTarget()); for (StateListAnimator sla : new StateListAnimator[]{sla1, sla2}) { diff --git a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java index 55837ba3926c..7a1de0c5d4fe 100644 --- a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java +++ b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java @@ -16,27 +16,38 @@ package android.animation; -import android.test.ActivityInstrumentationTestCase2; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + import android.view.View; import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; +import androidx.test.rule.ActivityTestRule; import com.android.frameworks.coretests.R; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + import java.util.ArrayList; -public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<AnimatorSetActivity> { +@SmallTest +public class AnimatorSetActivityTest { + + @Rule + public final ActivityTestRule<AnimatorSetActivity> mActivityRule = + new ActivityTestRule<>(AnimatorSetActivity.class); private static final long POLL_INTERVAL = 100; // ms private AnimatorSetActivity mActivity; private ObjectAnimator a1,a2,a3; private ValueAnimator a4,a5; - public AnimatorSetActivityTest() { - super(AnimatorSetActivity.class); - } - static class MyListener implements Animator.AnimatorListener { boolean startIsCalled = false; boolean endIsCalled = false; @@ -63,10 +74,9 @@ public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<An } } - @Override + @Before public void setUp() throws Exception { - super.setUp(); - mActivity = getActivity(); + mActivity = mActivityRule.getActivity(); View square1 = mActivity.findViewById(R.id.square1); View square2 = mActivity.findViewById(R.id.square2); @@ -78,7 +88,7 @@ public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<An a5 = ValueAnimator.ofFloat(10f, 5f).setDuration(850); } - @Override + @After public void tearDown() throws Exception { mActivity = null; a1 = null; @@ -86,10 +96,9 @@ public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<An a3 = null; a4 = null; a5 = null; - super.tearDown(); } - @SmallTest + @Test public void testGetChildAnimations() { AnimatorSet s1 = new AnimatorSet(); s1.playTogether(a1, a2, a3); @@ -129,7 +138,7 @@ public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<An } } - @SmallTest + @Test public void testTotalDuration() { ArrayList<Animator> list = getAnimatorList(); @@ -192,7 +201,7 @@ public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<An } - @SmallTest + @Test public void testGetDuration() { AnimatorSet s = new AnimatorSet(); assertTrue(s.getDuration() < 0); @@ -205,8 +214,8 @@ public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<An } - @SmallTest @UiThreadTest + @Test public void testSetDuration() { AnimatorSet s = getSequentialSet(); assertTrue(s.getDuration() < 0); @@ -224,7 +233,7 @@ public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<An assertEquals(duration, a5.getDuration()); } - @SmallTest + @Test public void testAddListener() throws InterruptedException { // Verify that the listener is added to the list of listeners in the AnimatorSet // and that newly added listener gets callback for lifecycle events of the animator @@ -241,13 +250,10 @@ public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<An assertFalse(listener.endIsCalled); try { - runTestOnUiThread(new Runnable() { - @Override - public void run() { - s.start(); - assertTrue(listener.startIsCalled); - assertFalse(listener.endIsCalled); - } + mActivityRule.runOnUiThread(() -> { + s.start(); + assertTrue(listener.startIsCalled); + assertFalse(listener.endIsCalled); }); } catch (Throwable throwable) { throwable.printStackTrace(); @@ -258,18 +264,13 @@ public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<An assertTrue(listener.endIsCalled); } - @SmallTest + @Test public void testRemoveListener() throws Throwable { final AnimatorSet s = new AnimatorSet(); s.playTogether(a1, a2, a3, a4); MyListener listener = new MyListener(); s.addListener(listener); - runTestOnUiThread(new Runnable() { - @Override - public void run() { - s.start(); - } - }); + mActivityRule.runOnUiThread(s::start); Thread.sleep(s.getTotalDuration() + 100); assertTrue(listener.startIsCalled); @@ -282,18 +283,13 @@ public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<An listener.startIsCalled = false; listener.endIsCalled = false; - runTestOnUiThread(new Runnable() { - @Override - public void run() { - s.start(); - } - }); + mActivityRule.runOnUiThread(s::start); Thread.sleep(s.getTotalDuration() + 100); assertFalse(listener.startIsCalled); assertFalse(listener.endIsCalled); } - @SmallTest + @Test public void testEnd() throws Throwable { // End animator set final AnimatorSet s = new AnimatorSet(); @@ -301,37 +297,30 @@ public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<An final MyListener listener = new MyListener(); s.addListener(listener); assertFalse(listener.endIsCalled); - runTestOnUiThread(new Runnable() { - @Override - public void run() { - s.start(); - assertTrue(s.isStarted()); - assertTrue(listener.startIsCalled); - assertFalse(listener.endIsCalled); - } + mActivityRule.runOnUiThread(() -> { + s.start(); + assertTrue(s.isStarted()); + assertTrue(listener.startIsCalled); + assertFalse(listener.endIsCalled); }); Thread.sleep(a2.getTotalDuration()); - runTestOnUiThread(new Runnable() { - @Override - public void run() { - s.end(); - assertTrue(listener.startIsCalled); - assertTrue(listener.endIsCalled); - assertFalse(s.isRunning()); - assertFalse(s.isStarted()); - - assertFalse(a1.isStarted()); - assertFalse(a2.isStarted()); - assertFalse(a3.isStarted()); - assertFalse(a4.isStarted()); - } + mActivityRule.runOnUiThread(() -> { + s.end(); + assertTrue(listener.startIsCalled); + assertTrue(listener.endIsCalled); + assertFalse(s.isRunning()); + assertFalse(s.isStarted()); + + assertFalse(a1.isStarted()); + assertFalse(a2.isStarted()); + assertFalse(a3.isStarted()); + assertFalse(a4.isStarted()); }); - } - @SmallTest + @Test public void testStart() throws Throwable { final AnimatorSet s = new AnimatorSet(); ArrayList<Animator> animators = getAnimatorList(); @@ -355,12 +344,9 @@ public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<An assertFalse(l.endIsCalled); } - runTestOnUiThread(new Runnable() { - @Override - public void run() { - s.start(); - assertTrue(l.startIsCalled); - } + mActivityRule.runOnUiThread(() -> { + s.start(); + assertTrue(l.startIsCalled); }); long timeout = s.getTotalDuration() * 2; @@ -383,7 +369,7 @@ public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<An } } - @SmallTest + @Test public void testCancel() throws Throwable { // Check whether cancel would trigger onAnimationCanceled and cancel all the unfinished // animations @@ -411,42 +397,33 @@ public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<An assertFalse(l.endIsCalled); } - runTestOnUiThread(new Runnable() { - @Override - public void run() { - s.start(); - } - }); + mActivityRule.runOnUiThread(s::start); Thread.sleep(a1.getTotalDuration()); - runTestOnUiThread(new Runnable() { - @Override - public void run() { - assertTrue(s.isStarted()); - ArrayList<Integer> runningAnimIds = new ArrayList<Integer>(); - for (int i = 0; i < animators.size(); i++) { - if (animators.get(i).isStarted()) { - runningAnimIds.add(i); - } + mActivityRule.runOnUiThread(() -> { + assertTrue(s.isStarted()); + ArrayList<Integer> runningAnimIds = new ArrayList<>(); + for (int i = 0; i < animators.size(); i++) { + if (animators.get(i).isStarted()) { + runningAnimIds.add(i); } - s.cancel(); - assertTrue(l.startIsCalled); - assertTrue(l.cancelIsCalled); - assertTrue(l.endIsCalled); - - for (int i = 0; i < listeners.size(); i++) { - assertTrue(listeners.get(i).startIsCalled); - if (runningAnimIds.contains(i)) { - assertTrue(listeners.get(i).cancelIsCalled); - } - assertTrue(listeners.get(i).endIsCalled); + } + s.cancel(); + assertTrue(l.startIsCalled); + assertTrue(l.cancelIsCalled); + assertTrue(l.endIsCalled); + + for (int i = 0; i < listeners.size(); i++) { + assertTrue(listeners.get(i).startIsCalled); + if (runningAnimIds.contains(i)) { + assertTrue(listeners.get(i).cancelIsCalled); } + assertTrue(listeners.get(i).endIsCalled); } }); - } - @SmallTest + @Test public void testIsRunning() throws Throwable { final AnimatorSet s = new AnimatorSet(); final long startDelay = 500; @@ -455,12 +432,7 @@ public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<An s.setStartDelay(startDelay); MyListener listener = new MyListener(); s.addListener(listener); - runTestOnUiThread(new Runnable() { - @Override - public void run() { - s.start(); - } - }); + mActivityRule.runOnUiThread(s::start); while (!listener.endIsCalled) { boolean passedStartDelay = a1.isStarted() || a2.isStarted() || a3.isStarted() || @@ -471,35 +443,29 @@ public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<An assertFalse(s.isRunning()); } - @SmallTest + @Test public void testPauseAndResume() throws Throwable { final AnimatorSet set = getSequentialSet(); - runTestOnUiThread(new Runnable() { - @Override - public void run() { - // Calling pause before start should have no effect, per documentation - set.pause(); - set.start(); - assertFalse(set.isPaused()); - } + mActivityRule.runOnUiThread(() -> { + // Calling pause before start should have no effect, per documentation + set.pause(); + set.start(); + assertFalse(set.isPaused()); }); while (!a2.isStarted()) { Thread.sleep(50); } - runTestOnUiThread(new Runnable() { - @Override - public void run() { - assertFalse(set.isPaused()); - set.pause(); - assertTrue(set.isPaused()); - set.resume(); - assertFalse(set.isPaused()); - } + mActivityRule.runOnUiThread(() -> { + assertFalse(set.isPaused()); + set.pause(); + assertTrue(set.isPaused()); + set.resume(); + assertFalse(set.isPaused()); }); } - @SmallTest + @Test public void testClone() throws Throwable { // Set up an AnimatorSet and two clones, add one listener to each. When the clones animate, // listeners of both the clone and the animator being cloned should receive animation @@ -535,14 +501,11 @@ public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<An // Start the animation, and make the first clone during its run and the second clone once // it ends. - runTestOnUiThread(new Runnable() { - @Override - public void run() { - assertFalse(l1.startIsCalled); - assertFalse(l1.endIsCalled); + mActivityRule.runOnUiThread(() -> { + assertFalse(l1.startIsCalled); + assertFalse(l1.endIsCalled); - s1.start(); - } + s1.start(); }); // Make the first clone, during the animation's run. @@ -552,20 +515,12 @@ public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<An s2.addListener(l2); Thread.sleep(POLL_INTERVAL); - runTestOnUiThread(new Runnable() { - @Override - public void run() { - s1.end(); - } - }); + mActivityRule.runOnUiThread(s1::end); Thread.sleep(POLL_INTERVAL); - runTestOnUiThread(new Runnable() { - @Override - public void run() { - assertTrue(l1.startIsCalled); - assertTrue(l1.endIsCalled); - } + mActivityRule.runOnUiThread(() -> { + assertTrue(l1.startIsCalled); + assertTrue(l1.endIsCalled); }); Thread.sleep(POLL_INTERVAL); @@ -574,59 +529,49 @@ public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<An final MyListener l3 = new MyListener(); s3.addListener(l3); - runTestOnUiThread(new Runnable() { - @Override - public void run() { - // Checking the fields before animations start. - assertFalse(l2.startIsCalled); - assertFalse(l2.cancelIsCalled); - assertFalse(l2.endIsCalled); - assertFalse(l3.startIsCalled); - assertFalse(l3.cancelIsCalled); - assertFalse(l3.endIsCalled); - - s2.start(); - s3.start(); - } + mActivityRule.runOnUiThread(() -> { + // Checking the fields before animations start. + assertFalse(l2.startIsCalled); + assertFalse(l2.cancelIsCalled); + assertFalse(l2.endIsCalled); + assertFalse(l3.startIsCalled); + assertFalse(l3.cancelIsCalled); + assertFalse(l3.endIsCalled); + + s2.start(); + s3.start(); }); Thread.sleep(POLL_INTERVAL); - runTestOnUiThread(new Runnable() { - @Override - public void run() { - // Make sure the listeners receive the callbacks - // At this time only onAnimationStart() should be called. - assertTrue(l2.startIsCalled); - assertTrue(l3.startIsCalled); - assertFalse(l2.endIsCalled); - assertFalse(l3.endIsCalled); - assertFalse(l2.cancelIsCalled); - assertFalse(l3.cancelIsCalled); - - s2.end(); - s3.cancel(); - } + mActivityRule.runOnUiThread(() -> { + // Make sure the listeners receive the callbacks + // At this time only onAnimationStart() should be called. + assertTrue(l2.startIsCalled); + assertTrue(l3.startIsCalled); + assertFalse(l2.endIsCalled); + assertFalse(l3.endIsCalled); + assertFalse(l2.cancelIsCalled); + assertFalse(l3.cancelIsCalled); + + s2.end(); + s3.cancel(); }); Thread.sleep(POLL_INTERVAL); - runTestOnUiThread(new Runnable() { - @Override - public void run() { - // Check that the new listeners for the new animations gets called for the events. - assertTrue(l2.startIsCalled); - assertFalse(l2.cancelIsCalled); - assertTrue(l2.endIsCalled); - assertTrue(l3.startIsCalled); - assertTrue(l3.cancelIsCalled); - assertTrue(l3.endIsCalled); - - // Check that the listener on the animation that was being clone receive the - // animation lifecycle events for the clones. - assertTrue(onlyContains(startedAnimators, s1, s2, s3)); - assertTrue(onlyContains(canceledAnimators, s3)); - assertTrue(onlyContains(endedAnimators, s1, s2, s3)); - } + mActivityRule.runOnUiThread(() -> { + // Check that the new listeners for the new animations gets called for the events. + assertTrue(l2.startIsCalled); + assertFalse(l2.cancelIsCalled); + assertTrue(l2.endIsCalled); + assertTrue(l3.startIsCalled); + assertTrue(l3.cancelIsCalled); + assertTrue(l3.endIsCalled); + + // Check that the listener on the animation that was being clone receive the + // animation lifecycle events for the clones. + assertTrue(onlyContains(startedAnimators, s1, s2, s3)); + assertTrue(onlyContains(canceledAnimators, s3)); + assertTrue(onlyContains(endedAnimators, s1, s2, s3)); }); - } /** @@ -663,5 +608,4 @@ public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<An list.add(a5); return list; } - } diff --git a/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java b/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java index 4e90d1acf4a0..94c90aa531da 100644 --- a/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java +++ b/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java @@ -23,6 +23,8 @@ import androidx.test.filters.MediumTest; import com.android.frameworks.coretests.R; +import org.junit.Test; + import java.util.concurrent.TimeUnit; /** @@ -36,7 +38,7 @@ public class AnimatorSetEventsTest extends EventsTest { @Override public void setUp() throws Exception { - button = (Button) getActivity().findViewById(R.id.animatingButton); + button = mActivityRule.getActivity().findViewById(R.id.animatingButton); mAnimator = new AnimatorSet(); ((AnimatorSet)mAnimator).playSequentially(xAnim, yAnim); super.setUp(); @@ -53,23 +55,21 @@ public class AnimatorSetEventsTest extends EventsTest { * its children */ @MediumTest - public void testPlayingCancelDuringChildDelay() throws Exception { + @Test + public void testPlayingCancelDuringChildDelay() throws Throwable { yAnim.setStartDelay(500); final AnimatorSet animSet = new AnimatorSet(); animSet.playSequentially(xAnim, yAnim); mFutureListener = new FutureReleaseListener(mFuture); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - Handler handler = new Handler(); - animSet.addListener(mFutureListener); - mRunning = true; - animSet.start(); - handler.postDelayed(new Canceler(animSet, mFuture), ANIM_DURATION + 250); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + Handler handler = new Handler(); + animSet.addListener(mFutureListener); + mRunning = true; + animSet.start(); + handler.postDelayed(new Canceler(animSet, mFuture), ANIM_DURATION + 250); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout(), TimeUnit.MILLISECONDS); diff --git a/core/tests/coretests/src/android/animation/AutoCancelTest.java b/core/tests/coretests/src/android/animation/AutoCancelTest.java index b3ec92cf1f0f..7df7336eb0b3 100644 --- a/core/tests/coretests/src/android/animation/AutoCancelTest.java +++ b/core/tests/coretests/src/android/animation/AutoCancelTest.java @@ -16,15 +16,24 @@ package android.animation; +import static org.junit.Assert.assertTrue; + import android.os.Handler; -import android.test.ActivityInstrumentationTestCase2; import androidx.test.filters.SmallTest; +import androidx.test.rule.ActivityTestRule; + +import org.junit.Rule; +import org.junit.Test; import java.util.HashMap; import java.util.concurrent.TimeUnit; -public class AutoCancelTest extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> { +public class AutoCancelTest { + + @Rule + public final ActivityTestRule<BasicAnimatorActivity> mActivityRule = + new ActivityTestRule<>(BasicAnimatorActivity.class); boolean mAnimX1Canceled = false; boolean mAnimXY1Canceled = false; @@ -37,10 +46,6 @@ public class AutoCancelTest extends ActivityInstrumentationTestCase2<BasicAnimat HashMap<Animator, Boolean> mCanceledMap = new HashMap<Animator, Boolean>(); - public AutoCancelTest() { - super(BasicAnimatorActivity.class); - } - ObjectAnimator setupAnimator(long startDelay, String... properties) { ObjectAnimator returnVal; if (properties.length == 1) { @@ -58,8 +63,7 @@ public class AutoCancelTest extends ActivityInstrumentationTestCase2<BasicAnimat return returnVal; } - private void setupAnimators(long startDelay, boolean startLater, final FutureWaiter future) - throws Exception { + private void setupAnimators(long startDelay, boolean startLater, final FutureWaiter future) { // Animators to be auto-canceled final ObjectAnimator animX1 = setupAnimator(startDelay, "x"); final ObjectAnimator animY1 = setupAnimator(startDelay, "y"); @@ -123,64 +127,56 @@ public class AutoCancelTest extends ActivityInstrumentationTestCase2<BasicAnimat } @SmallTest - public void testAutoCancel() throws Exception { + @Test + public void testAutoCancel() throws Throwable { final FutureWaiter future = new FutureWaiter(); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - setupAnimators(0, false, future); - } catch (Exception e) { - future.setException(e); - } + mActivityRule.runOnUiThread(() -> { + try { + setupAnimators(0, false, future); + } catch (Exception e) { + future.setException(e); } }); assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS)); } @SmallTest - public void testAutoCancelDelayed() throws Exception { + @Test + public void testAutoCancelDelayed() throws Throwable { final FutureWaiter future = new FutureWaiter(); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - setupAnimators(START_DELAY, false, future); - } catch (Exception e) { - future.setException(e); - } + mActivityRule.runOnUiThread(() -> { + try { + setupAnimators(START_DELAY, false, future); + } catch (Exception e) { + future.setException(e); } }); assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS)); } @SmallTest - public void testAutoCancelTestLater() throws Exception { + @Test + public void testAutoCancelTestLater() throws Throwable { final FutureWaiter future = new FutureWaiter(); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - setupAnimators(0, true, future); - } catch (Exception e) { - future.setException(e); - } + mActivityRule.runOnUiThread(() -> { + try { + setupAnimators(0, true, future); + } catch (Exception e) { + future.setException(e); } }); assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS)); } @SmallTest - public void testAutoCancelDelayedTestLater() throws Exception { + @Test + public void testAutoCancelDelayedTestLater() throws Throwable { final FutureWaiter future = new FutureWaiter(); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - setupAnimators(START_DELAY, true, future); - } catch (Exception e) { - future.setException(e); - } + mActivityRule.runOnUiThread(() -> { + try { + setupAnimators(START_DELAY, true, future); + } catch (Exception e) { + future.setException(e); } }); assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS)); diff --git a/core/tests/coretests/src/android/animation/EventsTest.java b/core/tests/coretests/src/android/animation/EventsTest.java index ba7413a2a4f7..0c40a95f8bab 100644 --- a/core/tests/coretests/src/android/animation/EventsTest.java +++ b/core/tests/coretests/src/android/animation/EventsTest.java @@ -16,12 +16,20 @@ package android.animation; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.annotation.CallSuper; import android.os.Handler; -import android.test.ActivityInstrumentationTestCase2; import androidx.test.annotation.UiThreadTest; import androidx.test.filters.MediumTest; import androidx.test.filters.SmallTest; +import androidx.test.rule.ActivityTestRule; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -38,8 +46,11 @@ import java.util.concurrent.TimeoutException; * wait for some later event to occur before ending. These tests use a combination of an * AbstractFuture mechanism and a delayed action to release that Future later. */ -public abstract class EventsTest - extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> { +public abstract class EventsTest { + + @Rule + public final ActivityTestRule<BasicAnimatorActivity> mActivityRule = + new ActivityTestRule<>(BasicAnimatorActivity.class); protected static final int ANIM_DURATION = 400; protected static final int ANIM_DELAY = 100; @@ -55,7 +66,6 @@ public abstract class EventsTest private boolean mCanceled; // tracks whether we've canceled the animator protected Animator.AnimatorListener mFutureListener; // mechanism for delaying end of the test protected FutureWaiter mFuture; // Mechanism for waiting for the UI test to complete - private Animator.AnimatorListener mListener; // Listener that handles/tests the events protected Animator mAnimator; // The animator used in the tests. Must be set in subclass // setup() method prior to calling the superclass setup() @@ -67,10 +77,12 @@ public abstract class EventsTest protected static class Canceler implements Runnable { Animator mAnim; FutureWaiter mFuture; + public Canceler(Animator anim, FutureWaiter future) { mAnim = anim; mFuture = future; } + @Override public void run() { try { @@ -79,7 +91,7 @@ public abstract class EventsTest mFuture.setException(new RuntimeException(e)); } } - }; + } /** * Timeout length, based on when the animation should reasonably be complete. @@ -95,10 +107,12 @@ public abstract class EventsTest static class Ender implements Runnable { Animator mAnim; FutureWaiter mFuture; + public Ender(Animator anim, FutureWaiter future) { mAnim = anim; mFuture = future; } + @Override public void run() { try { @@ -107,7 +121,7 @@ public abstract class EventsTest mFuture.setException(new RuntimeException(e)); } } - }; + } /** * Pauses the given animator. Used to delay pausing until some later time (after the @@ -116,10 +130,12 @@ public abstract class EventsTest static class Pauser implements Runnable { Animator mAnim; FutureWaiter mFuture; + public Pauser(Animator anim, FutureWaiter future) { mAnim = anim; mFuture = future; } + @Override public void run() { try { @@ -128,7 +144,7 @@ public abstract class EventsTest mFuture.setException(new RuntimeException(e)); } } - }; + } /** * Resumes the given animator. Used to delay resuming until some later time (after the @@ -137,10 +153,12 @@ public abstract class EventsTest static class Resumer implements Runnable { Animator mAnim; FutureWaiter mFuture; + public Resumer(Animator anim, FutureWaiter future) { mAnim = anim; mFuture = future; } + @Override public void run() { try { @@ -149,7 +167,7 @@ public abstract class EventsTest mFuture.setException(new RuntimeException(e)); } } - }; + } /** * Releases the given Future object when the listener's end() event is called. Specifically, @@ -171,28 +189,14 @@ public abstract class EventsTest public FutureReleaseListener(FutureWaiter future, long timeout) { mFuture = future; Handler handler = new Handler(); - handler.postDelayed(new Runnable() { - @Override - public void run() { - mFuture.release(); - } - }, timeout); + handler.postDelayed(mFuture::release, timeout); } @Override public void onAnimationEnd(Animator animation) { Handler handler = new Handler(); - handler.postDelayed(new Runnable() { - @Override - public void run() { - mFuture.release(); - } - }, FUTURE_RELEASE_DELAY); + handler.postDelayed(mFuture::release, FUTURE_RELEASE_DELAY); } - }; - - public EventsTest() { - super(BasicAnimatorActivity.class); } /** @@ -201,13 +205,12 @@ public abstract class EventsTest * and then call super.setup(), where further properties are set on that animator. * @throws Exception */ - @Override + @CallSuper + @Before public void setUp() throws Exception { - super.setUp(); - // mListener is the main testing mechanism of this file. The asserts of each test // are embedded in the listener callbacks that it implements. - mListener = new AnimatorListenerAdapter() { + final Animator.AnimatorListener listener = new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { // This should only be called on an animation that has not yet been started @@ -236,9 +239,8 @@ public abstract class EventsTest } }; - mAnimator.addListener(mListener); + mAnimator.addListener(listener); mAnimator.setDuration(ANIM_DURATION); - mFuture = new FutureWaiter(); mRunning = false; @@ -251,6 +253,7 @@ public abstract class EventsTest */ @UiThreadTest @SmallTest + @Test public void testCancel() throws Exception { mAnimator.cancel(); } @@ -260,6 +263,7 @@ public abstract class EventsTest */ @UiThreadTest @SmallTest + @Test public void testEnd() throws Exception { mRunning = true; // end() implicitly starts an unstarted animator mAnimator.end(); @@ -270,19 +274,17 @@ public abstract class EventsTest */ @UiThreadTest @SmallTest - public void testStartCancel() throws Exception { + @Test + public void testStartCancel() throws Throwable { mFutureListener = new FutureReleaseListener(mFuture); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - mRunning = true; - mAnimator.start(); - mAnimator.cancel(); - mFuture.release(); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + mRunning = true; + mAnimator.start(); + mAnimator.cancel(); + mFuture.release(); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout(), TimeUnit.MILLISECONDS); @@ -293,19 +295,17 @@ public abstract class EventsTest */ @UiThreadTest @SmallTest - public void testStartEnd() throws Exception { + @Test + public void testStartEnd() throws Throwable { mFutureListener = new FutureReleaseListener(mFuture); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - mRunning = true; - mAnimator.start(); - mAnimator.end(); - mFuture.release(); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + mRunning = true; + mAnimator.start(); + mAnimator.end(); + mFuture.release(); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout(), TimeUnit.MILLISECONDS); @@ -315,20 +315,18 @@ public abstract class EventsTest * Same as testStartCancel, but with a startDelayed animator */ @SmallTest - public void testStartDelayedCancel() throws Exception { + @Test + public void testStartDelayedCancel() throws Throwable { mFutureListener = new FutureReleaseListener(mFuture); mAnimator.setStartDelay(ANIM_DELAY); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - mRunning = true; - mAnimator.start(); - mAnimator.cancel(); - mFuture.release(); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + mRunning = true; + mAnimator.start(); + mAnimator.cancel(); + mFuture.release(); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout(), TimeUnit.MILLISECONDS); @@ -338,20 +336,18 @@ public abstract class EventsTest * Same as testStartEnd, but with a startDelayed animator */ @SmallTest - public void testStartDelayedEnd() throws Exception { + @Test + public void testStartDelayedEnd() throws Throwable { mFutureListener = new FutureReleaseListener(mFuture); mAnimator.setStartDelay(ANIM_DELAY); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - mRunning = true; - mAnimator.start(); - mAnimator.end(); - mFuture.release(); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + mRunning = true; + mAnimator.start(); + mAnimator.end(); + mFuture.release(); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout(), TimeUnit.MILLISECONDS); @@ -361,20 +357,18 @@ public abstract class EventsTest * Verify that canceling an animator that is playing does the right thing. */ @MediumTest - public void testPlayingCancel() throws Exception { + @Test + public void testPlayingCancel() throws Throwable { mFutureListener = new FutureReleaseListener(mFuture); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - Handler handler = new Handler(); - mAnimator.addListener(mFutureListener); - mRunning = true; - mAnimator.start(); - handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + Handler handler = new Handler(); + mAnimator.addListener(mFutureListener); + mRunning = true; + mAnimator.start(); + handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout(), TimeUnit.MILLISECONDS); @@ -384,20 +378,18 @@ public abstract class EventsTest * Verify that ending an animator that is playing does the right thing. */ @MediumTest - public void testPlayingEnd() throws Exception { + @Test + public void testPlayingEnd() throws Throwable { mFutureListener = new FutureReleaseListener(mFuture); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - Handler handler = new Handler(); - mAnimator.addListener(mFutureListener); - mRunning = true; - mAnimator.start(); - handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DURATION); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + Handler handler = new Handler(); + mAnimator.addListener(mFutureListener); + mRunning = true; + mAnimator.start(); + handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DURATION); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout(), TimeUnit.MILLISECONDS); @@ -407,21 +399,19 @@ public abstract class EventsTest * Same as testPlayingCancel, but with a startDelayed animator */ @MediumTest - public void testPlayingDelayedCancel() throws Exception { + @Test + public void testPlayingDelayedCancel() throws Throwable { mAnimator.setStartDelay(ANIM_DELAY); mFutureListener = new FutureReleaseListener(mFuture); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - Handler handler = new Handler(); - mAnimator.addListener(mFutureListener); - mRunning = true; - mAnimator.start(); - handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + Handler handler = new Handler(); + mAnimator.addListener(mFutureListener); + mRunning = true; + mAnimator.start(); + handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout(), TimeUnit.MILLISECONDS); @@ -431,21 +421,19 @@ public abstract class EventsTest * Same as testPlayingEnd, but with a startDelayed animator */ @MediumTest - public void testPlayingDelayedEnd() throws Exception { + @Test + public void testPlayingDelayedEnd() throws Throwable { mAnimator.setStartDelay(ANIM_DELAY); mFutureListener = new FutureReleaseListener(mFuture); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - Handler handler = new Handler(); - mAnimator.addListener(mFutureListener); - mRunning = true; - mAnimator.start(); - handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DURATION); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + Handler handler = new Handler(); + mAnimator.addListener(mFutureListener); + mRunning = true; + mAnimator.start(); + handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DURATION); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout(), TimeUnit.MILLISECONDS); @@ -455,24 +443,21 @@ public abstract class EventsTest * Same as testPlayingDelayedCancel, but cancel during the startDelay period */ @MediumTest - public void testPlayingDelayedCancelMidDelay() throws Exception { + @Test + public void testPlayingDelayedCancelMidDelay() throws Throwable { mAnimator.setStartDelay(ANIM_DELAY); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - // Set the listener to automatically timeout after an uncanceled animation - // would have finished. This tests to make sure that we're not calling - // the listeners with cancel/end callbacks since they won't be called - // with the start event. - mFutureListener = new FutureReleaseListener(mFuture, getTimeout()); - Handler handler = new Handler(); - mRunning = true; - mAnimator.start(); - handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DELAY); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + // Set the listener to automatically timeout after an uncanceled animation would + // have finished. This tests to make sure that we're not calling the listeners with + // cancel/end callbacks since they won't be called with the start event. + mFutureListener = new FutureReleaseListener(mFuture, getTimeout()); + Handler handler = new Handler(); + mRunning = true; + mAnimator.start(); + handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DELAY); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout() + 100, TimeUnit.MILLISECONDS); @@ -482,24 +467,21 @@ public abstract class EventsTest * Same as testPlayingDelayedEnd, but end during the startDelay period */ @MediumTest - public void testPlayingDelayedEndMidDelay() throws Exception { + @Test + public void testPlayingDelayedEndMidDelay() throws Throwable { mAnimator.setStartDelay(ANIM_DELAY); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - // Set the listener to automatically timeout after an uncanceled animation - // would have finished. This tests to make sure that we're not calling - // the listeners with cancel/end callbacks since they won't be called - // with the start event. - mFutureListener = new FutureReleaseListener(mFuture, getTimeout()); - Handler handler = new Handler(); - mRunning = true; - mAnimator.start(); - handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DELAY); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + // Set the listener to automatically timeout after an uncanceled animation would + // have finished. This tests to make sure that we're not calling the listeners with + // cancel/end callbacks since they won't be called with the start event. + mFutureListener = new FutureReleaseListener(mFuture, getTimeout()); + Handler handler = new Handler(); + mRunning = true; + mAnimator.start(); + handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DELAY); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout() + 100, TimeUnit.MILLISECONDS); @@ -510,20 +492,18 @@ public abstract class EventsTest * does nothing. */ @MediumTest - public void testStartDoubleCancel() throws Exception { + @Test + public void testStartDoubleCancel() throws Throwable { mFutureListener = new FutureReleaseListener(mFuture); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - mRunning = true; - mAnimator.start(); - mAnimator.cancel(); - mAnimator.cancel(); - mFuture.release(); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + mRunning = true; + mAnimator.start(); + mAnimator.cancel(); + mAnimator.cancel(); + mFuture.release(); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout(), TimeUnit.MILLISECONDS); @@ -534,21 +514,19 @@ public abstract class EventsTest * does nothing. */ @MediumTest - public void testStartDoubleEnd() throws Exception { + @Test + public void testStartDoubleEnd() throws Throwable { mFutureListener = new FutureReleaseListener(mFuture); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - mRunning = true; - mAnimator.start(); - mAnimator.end(); - mRunning = true; // end() implicitly starts an unstarted animator - mAnimator.end(); - mFuture.release(); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + mRunning = true; + mAnimator.start(); + mAnimator.end(); + mRunning = true; // end() implicitly starts an unstarted animator + mAnimator.end(); + mFuture.release(); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout(), TimeUnit.MILLISECONDS); @@ -558,21 +536,19 @@ public abstract class EventsTest * Same as testStartDoubleCancel, but with a startDelayed animator */ @MediumTest - public void testStartDelayedDoubleCancel() throws Exception { + @Test + public void testStartDelayedDoubleCancel() throws Throwable { mAnimator.setStartDelay(ANIM_DELAY); mFutureListener = new FutureReleaseListener(mFuture); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - mRunning = true; - mAnimator.start(); - mAnimator.cancel(); - mAnimator.cancel(); - mFuture.release(); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + mRunning = true; + mAnimator.start(); + mAnimator.cancel(); + mAnimator.cancel(); + mFuture.release(); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout(), TimeUnit.MILLISECONDS); @@ -582,22 +558,20 @@ public abstract class EventsTest * Same as testStartDoubleEnd, but with a startDelayed animator */ @MediumTest - public void testStartDelayedDoubleEnd() throws Exception { + @Test + public void testStartDelayedDoubleEnd() throws Throwable { mAnimator.setStartDelay(ANIM_DELAY); mFutureListener = new FutureReleaseListener(mFuture); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - mRunning = true; - mAnimator.start(); - mAnimator.end(); - mRunning = true; // end() implicitly starts an unstarted animator - mAnimator.end(); - mFuture.release(); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + mRunning = true; + mAnimator.start(); + mAnimator.end(); + mRunning = true; // end() implicitly starts an unstarted animator + mAnimator.end(); + mFuture.release(); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout(), TimeUnit.MILLISECONDS); @@ -608,22 +582,20 @@ public abstract class EventsTest * the appropriate timeout duration. */ @MediumTest - public void testPauseResume() throws Exception { + @Test + public void testPauseResume() throws Throwable { mFutureListener = new FutureReleaseListener(mFuture); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - Handler handler = new Handler(); - mAnimator.addListener(mFutureListener); - mRunning = true; - mAnimator.start(); - handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY); - handler.postDelayed(new Resumer(mAnimator, mFuture), - ANIM_PAUSE_DELAY + ANIM_PAUSE_DURATION); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + Handler handler = new Handler(); + mAnimator.addListener(mFutureListener); + mRunning = true; + mAnimator.start(); + handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY); + handler.postDelayed(new Resumer(mAnimator, mFuture), + ANIM_PAUSE_DELAY + ANIM_PAUSE_DURATION); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout() + ANIM_PAUSE_DURATION, TimeUnit.MILLISECONDS); @@ -634,23 +606,21 @@ public abstract class EventsTest * the appropriate timeout duration. */ @MediumTest - public void testPauseResumeDelayed() throws Exception { + @Test + public void testPauseResumeDelayed() throws Throwable { mAnimator.setStartDelay(ANIM_DELAY); mFutureListener = new FutureReleaseListener(mFuture); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - Handler handler = new Handler(); - mAnimator.addListener(mFutureListener); - mRunning = true; - mAnimator.start(); - handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY); - handler.postDelayed(new Resumer(mAnimator, mFuture), - ANIM_PAUSE_DELAY + ANIM_PAUSE_DURATION); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + Handler handler = new Handler(); + mAnimator.addListener(mFutureListener); + mRunning = true; + mAnimator.start(); + handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY); + handler.postDelayed(new Resumer(mAnimator, mFuture), + ANIM_PAUSE_DELAY + ANIM_PAUSE_DURATION); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout() + ANIM_PAUSE_DURATION + ANIM_FULL_DURATION_SLOP, @@ -661,20 +631,18 @@ public abstract class EventsTest * Verify that pausing an animator without resuming it causes a timeout. */ @MediumTest - public void testPauseTimeout() throws Exception { + @Test + public void testPauseTimeout() throws Throwable { mFutureListener = new FutureReleaseListener(mFuture); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - Handler handler = new Handler(); - mAnimator.addListener(mFutureListener); - mRunning = true; - mAnimator.start(); - handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + Handler handler = new Handler(); + mAnimator.addListener(mFutureListener); + mRunning = true; + mAnimator.start(); + handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); try { @@ -689,21 +657,19 @@ public abstract class EventsTest * Verify that pausing a startDelayed animator without resuming it causes a timeout. */ @MediumTest - public void testPauseTimeoutDelayed() throws Exception { + @Test + public void testPauseTimeoutDelayed() throws Throwable { mAnimator.setStartDelay(ANIM_DELAY); mFutureListener = new FutureReleaseListener(mFuture); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - Handler handler = new Handler(); - mAnimator.addListener(mFutureListener); - mRunning = true; - mAnimator.start(); - handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + Handler handler = new Handler(); + mAnimator.addListener(mFutureListener); + mRunning = true; + mAnimator.start(); + handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); try { diff --git a/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java b/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java index 53f94727f6d3..63ad061b2c6c 100644 --- a/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java +++ b/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java @@ -27,11 +27,10 @@ public class ObjectAnimatorEventsTest extends EventsTest { @Override public void setUp() throws Exception { - final BasicAnimatorActivity activity = getActivity(); - Button button = (Button) activity.findViewById(R.id.animatingButton); + final BasicAnimatorActivity activity = mActivityRule.getActivity(); + Button button = activity.findViewById(R.id.animatingButton); mAnimator = ObjectAnimator.ofFloat(button, "translationX", 0, 100); super.setUp(); } - } diff --git a/core/tests/coretests/src/android/animation/StateListAnimatorTest.java b/core/tests/coretests/src/android/animation/StateListAnimatorTest.java index e755b89f8551..12f1977c6d2f 100644 --- a/core/tests/coretests/src/android/animation/StateListAnimatorTest.java +++ b/core/tests/coretests/src/android/animation/StateListAnimatorTest.java @@ -16,41 +16,47 @@ package android.animation; -import android.test.ActivityInstrumentationTestCase2; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + import android.util.StateSet; import android.view.View; import android.view.ViewGroup; import androidx.test.annotation.UiThreadTest; import androidx.test.filters.LargeTest; +import androidx.test.rule.ActivityTestRule; import com.android.frameworks.coretests.R; +import org.junit.Rule; +import org.junit.Test; + import java.util.concurrent.atomic.AtomicInteger; @LargeTest -public class StateListAnimatorTest extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> { +public class StateListAnimatorTest { - public StateListAnimatorTest() { - super(BasicAnimatorActivity.class); - } - - @Override - protected void setUp() throws Exception { - super.setUp(); - } + @Rule + public final ActivityTestRule<BasicAnimatorActivity> mActivityRule = + new ActivityTestRule<>(BasicAnimatorActivity.class); + @Test public void testInflateFromAnimator() throws Exception { StateListAnimator stateListAnimator = AnimatorInflater - .loadStateListAnimator(getActivity(), R.anim.test_state_anim); + .loadStateListAnimator(mActivityRule.getActivity(), R.anim.test_state_anim); assertNotNull("A state list animator should be returned", stateListAnimator); assertEquals("State list animator should have three items", 3, stateListAnimator.getTuples().size()); } @UiThreadTest + @Test public void testAttachDetach() throws Exception { - View view = new View(getActivity()); + final BasicAnimatorActivity activity = mActivityRule.getActivity(); + View view = new View(activity); final AtomicInteger setStateCount = new AtomicInteger(0); StateListAnimator stateListAnimator = new StateListAnimator() { @Override @@ -62,7 +68,7 @@ public class StateListAnimatorTest extends ActivityInstrumentationTestCase2<Basi view.setStateListAnimator(stateListAnimator); assertNotNull("State list animator should have a reference to view even if it is detached", stateListAnimator.getTarget()); - ViewGroup viewGroup = (ViewGroup) getActivity().findViewById(android.R.id.content); + ViewGroup viewGroup = activity.findViewById(android.R.id.content); int preSetStateCount = setStateCount.get(); viewGroup.addView(view); assertTrue("When view is attached, state list drawable's setState should be called", @@ -82,9 +88,10 @@ public class StateListAnimatorTest extends ActivityInstrumentationTestCase2<Basi stateListAnimator2.getTarget()); } + @Test public void testStateListLoading() throws InterruptedException { StateListAnimator stateListAnimator = AnimatorInflater - .loadStateListAnimator(getActivity(), R.anim.test_state_anim); + .loadStateListAnimator(mActivityRule.getActivity(), R.anim.test_state_anim); assertNotNull("A state list animator should be returned", stateListAnimator); assertEquals("Steate list animator should have two items", 3, stateListAnimator.getTuples().size()); diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java b/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java index f6d71b88bd89..ba9aef828b8b 100644 --- a/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java +++ b/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java @@ -26,5 +26,4 @@ public class ValueAnimatorEventsTest extends EventsTest { mAnimator = ValueAnimator.ofFloat(0, 1); super.setUp(); } - } diff --git a/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java b/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java index 997af009a9b8..81cd4da4f425 100644 --- a/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java +++ b/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java @@ -16,17 +16,24 @@ package android.animation; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import android.os.Handler; -import android.test.ActivityInstrumentationTestCase2; import android.view.ViewPropertyAnimator; import android.widget.Button; import androidx.test.annotation.UiThreadTest; import androidx.test.filters.MediumTest; import androidx.test.filters.SmallTest; +import androidx.test.rule.ActivityTestRule; import com.android.frameworks.coretests.R; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + import java.util.concurrent.TimeUnit; /** @@ -41,8 +48,11 @@ import java.util.concurrent.TimeUnit; * wait for some later event to occur before ending. These tests use a combination of an * AbstractFuture mechanism and a delayed action to release that Future later. */ -public abstract class ViewPropertyAnimatorTest - extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> { +public class ViewPropertyAnimatorTest { + + @Rule + public final ActivityTestRule<BasicAnimatorActivity> mActivityRule = + new ActivityTestRule<>(BasicAnimatorActivity.class); protected static final int ANIM_DURATION = 400; protected static final int ANIM_DELAY = 100; @@ -79,7 +89,7 @@ public abstract class ViewPropertyAnimatorTest mFuture.setException(new RuntimeException(e)); } } - }; + } /** * Timeout length, based on when the animation should reasonably be complete. @@ -108,28 +118,14 @@ public abstract class ViewPropertyAnimatorTest public FutureReleaseListener(FutureWaiter future, long timeout) { mFuture = future; Handler handler = new Handler(); - handler.postDelayed(new Runnable() { - @Override - public void run() { - mFuture.release(); - } - }, timeout); + handler.postDelayed(mFuture::release, timeout); } @Override public void onAnimationEnd(Animator animation) { Handler handler = new Handler(); - handler.postDelayed(new Runnable() { - @Override - public void run() { - mFuture.release(); - } - }, FUTURE_RELEASE_DELAY); + handler.postDelayed(mFuture::release, FUTURE_RELEASE_DELAY); } - }; - - public ViewPropertyAnimatorTest() { - super(BasicAnimatorActivity.class); } /** @@ -138,15 +134,13 @@ public abstract class ViewPropertyAnimatorTest * and then call super.setup(), where further properties are set on that animator. * @throws Exception */ - @Override + @Before public void setUp() throws Exception { - final BasicAnimatorActivity activity = getActivity(); - Button button = (Button) activity.findViewById(R.id.animatingButton); + final BasicAnimatorActivity activity = mActivityRule.getActivity(); + Button button = activity.findViewById(R.id.animatingButton); mAnimator = button.animate().x(100).y(100); - super.setUp(); - // mListener is the main testing mechanism of this file. The asserts of each test // are embedded in the listener callbacks that it implements. mListener = new AnimatorListenerAdapter() { @@ -195,6 +189,7 @@ public abstract class ViewPropertyAnimatorTest */ @UiThreadTest @SmallTest + @Test public void testCancel() throws Exception { mAnimator.cancel(); } @@ -204,19 +199,17 @@ public abstract class ViewPropertyAnimatorTest */ @UiThreadTest @SmallTest - public void testStartCancel() throws Exception { + @Test + public void testStartCancel() throws Throwable { mFutureListener = new FutureReleaseListener(mFuture); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - mRunning = true; - mAnimator.start(); - mAnimator.cancel(); - mFuture.release(); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + mRunning = true; + mAnimator.start(); + mAnimator.cancel(); + mFuture.release(); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout(), TimeUnit.MILLISECONDS); @@ -226,20 +219,18 @@ public abstract class ViewPropertyAnimatorTest * Same as testStartCancel, but with a startDelayed animator */ @SmallTest - public void testStartDelayedCancel() throws Exception { + @Test + public void testStartDelayedCancel() throws Throwable { mFutureListener = new FutureReleaseListener(mFuture); mAnimator.setStartDelay(ANIM_DELAY); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - mRunning = true; - mAnimator.start(); - mAnimator.cancel(); - mFuture.release(); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + mRunning = true; + mAnimator.start(); + mAnimator.cancel(); + mFuture.release(); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout(), TimeUnit.MILLISECONDS); @@ -249,20 +240,18 @@ public abstract class ViewPropertyAnimatorTest * Verify that canceling an animator that is playing does the right thing. */ @MediumTest - public void testPlayingCancel() throws Exception { + @Test + public void testPlayingCancel() throws Throwable { mFutureListener = new FutureReleaseListener(mFuture); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - Handler handler = new Handler(); - mAnimator.setListener(mFutureListener); - mRunning = true; - mAnimator.start(); - handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + Handler handler = new Handler(); + mAnimator.setListener(mFutureListener); + mRunning = true; + mAnimator.start(); + handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout(), TimeUnit.MILLISECONDS); @@ -272,21 +261,19 @@ public abstract class ViewPropertyAnimatorTest * Same as testPlayingCancel, but with a startDelayed animator */ @MediumTest - public void testPlayingDelayedCancel() throws Exception { + @Test + public void testPlayingDelayedCancel() throws Throwable { mAnimator.setStartDelay(ANIM_DELAY); mFutureListener = new FutureReleaseListener(mFuture); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - Handler handler = new Handler(); - mAnimator.setListener(mFutureListener); - mRunning = true; - mAnimator.start(); - handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + Handler handler = new Handler(); + mAnimator.setListener(mFutureListener); + mRunning = true; + mAnimator.start(); + handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout(), TimeUnit.MILLISECONDS); @@ -296,24 +283,21 @@ public abstract class ViewPropertyAnimatorTest * Same as testPlayingDelayedCancel, but cancel during the startDelay period */ @MediumTest - public void testPlayingDelayedCancelMidDelay() throws Exception { + @Test + public void testPlayingDelayedCancelMidDelay() throws Throwable { mAnimator.setStartDelay(ANIM_DELAY); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - // Set the listener to automatically timeout after an uncanceled animation - // would have finished. This tests to make sure that we're not calling - // the listeners with cancel/end callbacks since they won't be called - // with the start event. - mFutureListener = new FutureReleaseListener(mFuture, getTimeout()); - Handler handler = new Handler(); - mRunning = true; - mAnimator.start(); - handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DELAY); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + // Set the listener to automatically timeout after an uncanceled animation would + // have finished. This tests to make sure that we're not calling the listeners with + // cancel/end callbacks since they won't be called with the start event. + mFutureListener = new FutureReleaseListener(mFuture, getTimeout()); + Handler handler = new Handler(); + mRunning = true; + mAnimator.start(); + handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DELAY); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout() + 100, TimeUnit.MILLISECONDS); @@ -324,20 +308,18 @@ public abstract class ViewPropertyAnimatorTest * does nothing. */ @MediumTest - public void testStartDoubleCancel() throws Exception { + @Test + public void testStartDoubleCancel() throws Throwable { mFutureListener = new FutureReleaseListener(mFuture); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - mRunning = true; - mAnimator.start(); - mAnimator.cancel(); - mAnimator.cancel(); - mFuture.release(); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + mRunning = true; + mAnimator.start(); + mAnimator.cancel(); + mAnimator.cancel(); + mFuture.release(); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout(), TimeUnit.MILLISECONDS); @@ -347,24 +329,21 @@ public abstract class ViewPropertyAnimatorTest * Same as testStartDoubleCancel, but with a startDelayed animator */ @MediumTest - public void testStartDelayedDoubleCancel() throws Exception { + @Test + public void testStartDelayedDoubleCancel() throws Throwable { mAnimator.setStartDelay(ANIM_DELAY); mFutureListener = new FutureReleaseListener(mFuture); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - mRunning = true; - mAnimator.start(); - mAnimator.cancel(); - mAnimator.cancel(); - mFuture.release(); - } catch (junit.framework.AssertionFailedError e) { - mFuture.setException(new RuntimeException(e)); - } + mActivityRule.runOnUiThread(() -> { + try { + mRunning = true; + mAnimator.start(); + mAnimator.cancel(); + mAnimator.cancel(); + mFuture.release(); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); } }); mFuture.get(getTimeout(), TimeUnit.MILLISECONDS); } - } diff --git a/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java b/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java index 16be0b0a27c1..d8799cb182c8 100644 --- a/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java +++ b/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java @@ -17,7 +17,7 @@ package android.hardware.hdmi; import static com.google.common.truth.Truth.assertThat; -import android.support.test.filters.SmallTest; +import androidx.test.filters.SmallTest; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/android/view/AccessibilityInteractionControllerTest.java b/core/tests/coretests/src/android/view/AccessibilityInteractionControllerTest.java index d0719cbea86e..7855ef9c49ad 100644 --- a/core/tests/coretests/src/android/view/AccessibilityInteractionControllerTest.java +++ b/core/tests/coretests/src/android/view/AccessibilityInteractionControllerTest.java @@ -26,9 +26,6 @@ import android.app.Service; import android.app.UiAutomation; import android.graphics.Rect; import android.os.SystemClock; -import android.support.test.InstrumentationRegistry; -import android.support.test.rule.ActivityTestRule; -import android.support.test.runner.AndroidJUnit4; import android.text.TextUtils; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; @@ -36,6 +33,10 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityTestActivity; import android.view.accessibility.AccessibilityWindowInfo; +import androidx.test.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + import com.android.compatibility.common.util.TestUtils; import com.android.frameworks.coretests.R; diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java index b07cb99f35c5..da81d176de8a 100644 --- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java @@ -24,6 +24,7 @@ import static junit.framework.Assert.assertTrue; import android.content.Context; import android.graphics.Insets; +import android.graphics.Point; import android.graphics.Rect; import android.os.Bundle; import android.platform.test.annotations.Presubmit; @@ -80,7 +81,7 @@ public class ImeInsetsSourceConsumerTest { @Test public void testImeVisibility() { - final InsetsSourceControl ime = new InsetsSourceControl(TYPE_IME, mLeash); + final InsetsSourceControl ime = new InsetsSourceControl(TYPE_IME, mLeash, new Point()); mController.onControlsChanged(new InsetsSourceControl[] { ime }); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java index 7cd3c44d9a4e..71ce02d859f5 100644 --- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java +++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java @@ -19,13 +19,23 @@ package android.view; import static android.view.InsetsState.TYPE_NAVIGATION_BAR; import static android.view.InsetsState.TYPE_TOP_BAR; +import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; +import static android.view.WindowInsets.Type.sideBars; +import static android.view.WindowInsets.Type.systemBars; +import static android.view.WindowInsets.Type.topBar; import static junit.framework.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.graphics.Insets; import android.graphics.Matrix; +import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.platform.test.annotations.Presubmit; @@ -55,6 +65,7 @@ public class InsetsAnimationControlImplTest { private SurfaceSession mSession = new SurfaceSession(); private SurfaceControl mTopLeash; private SurfaceControl mNavLeash; + private InsetsState mInsetsState; @Mock Transaction mMockTransaction; @Mock InsetsController mMockController; @@ -63,6 +74,7 @@ public class InsetsAnimationControlImplTest { @Before public void setup() { + ViewRootImpl.sNewInsetsMode = NEW_INSETS_MODE_FULL; MockitoAnnotations.initMocks(this); mTopLeash = new SurfaceControl.Builder(mSession) .setName("testSurface") @@ -70,24 +82,25 @@ public class InsetsAnimationControlImplTest { mNavLeash = new SurfaceControl.Builder(mSession) .setName("testSurface") .build(); - InsetsState state = new InsetsState(); - state.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 500, 100)); - state.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(400, 0, 500, 500)); - InsetsSourceConsumer topConsumer = new InsetsSourceConsumer(TYPE_TOP_BAR, state, + mInsetsState = new InsetsState(); + mInsetsState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 500, 100)); + mInsetsState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(400, 0, 500, 500)); + InsetsSourceConsumer topConsumer = new InsetsSourceConsumer(TYPE_TOP_BAR, mInsetsState, () -> mMockTransaction, mMockController); - topConsumer.setControl(new InsetsSourceControl(TYPE_TOP_BAR, mTopLeash)); + topConsumer.setControl(new InsetsSourceControl(TYPE_TOP_BAR, mTopLeash, new Point(0, 0))); - InsetsSourceConsumer navConsumer = new InsetsSourceConsumer(TYPE_NAVIGATION_BAR, state, - () -> mMockTransaction, mMockController); + InsetsSourceConsumer navConsumer = new InsetsSourceConsumer(TYPE_NAVIGATION_BAR, + mInsetsState, () -> mMockTransaction, mMockController); navConsumer.hide(); - navConsumer.setControl(new InsetsSourceControl(TYPE_NAVIGATION_BAR, mNavLeash)); + navConsumer.setControl(new InsetsSourceControl(TYPE_NAVIGATION_BAR, mNavLeash, + new Point(400, 0))); SparseArray<InsetsSourceConsumer> consumers = new SparseArray<>(); consumers.put(TYPE_TOP_BAR, topConsumer); consumers.put(TYPE_NAVIGATION_BAR, navConsumer); mController = new InsetsAnimationControlImpl(consumers, - new Rect(0, 0, 500, 500), state, mMockListener, WindowInsets.Type.systemBars(), - () -> mMockTransactionApplier, mock(InsetsController.class)); + new Rect(0, 0, 500, 500), mInsetsState, mMockListener, systemBars(), + () -> mMockTransactionApplier, mMockController); } @Test @@ -95,7 +108,7 @@ public class InsetsAnimationControlImplTest { assertEquals(Insets.of(0, 100, 100, 0), mController.getShownStateInsets()); assertEquals(Insets.of(0, 0, 0, 0), mController.getHiddenStateInsets()); assertEquals(Insets.of(0, 100, 0, 0), mController.getCurrentInsets()); - assertEquals(WindowInsets.Type.systemBars(), mController.getTypes()); + assertEquals(systemBars(), mController.getTypes()); } @Test diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index 8f2109676dfb..6dad6a22f7ea 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -27,6 +27,7 @@ import static junit.framework.Assert.assertTrue; import android.content.Context; import android.graphics.Insets; +import android.graphics.Point; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.view.WindowInsets.Type; @@ -74,11 +75,12 @@ public class InsetsControllerTest { Insets.of(10, 10, 10, 10), rect, rect, rect, rect), rect, rect); }); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } @Test public void testControlsChanged() { - InsetsSourceControl control = new InsetsSourceControl(TYPE_TOP_BAR, mLeash); + InsetsSourceControl control = new InsetsSourceControl(TYPE_TOP_BAR, mLeash, new Point()); mController.onControlsChanged(new InsetsSourceControl[] { control }); assertEquals(mLeash, mController.getSourceConsumer(TYPE_TOP_BAR).getControl().getLeash()); @@ -86,7 +88,7 @@ public class InsetsControllerTest { @Test public void testControlsRevoked() { - InsetsSourceControl control = new InsetsSourceControl(TYPE_TOP_BAR, mLeash); + InsetsSourceControl control = new InsetsSourceControl(TYPE_TOP_BAR, mLeash, new Point()); mController.onControlsChanged(new InsetsSourceControl[] { control }); mController.onControlsChanged(new InsetsSourceControl[0]); assertNull(mController.getSourceConsumer(TYPE_TOP_BAR).getControl()); @@ -94,22 +96,19 @@ public class InsetsControllerTest { @Test public void testAnimationEndState() { - final InsetsSourceControl navBar = new InsetsSourceControl(TYPE_NAVIGATION_BAR, mLeash); - final InsetsSourceControl topBar = new InsetsSourceControl(TYPE_TOP_BAR, mLeash); - final InsetsSourceControl ime = new InsetsSourceControl(TYPE_IME, mLeash); + InsetsSourceControl[] controls = prepareControls(); + InsetsSourceControl navBar = controls[0]; + InsetsSourceControl topBar = controls[1]; + InsetsSourceControl ime = controls[2]; - InsetsSourceControl[] controls = new InsetsSourceControl[3]; - controls[0] = navBar; - controls[1] = topBar; - controls[2] = ime; - mController.onControlsChanged(controls); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mController.show(Type.all()); // quickly jump to final state by cancelling it. mController.cancelExistingAnimation(); assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible()); assertTrue(mController.getSourceConsumer(topBar.getType()).isVisible()); - assertTrue(mController.getSourceConsumer(ime.getType()).isVisible()); + // no focused view, no IME. + assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); mController.hide(Type.all()); mController.cancelExistingAnimation(); @@ -119,11 +118,175 @@ public class InsetsControllerTest { mController.show(Type.ime()); mController.cancelExistingAnimation(); + // no focused view, no IME. + assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + }); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + } + + @Test + public void testApplyImeVisibility() { + final InsetsSourceControl ime = new InsetsSourceControl(TYPE_IME, mLeash, new Point()); + + InsetsSourceControl[] controls = new InsetsSourceControl[3]; + controls[0] = ime; + mController.onControlsChanged(controls); + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + mController.applyImeVisibility(true); + mController.cancelExistingAnimation(); assertTrue(mController.getSourceConsumer(ime.getType()).isVisible()); + mController.applyImeVisibility(false); + mController.cancelExistingAnimation(); + assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + }); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + } - mController.hide(Type.ime()); + @Test + public void testShowHideSelectively() { + InsetsSourceControl[] controls = prepareControls(); + InsetsSourceControl navBar = controls[0]; + InsetsSourceControl topBar = controls[1]; + InsetsSourceControl ime = controls[2]; + + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + int types = Type.sideBars() | Type.systemBars(); + // test show select types. + mController.show(types); mController.cancelExistingAnimation(); + assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible()); + assertTrue(mController.getSourceConsumer(topBar.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + + // test hide all + mController.hide(types); + mController.cancelExistingAnimation(); + assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible()); assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); }); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + } + + @Test + public void testShowHideSingle() { + InsetsSourceControl[] controls = prepareControls(); + InsetsSourceControl navBar = controls[0]; + InsetsSourceControl topBar = controls[1]; + InsetsSourceControl ime = controls[2]; + + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + int types = Type.sideBars() | Type.systemBars(); + // test show select types. + mController.show(types); + mController.cancelExistingAnimation(); + assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible()); + assertTrue(mController.getSourceConsumer(topBar.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + + // test hide all + mController.hide(Type.all()); + mController.cancelExistingAnimation(); + assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + + // test single show + mController.show(Type.sideBars()); + mController.cancelExistingAnimation(); + assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + + // test single hide + mController.hide(Type.sideBars()); + assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + + }); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + } + + @Test + public void testShowHideMultiple() { + InsetsSourceControl[] controls = prepareControls(); + InsetsSourceControl navBar = controls[0]; + InsetsSourceControl topBar = controls[1]; + InsetsSourceControl ime = controls[2]; + + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + // start two animations and see if previous is cancelled and final state is reached. + mController.show(Type.sideBars()); + mController.show(Type.systemBars()); + mController.cancelExistingAnimation(); + assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible()); + assertTrue(mController.getSourceConsumer(topBar.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + + mController.hide(Type.sideBars()); + mController.hide(Type.systemBars()); + mController.cancelExistingAnimation(); + assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + + int types = Type.sideBars() | Type.systemBars(); + // show two at a time and hide one by one. + mController.show(types); + mController.hide(Type.sideBars()); + mController.cancelExistingAnimation(); + assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible()); + assertTrue(mController.getSourceConsumer(topBar.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + + mController.hide(Type.systemBars()); + mController.cancelExistingAnimation(); + assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + }); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + } + + @Test + public void testShowMultipleHideOneByOne() { + InsetsSourceControl[] controls = prepareControls(); + InsetsSourceControl navBar = controls[0]; + InsetsSourceControl topBar = controls[1]; + InsetsSourceControl ime = controls[2]; + + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + int types = Type.sideBars() | Type.systemBars(); + // show two at a time and hide one by one. + mController.show(types); + mController.hide(Type.sideBars()); + mController.cancelExistingAnimation(); + assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible()); + assertTrue(mController.getSourceConsumer(topBar.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + + mController.hide(Type.systemBars()); + mController.cancelExistingAnimation(); + assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + }); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + } + + private InsetsSourceControl[] prepareControls() { + final InsetsSourceControl navBar = new InsetsSourceControl(TYPE_NAVIGATION_BAR, mLeash, + new Point()); + final InsetsSourceControl topBar = new InsetsSourceControl(TYPE_TOP_BAR, mLeash, + new Point()); + final InsetsSourceControl ime = new InsetsSourceControl(TYPE_IME, mLeash, new Point()); + + InsetsSourceControl[] controls = new InsetsSourceControl[3]; + controls[0] = navBar; + controls[1] = topBar; + controls[2] = ime; + mController.onControlsChanged(controls); + return controls; } } diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java index 82cd2131ab4e..66146c936dca 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java @@ -24,6 +24,7 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; +import android.graphics.Point; import android.platform.test.annotations.Presubmit; import android.view.SurfaceControl.Transaction; @@ -56,7 +57,7 @@ public class InsetsSourceConsumerTest { .build(); mConsumer = new InsetsSourceConsumer(TYPE_TOP_BAR, new InsetsState(), () -> mMockTransaction, mMockController); - mConsumer.setControl(new InsetsSourceControl(TYPE_TOP_BAR, mLeash)); + mConsumer.setControl(new InsetsSourceControl(TYPE_TOP_BAR, mLeash, new Point())); } @Test @@ -78,7 +79,7 @@ public class InsetsSourceConsumerTest { reset(mMockTransaction); mConsumer.hide(); verifyZeroInteractions(mMockTransaction); - mConsumer.setControl(new InsetsSourceControl(TYPE_TOP_BAR, mLeash)); + mConsumer.setControl(new InsetsSourceControl(TYPE_TOP_BAR, mLeash, new Point())); verify(mMockTransaction).hide(eq(mLeash)); } } diff --git a/core/tests/coretests/src/android/view/PinchZoomAction.java b/core/tests/coretests/src/android/view/PinchZoomAction.java index bec9b557a40d..cfdec4dc831a 100644 --- a/core/tests/coretests/src/android/view/PinchZoomAction.java +++ b/core/tests/coretests/src/android/view/PinchZoomAction.java @@ -16,20 +16,21 @@ package android.view; -import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom; -import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom; +import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed; import static com.google.common.base.Preconditions.checkNotNull; import static org.hamcrest.Matchers.allOf; import android.os.SystemClock; -import android.support.test.espresso.InjectEventSecurityException; -import android.support.test.espresso.PerformException; -import android.support.test.espresso.UiController; -import android.support.test.espresso.ViewAction; -import android.support.test.espresso.action.Swiper; -import android.support.test.espresso.util.HumanReadables; + +import androidx.test.espresso.InjectEventSecurityException; +import androidx.test.espresso.PerformException; +import androidx.test.espresso.UiController; +import androidx.test.espresso.ViewAction; +import androidx.test.espresso.action.Swiper; +import androidx.test.espresso.util.HumanReadables; import org.hamcrest.Matcher; diff --git a/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java b/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java index 199013513bb3..f63a4548b478 100644 --- a/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java +++ b/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java @@ -16,48 +16,43 @@ package android.view; -import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import android.content.Context; -import android.test.ActivityInstrumentationTestCase2; import android.util.DisplayMetrics; import android.widget.TextView; -import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; +import androidx.test.rule.ActivityTestRule; import com.android.frameworks.coretests.R; -import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; @LargeTest -public class ScaleGestureDetectorTest extends ActivityInstrumentationTestCase2<ScaleGesture> { - private ScaleGesture mScaleGestureActivity; +public class ScaleGestureDetectorTest { - public ScaleGestureDetectorTest() { - super("com.android.frameworks.coretests", ScaleGesture.class); - } + @Rule + public final ActivityTestRule<ScaleGesture> mActivityRule = + new ActivityTestRule<>(ScaleGesture.class); + private ScaleGesture mScaleGestureActivity; @Before public void setUp() throws Exception { - super.setUp(); - injectInstrumentation(InstrumentationRegistry.getInstrumentation()); - mScaleGestureActivity = getActivity(); - } - - @After - public void tearDown() throws Exception { - super.tearDown(); + mScaleGestureActivity = mActivityRule.getActivity(); } @Test public void testScaleGestureDetector() { // No scaling should have occurred prior to performing pinch zoom action. final float initialScaleFactor = 1.0f; - assertEquals(initialScaleFactor, mScaleGestureActivity.getScaleFactor()); + assertEquals(initialScaleFactor, mScaleGestureActivity.getScaleFactor(), 0f); // Specify start and end coordinates, irrespective of device display size. final DisplayMetrics dm = new DisplayMetrics(); diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java index 71612e685920..34fdebfdf348 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java @@ -155,5 +155,10 @@ public class ContentCaptureSessionTest { void internalNotifyViewTextChanged(AutofillId id, CharSequence text) { throw new UnsupportedOperationException("should not have been called"); } + + @Override + public void internalNotifyViewHierarchyEvent(boolean started) { + throw new UnsupportedOperationException("should not have been called"); + } } } diff --git a/core/tests/coretests/src/android/widget/EditorCursorTest.java b/core/tests/coretests/src/android/widget/EditorCursorTest.java index e4f55df6f683..585c601b84c6 100644 --- a/core/tests/coretests/src/android/widget/EditorCursorTest.java +++ b/core/tests/coretests/src/android/widget/EditorCursorTest.java @@ -16,11 +16,12 @@ package android.widget; -import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.action.ViewActions.click; import static android.widget.espresso.TextViewAssertions.hasInsertionPointerOnLeft; import static android.widget.espresso.TextViewAssertions.hasInsertionPointerOnRight; +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; + import static junit.framework.Assert.fail; import static org.hamcrest.MatcherAssert.assertThat; diff --git a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java index 483270e1f9ab..f6e02bc1f48a 100644 --- a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java +++ b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java @@ -16,15 +16,6 @@ package android.widget; -import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.Espresso.pressBack; -import static android.support.test.espresso.action.ViewActions.clearText; -import static android.support.test.espresso.action.ViewActions.click; -import static android.support.test.espresso.action.ViewActions.replaceText; -import static android.support.test.espresso.assertion.ViewAssertions.matches; -import static android.support.test.espresso.matcher.RootMatchers.withDecorView; -import static android.support.test.espresso.matcher.ViewMatchers.withId; -import static android.support.test.espresso.matcher.ViewMatchers.withText; import static android.widget.espresso.DragHandleUtils.onHandleView; import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem; import static android.widget.espresso.FloatingToolbarEspressoUtils.clickFloatingToolbarItem; @@ -37,43 +28,55 @@ import static android.widget.espresso.SuggestionsPopupwindowUtils.onSuggestionsP import static android.widget.espresso.TextViewActions.clickOnTextAtIndex; import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex; +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.Espresso.pressBack; +import static androidx.test.espresso.action.ViewActions.clearText; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.action.ViewActions.replaceText; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.RootMatchers.withDecorView; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import android.content.res.TypedArray; -import android.support.test.espresso.NoMatchingViewException; -import android.support.test.espresso.ViewAssertion; -import android.test.ActivityInstrumentationTestCase2; import android.text.Selection; import android.text.Spannable; import android.text.Spanned; import android.text.TextPaint; import android.text.style.SuggestionSpan; import android.text.style.TextAppearanceSpan; -import android.view.View; import androidx.test.filters.SmallTest; +import androidx.test.rule.ActivityTestRule; import com.android.frameworks.coretests.R; +import org.junit.Rule; +import org.junit.Test; + /** * SuggestionsPopupWindowTest tests. * * TODO: Add tests for when there are no suggestions */ -public class SuggestionsPopupWindowTest extends ActivityInstrumentationTestCase2<TextViewActivity> { +@SmallTest +public class SuggestionsPopupWindowTest { - public SuggestionsPopupWindowTest() { - super(TextViewActivity.class); - } + @Rule + public final ActivityTestRule<TextViewActivity> mActivityRule = + new ActivityTestRule<>(TextViewActivity.class); - @Override - protected void setUp() throws Exception { - super.setUp(); - getActivity(); + private TextViewActivity getActivity() { + return mActivityRule.getActivity(); } private void setSuggestionSpan(SuggestionSpan span, int start, int end) { - final TextView textView = (TextView) getActivity().findViewById(R.id.textview); + final TextView textView = getActivity().findViewById(R.id.textview); textView.post( () -> { final Spannable text = (Spannable) textView.getText(); @@ -83,7 +86,7 @@ public class SuggestionsPopupWindowTest extends ActivityInstrumentationTestCase2 getInstrumentation().waitForIdleSync(); } - @SmallTest + @Test public void testOnTextContextMenuItem() { final String text = "abc def ghi"; @@ -94,14 +97,14 @@ public class SuggestionsPopupWindowTest extends ActivityInstrumentationTestCase2 new String[]{"DEF", "Def"}, SuggestionSpan.FLAG_AUTO_CORRECTION); setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1); - final TextView textView = (TextView) getActivity().findViewById(R.id.textview); + final TextView textView = getActivity().findViewById(R.id.textview); textView.post(() -> textView.onTextContextMenuItem(TextView.ID_REPLACE)); getInstrumentation().waitForIdleSync(); assertSuggestionsPopupIsDisplayed(); } - @SmallTest + @Test public void testSelectionActionMode() { final String text = "abc def ghi"; @@ -123,7 +126,7 @@ public class SuggestionsPopupWindowTest extends ActivityInstrumentationTestCase2 assertSuggestionsPopupIsDisplayed(); } - @SmallTest + @Test public void testInsertionActionMode() { final String text = "abc def ghi"; @@ -146,13 +149,13 @@ public class SuggestionsPopupWindowTest extends ActivityInstrumentationTestCase2 } private void showSuggestionsPopup() { - final TextView textView = (TextView) getActivity().findViewById(R.id.textview); + final TextView textView = getActivity().findViewById(R.id.textview); textView.post(() -> textView.onTextContextMenuItem(TextView.ID_REPLACE)); getInstrumentation().waitForIdleSync(); assertSuggestionsPopupIsDisplayed(); } - @SmallTest + @Test public void testSuggestionItems() { final String text = "abc def ghi"; @@ -190,7 +193,7 @@ public class SuggestionsPopupWindowTest extends ActivityInstrumentationTestCase2 onView(withId(R.id.textview)).check(matches(withText("abc ghi"))); } - @SmallTest + @Test public void testMisspelled() { final String text = "abc def ghi"; @@ -217,7 +220,7 @@ public class SuggestionsPopupWindowTest extends ActivityInstrumentationTestCase2 // TODO: Check if add to dictionary dialog is displayed. } - @SmallTest + @Test public void testEasyCorrect() { final String text = "abc def ghi"; @@ -253,7 +256,7 @@ public class SuggestionsPopupWindowTest extends ActivityInstrumentationTestCase2 getActivity().getString(com.android.internal.R.string.delete)); } - @SmallTest + @Test public void testTextAppearanceInSuggestionsPopup() { final String text = "abc def ghi"; @@ -302,53 +305,49 @@ public class SuggestionsPopupWindowTest extends ActivityInstrumentationTestCase2 assertSuggestionsPopupContainsItem( getActivity().getString(com.android.internal.R.string.delete)); - onSuggestionsPopup().check(new ViewAssertion() { - @Override - public void check(View view, NoMatchingViewException e) { - final ListView listView = (ListView) view.findViewById( - com.android.internal.R.id.suggestionContainer); - assertNotNull(listView); - final int childNum = listView.getChildCount(); - assertEquals(singleWordCandidates.length + multiWordCandidates.length, - childNum); - - for (int j = 0; j < childNum; j++) { - final TextView suggestion = (TextView) listView.getChildAt(j); - assertNotNull(suggestion); - final Spanned spanned = (Spanned) suggestion.getText(); - assertNotNull(spanned); - - // Check that the suggestion item order is kept. - final String expectedText; - if (j < singleWordCandidates.length) { - expectedText = "abc " + singleWordCandidates[j] + " ghi"; - } else { - expectedText = multiWordCandidates[j - singleWordCandidates.length]; - } - assertEquals(expectedText, spanned.toString()); - - // Check that the text is highlighted with correct color and text size. - final TextAppearanceSpan[] taSpan = spanned.getSpans( - text.indexOf('d'), text.indexOf('f') + 1, TextAppearanceSpan.class); - assertEquals(1, taSpan.length); - TextPaint tp = new TextPaint(); - taSpan[0].updateDrawState(tp); - assertEquals(expectedHighlightTextColor, tp.getColor()); - assertEquals(expectedHighlightTextSize, tp.getTextSize()); - - // Check the correct part of the text is highlighted. - final int expectedStart; - final int expectedEnd; - if (j < singleWordCandidates.length) { - expectedStart = text.indexOf('d'); - expectedEnd = text.indexOf('f') + 1; - } else { - expectedStart = 0; - expectedEnd = text.length(); - } - assertEquals(expectedStart, spanned.getSpanStart(taSpan[0])); - assertEquals(expectedEnd, spanned.getSpanEnd(taSpan[0])); + onSuggestionsPopup().check((view, e) -> { + final ListView listView = view.findViewById( + com.android.internal.R.id.suggestionContainer); + assertNotNull(listView); + final int childNum = listView.getChildCount(); + assertEquals(singleWordCandidates.length + multiWordCandidates.length, childNum); + + for (int j = 0; j < childNum; j++) { + final TextView suggestion = (TextView) listView.getChildAt(j); + assertNotNull(suggestion); + final Spanned spanned = (Spanned) suggestion.getText(); + assertNotNull(spanned); + + // Check that the suggestion item order is kept. + final String expectedText; + if (j < singleWordCandidates.length) { + expectedText = "abc " + singleWordCandidates[j] + " ghi"; + } else { + expectedText = multiWordCandidates[j - singleWordCandidates.length]; + } + assertEquals(expectedText, spanned.toString()); + + // Check that the text is highlighted with correct color and text size. + final TextAppearanceSpan[] taSpan = spanned.getSpans( + text.indexOf('d'), text.indexOf('f') + 1, TextAppearanceSpan.class); + assertEquals(1, taSpan.length); + TextPaint tp = new TextPaint(); + taSpan[0].updateDrawState(tp); + assertEquals(expectedHighlightTextColor, tp.getColor()); + assertEquals(expectedHighlightTextSize, tp.getTextSize(), 0f); + + // Check the correct part of the text is highlighted. + final int expectedStart; + final int expectedEnd; + if (j < singleWordCandidates.length) { + expectedStart = text.indexOf('d'); + expectedEnd = text.indexOf('f') + 1; + } else { + expectedStart = 0; + expectedEnd = text.length(); } + assertEquals(expectedStart, spanned.getSpanStart(taSpan[0])); + assertEquals(expectedEnd, spanned.getSpanEnd(taSpan[0])); } }); pressBack(); diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java index 41fa08b7e0e2..b4116681e723 100644 --- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java +++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java @@ -16,15 +16,6 @@ package android.widget; - -import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.Espresso.pressBack; -import static android.support.test.espresso.action.ViewActions.replaceText; -import static android.support.test.espresso.action.ViewActions.typeText; -import static android.support.test.espresso.assertion.ViewAssertions.matches; -import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; -import static android.support.test.espresso.matcher.ViewMatchers.withId; -import static android.support.test.espresso.matcher.ViewMatchers.withText; import static android.widget.espresso.ContextMenuUtils.assertContextMenuContainsItemDisabled; import static android.widget.espresso.ContextMenuUtils.assertContextMenuContainsItemEnabled; import static android.widget.espresso.ContextMenuUtils.assertContextMenuIsNotDisplayed; @@ -42,6 +33,15 @@ import static android.widget.espresso.TextViewActions.mouseTripleClickOnTextAtIn import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex; import static android.widget.espresso.TextViewAssertions.hasSelection; +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.Espresso.pressBack; +import static androidx.test.espresso.action.ViewActions.replaceText; +import static androidx.test.espresso.action.ViewActions.typeText; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + import android.app.Activity; import android.view.MotionEvent; import android.view.textclassifier.TextClassificationManager; diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java index 9d93421b1783..267a9c8601da 100644 --- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java +++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java @@ -16,15 +16,6 @@ package android.widget; -import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.action.ViewActions.click; -import static android.support.test.espresso.action.ViewActions.longClick; -import static android.support.test.espresso.action.ViewActions.pressKey; -import static android.support.test.espresso.action.ViewActions.replaceText; -import static android.support.test.espresso.assertion.ViewAssertions.matches; -import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; -import static android.support.test.espresso.matcher.ViewMatchers.withId; -import static android.support.test.espresso.matcher.ViewMatchers.withText; import static android.widget.espresso.CustomViewActions.longPressAtRelativeCoordinates; import static android.widget.espresso.DragHandleUtils.onHandleView; import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem; @@ -44,6 +35,16 @@ import static android.widget.espresso.TextViewAssertions.doesNotHaveStyledText; import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex; import static android.widget.espresso.TextViewAssertions.hasSelection; +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.action.ViewActions.longClick; +import static androidx.test.espresso.action.ViewActions.pressKey; +import static androidx.test.espresso.action.ViewActions.replaceText; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; @@ -60,7 +61,6 @@ import android.app.Activity; import android.app.Instrumentation; import android.content.ClipData; import android.content.ClipboardManager; -import android.support.test.espresso.action.EspressoKey; import android.support.test.uiautomator.UiDevice; import android.text.InputType; import android.text.Selection; @@ -79,6 +79,7 @@ import android.view.textclassifier.TextLinksParams; import android.widget.espresso.CustomViewActions.RelativeCoordinatesProvider; import androidx.test.InstrumentationRegistry; +import androidx.test.espresso.action.EspressoKey; import androidx.test.filters.MediumTest; import androidx.test.filters.Suppress; import androidx.test.rule.ActivityTestRule; diff --git a/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java b/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java index 02ee9be825be..0f8faa884e3e 100644 --- a/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java +++ b/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java @@ -16,28 +16,29 @@ package android.widget.espresso; -import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.action.ViewActions.click; -import static android.support.test.espresso.assertion.ViewAssertions.matches; -import static android.support.test.espresso.matcher.RootMatchers.withDecorView; -import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant; -import static android.support.test.espresso.matcher.ViewMatchers.hasFocus; -import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom; -import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; -import static android.support.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast; -import static android.support.test.espresso.matcher.ViewMatchers.isEnabled; -import static android.support.test.espresso.matcher.ViewMatchers.withText; +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.RootMatchers.withDecorView; +import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant; +import static androidx.test.espresso.matcher.ViewMatchers.hasFocus; +import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast; +import static androidx.test.espresso.matcher.ViewMatchers.isEnabled; +import static androidx.test.espresso.matcher.ViewMatchers.withText; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.not; -import android.support.test.espresso.NoMatchingRootException; -import android.support.test.espresso.NoMatchingViewException; -import android.support.test.espresso.ViewInteraction; -import android.support.test.espresso.matcher.ViewMatchers; import android.view.View; import android.widget.MenuPopupWindow.MenuDropDownListView; +import androidx.test.espresso.NoMatchingRootException; +import androidx.test.espresso.NoMatchingViewException; +import androidx.test.espresso.ViewInteraction; +import androidx.test.espresso.matcher.ViewMatchers; + import com.android.internal.view.menu.ListMenuItemView; import org.hamcrest.Description; diff --git a/core/tests/coretests/src/android/widget/espresso/CustomViewActions.java b/core/tests/coretests/src/android/widget/espresso/CustomViewActions.java index daf9e78c9b4e..217553ec480f 100644 --- a/core/tests/coretests/src/android/widget/espresso/CustomViewActions.java +++ b/core/tests/coretests/src/android/widget/espresso/CustomViewActions.java @@ -16,15 +16,15 @@ package android.widget.espresso; -import static android.support.test.espresso.action.ViewActions.actionWithAssertions; +import static androidx.test.espresso.action.ViewActions.actionWithAssertions; import android.view.View; -import android.support.test.espresso.ViewAction; -import android.support.test.espresso.action.CoordinatesProvider; -import android.support.test.espresso.action.GeneralClickAction; -import android.support.test.espresso.action.Press; -import android.support.test.espresso.action.Tap; +import androidx.test.espresso.ViewAction; +import androidx.test.espresso.action.CoordinatesProvider; +import androidx.test.espresso.action.GeneralClickAction; +import androidx.test.espresso.action.Press; +import androidx.test.espresso.action.Tap; import com.android.internal.util.Preconditions; diff --git a/core/tests/coretests/src/android/widget/espresso/DragAction.java b/core/tests/coretests/src/android/widget/espresso/DragAction.java index b2c8e38141b8..afdc4d379e5e 100644 --- a/core/tests/coretests/src/android/widget/espresso/DragAction.java +++ b/core/tests/coretests/src/android/widget/espresso/DragAction.java @@ -16,27 +16,30 @@ package android.widget.espresso; -import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom; -import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom; +import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed; + import static com.android.internal.util.Preconditions.checkNotNull; + import static org.hamcrest.Matchers.allOf; + import android.annotation.Nullable; import android.os.SystemClock; -import android.support.test.espresso.UiController; -import android.support.test.espresso.PerformException; -import android.support.test.espresso.ViewAction; -import android.support.test.espresso.action.CoordinatesProvider; -import android.support.test.espresso.action.MotionEvents; -import android.support.test.espresso.action.PrecisionDescriber; -import android.support.test.espresso.action.Swiper; -import android.support.test.espresso.util.HumanReadables; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; -import org.hamcrest.Matcher; +import androidx.test.espresso.PerformException; +import androidx.test.espresso.UiController; +import androidx.test.espresso.ViewAction; +import androidx.test.espresso.action.CoordinatesProvider; +import androidx.test.espresso.action.MotionEvents; +import androidx.test.espresso.action.PrecisionDescriber; +import androidx.test.espresso.action.Swiper; +import androidx.test.espresso.util.HumanReadables; +import org.hamcrest.Matcher; /** * Drags on a View using touch events.<br> diff --git a/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java b/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java index 1693e548f7bd..1b849b587b3e 100644 --- a/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java +++ b/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java @@ -16,22 +16,23 @@ package android.widget.espresso; -import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.assertion.ViewAssertions.matches; -import static android.support.test.espresso.matcher.RootMatchers.isPlatformPopup; -import static android.support.test.espresso.matcher.RootMatchers.withDecorView; -import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant; -import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom; -import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; -import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.RootMatchers.isPlatformPopup; +import static androidx.test.espresso.matcher.RootMatchers.withDecorView; +import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant; +import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withId; import static org.hamcrest.Matchers.allOf; -import android.support.test.espresso.NoMatchingRootException; -import android.support.test.espresso.NoMatchingViewException; -import android.support.test.espresso.ViewInteraction; import android.widget.Editor; +import androidx.test.espresso.NoMatchingRootException; +import androidx.test.espresso.NoMatchingViewException; +import androidx.test.espresso.ViewInteraction; + public final class DragHandleUtils { private DragHandleUtils() {} diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java index 0355f82506d7..d45d4b04af33 100644 --- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java +++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java @@ -16,30 +16,31 @@ package android.widget.espresso; -import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.action.ViewActions.click; -import static android.support.test.espresso.assertion.ViewAssertions.matches; -import static android.support.test.espresso.matcher.RootMatchers.isPlatformPopup; -import static android.support.test.espresso.matcher.RootMatchers.withDecorView; -import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant; -import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; -import static android.support.test.espresso.matcher.ViewMatchers.isRoot; -import static android.support.test.espresso.matcher.ViewMatchers.withId; -import static android.support.test.espresso.matcher.ViewMatchers.withTagValue; -import static android.support.test.espresso.matcher.ViewMatchers.withText; +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.RootMatchers.isPlatformPopup; +import static androidx.test.espresso.matcher.RootMatchers.withDecorView; +import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.isRoot; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withTagValue; +import static androidx.test.espresso.matcher.ViewMatchers.withText; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.is; -import android.support.test.espresso.NoMatchingRootException; -import android.support.test.espresso.NoMatchingViewException; -import android.support.test.espresso.UiController; -import android.support.test.espresso.ViewAction; -import android.support.test.espresso.ViewInteraction; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import androidx.test.espresso.NoMatchingRootException; +import androidx.test.espresso.NoMatchingViewException; +import androidx.test.espresso.UiController; +import androidx.test.espresso.ViewAction; +import androidx.test.espresso.ViewInteraction; + import com.android.internal.widget.FloatingToolbar; import org.hamcrest.Description; diff --git a/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java b/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java index b50d6f42b7be..f56af5a04dc3 100644 --- a/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java +++ b/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java @@ -16,18 +16,19 @@ package android.widget.espresso; -import android.support.test.espresso.UiController; -import android.support.test.espresso.ViewAction; -import android.support.test.espresso.action.CoordinatesProvider; -import android.support.test.espresso.action.MotionEvents; -import android.support.test.espresso.action.MotionEvents.DownResultHolder; -import android.support.test.espresso.action.Press; -import android.support.test.espresso.action.Tapper; import android.view.InputDevice; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; +import androidx.test.espresso.UiController; +import androidx.test.espresso.ViewAction; +import androidx.test.espresso.action.CoordinatesProvider; +import androidx.test.espresso.action.MotionEvents; +import androidx.test.espresso.action.MotionEvents.DownResultHolder; +import androidx.test.espresso.action.Press; +import androidx.test.espresso.action.Tapper; + import org.hamcrest.Matcher; /** diff --git a/core/tests/coretests/src/android/widget/espresso/MouseUiController.java b/core/tests/coretests/src/android/widget/espresso/MouseUiController.java index 022be7603dc1..abee7369414b 100644 --- a/core/tests/coretests/src/android/widget/espresso/MouseUiController.java +++ b/core/tests/coretests/src/android/widget/espresso/MouseUiController.java @@ -18,16 +18,19 @@ package android.widget.espresso; import static com.android.internal.util.Preconditions.checkNotNull; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - import android.annotation.IntDef; -import android.support.test.espresso.InjectEventSecurityException; -import android.support.test.espresso.UiController; +import android.os.SystemClock; import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; +import androidx.test.espresso.InjectEventSecurityException; +import androidx.test.espresso.UiController; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Iterator; + /** * Class to wrap an UiController to overwrite source of motion events to SOURCE_MOUSE. * Note that this doesn't change the tool type. @@ -71,6 +74,32 @@ public final class MouseUiController implements UiController { return mUiController.injectMotionEvent(event); } + /** + * Copied from latest {@link androidx.test.espresso.UiController}, since current + * {@link androidx.test.espresso.UiController#injectMotionEventSequence(Iterable)} seems not a + * default method. + */ + @Override + public boolean injectMotionEventSequence(Iterable<MotionEvent> events) + throws InjectEventSecurityException { + android.util.Log.w( + "UIC", + "Using default injectMotionEventSeq() - this may not inject a sequence properly. " + + "If wrapping UIController please override this method and delegate."); + Iterator<MotionEvent> mei = events.iterator(); + boolean success = true; + while (mei.hasNext()) { + MotionEvent me = mei.next(); + if (me.getEventTime() - SystemClock.uptimeMillis() > 10) { + // Because the loopMainThreadForAtLeast is overkill for waiting, intentially only + // call it with a smaller amount of milliseconds as best effort + loopMainThreadForAtLeast(10); + } + success &= injectMotionEvent(me); + } + return success; + } + @Override public boolean injectString(String str) throws InjectEventSecurityException { return mUiController.injectString(str); diff --git a/core/tests/coretests/src/android/widget/espresso/SuggestionsPopupwindowUtils.java b/core/tests/coretests/src/android/widget/espresso/SuggestionsPopupwindowUtils.java index b5a96ae6c49a..32c02403204f 100644 --- a/core/tests/coretests/src/android/widget/espresso/SuggestionsPopupwindowUtils.java +++ b/core/tests/coretests/src/android/widget/espresso/SuggestionsPopupwindowUtils.java @@ -16,36 +16,40 @@ package android.widget.espresso; -import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.assertion.ViewAssertions.matches; -import static android.support.test.espresso.matcher.RootMatchers.withDecorView; -import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant; -import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; -import static android.support.test.espresso.matcher.ViewMatchers.withId; -import static android.support.test.espresso.matcher.ViewMatchers.withText; +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.RootMatchers.withDecorView; +import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; -import org.hamcrest.Matcher; - -import android.support.test.espresso.NoMatchingRootException; -import android.support.test.espresso.NoMatchingViewException; -import android.support.test.espresso.UiController; -import android.support.test.espresso.ViewAction; -import android.support.test.espresso.ViewInteraction; -import android.support.test.espresso.action.GeneralLocation; -import android.support.test.espresso.action.Press; -import android.support.test.espresso.action.Tap; import android.view.View; +import androidx.test.espresso.NoMatchingRootException; +import androidx.test.espresso.NoMatchingViewException; +import androidx.test.espresso.UiController; +import androidx.test.espresso.ViewAction; +import androidx.test.espresso.ViewInteraction; +import androidx.test.espresso.action.GeneralLocation; +import androidx.test.espresso.action.Press; +import androidx.test.espresso.action.Tap; + +import org.hamcrest.Matcher; + public final class SuggestionsPopupwindowUtils { private static final int id = com.android.internal.R.id.suggestionWindowContainer; private SuggestionsPopupwindowUtils() {}; public static ViewInteraction onSuggestionsPopup() { + getInstrumentation().waitForIdleSync(); return onView(withId(id)).inRoot(withDecorView(hasDescendant(withId(id)))); } private static ViewInteraction onSuggestionsPopupItem(Matcher<View> matcher) { + getInstrumentation().waitForIdleSync(); return onView(matcher).inRoot(withDecorView(hasDescendant(withId(id)))); } diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java index b4c547e1fbb7..83ce67bd96ad 100644 --- a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java +++ b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java @@ -16,16 +16,9 @@ package android.widget.espresso; -import static android.support.test.espresso.action.ViewActions.actionWithAssertions; +import static androidx.test.espresso.action.ViewActions.actionWithAssertions; import android.graphics.Rect; -import android.support.test.espresso.PerformException; -import android.support.test.espresso.ViewAction; -import android.support.test.espresso.action.CoordinatesProvider; -import android.support.test.espresso.action.GeneralLocation; -import android.support.test.espresso.action.Press; -import android.support.test.espresso.action.Tap; -import android.support.test.espresso.util.HumanReadables; import android.text.Layout; import android.view.MotionEvent; import android.view.View; @@ -33,6 +26,14 @@ import android.widget.Editor; import android.widget.Editor.HandleView; import android.widget.TextView; +import androidx.test.espresso.PerformException; +import androidx.test.espresso.ViewAction; +import androidx.test.espresso.action.CoordinatesProvider; +import androidx.test.espresso.action.GeneralLocation; +import androidx.test.espresso.action.Press; +import androidx.test.espresso.action.Tap; +import androidx.test.espresso.util.HumanReadables; + /** * A collection of actions on a {@link android.widget.TextView}. */ diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java b/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java index 5ed69e06178b..d638da56a896 100644 --- a/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java +++ b/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java @@ -16,7 +16,7 @@ package android.widget.espresso; -import static android.support.test.espresso.matcher.ViewMatchers.assertThat; +import static androidx.test.espresso.matcher.ViewMatchers.assertThat; import static com.android.internal.util.Preconditions.checkNotNull; @@ -26,14 +26,15 @@ import static org.hamcrest.number.IsCloseTo.closeTo; import android.annotation.IntDef; import android.graphics.Rect; import android.graphics.drawable.Drawable; -import android.support.test.espresso.NoMatchingViewException; -import android.support.test.espresso.ViewAssertion; import android.text.Spanned; import android.text.TextUtils; import android.view.View; import android.widget.EditText; import android.widget.TextView; +import androidx.test.espresso.NoMatchingViewException; +import androidx.test.espresso.ViewAssertion; + import junit.framework.AssertionFailedError; import org.hamcrest.Matcher; diff --git a/core/tests/coretests/src/android/widget/espresso/ViewClickAction.java b/core/tests/coretests/src/android/widget/espresso/ViewClickAction.java index 8bce1b09517b..1e6447df334f 100644 --- a/core/tests/coretests/src/android/widget/espresso/ViewClickAction.java +++ b/core/tests/coretests/src/android/widget/espresso/ViewClickAction.java @@ -16,17 +16,18 @@ package android.widget.espresso; -import org.hamcrest.Matcher; - -import android.support.test.espresso.UiController; -import android.support.test.espresso.ViewAction; -import android.support.test.espresso.action.CoordinatesProvider; -import android.support.test.espresso.action.GeneralClickAction; -import android.support.test.espresso.action.PrecisionDescriber; -import android.support.test.espresso.action.Tapper; import android.view.View; import android.view.ViewConfiguration; +import androidx.test.espresso.UiController; +import androidx.test.espresso.ViewAction; +import androidx.test.espresso.action.CoordinatesProvider; +import androidx.test.espresso.action.GeneralClickAction; +import androidx.test.espresso.action.PrecisionDescriber; +import androidx.test.espresso.action.Tapper; + +import org.hamcrest.Matcher; + public final class ViewClickAction implements ViewAction { private final GeneralClickAction mGeneralClickAction; diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java index fe2fb8530cf6..7430c7ab23fc 100644 --- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java @@ -16,12 +16,12 @@ package com.android.internal.app; -import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.action.ViewActions.click; -import static android.support.test.espresso.assertion.ViewAssertions.matches; -import static android.support.test.espresso.matcher.ViewMatchers.isEnabled; -import static android.support.test.espresso.matcher.ViewMatchers.withId; -import static android.support.test.espresso.matcher.ViewMatchers.withText; +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isEnabled; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; import static com.android.internal.app.ResolverWrapperActivity.sOverrides; diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java index 3ddd8aaeba2c..64b7c2cce946 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java @@ -22,8 +22,9 @@ import static org.junit.Assert.assertTrue; import android.os.Process; import android.os.SystemClock; -import android.support.test.filters.LargeTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.LargeTest; import com.android.internal.os.KernelCpuThreadReader.ProcessCpuUsage; import com.android.internal.os.KernelCpuThreadReader.ThreadCpuUsage; diff --git a/core/tests/coretests/src/com/android/internal/statusbar/NotificationVisibilityTest.java b/core/tests/coretests/src/com/android/internal/statusbar/NotificationVisibilityTest.java index b740eccfb794..22432a82c232 100644 --- a/core/tests/coretests/src/com/android/internal/statusbar/NotificationVisibilityTest.java +++ b/core/tests/coretests/src/com/android/internal/statusbar/NotificationVisibilityTest.java @@ -19,8 +19,8 @@ package com.android.internal.statusbar; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 035ee108c6da..bb4765835890 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -58,6 +58,14 @@ prebuilt_etc { } prebuilt_etc { + name: "privapp_whitelist_com.android.dialer", + product_specific: true, + sub_dir: "permissions", + src: "com.android.dialer.xml", + filename_from_src: true, +} + +prebuilt_etc { name: "privapp_whitelist_com.android.launcher3", product_specific: true, sub_dir: "permissions", diff --git a/data/etc/com.android.dialer.xml b/data/etc/com.android.dialer.xml new file mode 100644 index 000000000000..ccdb21fa5040 --- /dev/null +++ b/data/etc/com.android.dialer.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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 + --> +<permissions> + <privapp-permissions package="com.android.dialer"> + <permission name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"/> + <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/> + <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/> + <permission name="android.permission.MODIFY_PHONE_STATE"/> + <permission name="android.permission.STATUS_BAR"/> + <permission name="android.permission.STOP_APP_SWITCHES"/> + <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/> + <permission name="com.android.voicemail.permission.WRITE_VOICEMAIL"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 2e7b9981f965..0f35918232b4 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -49,17 +49,6 @@ applications that come with the platform <permission name="android.permission.WRITE_MEDIA_STORAGE"/> </privapp-permissions> - <privapp-permissions package="com.android.dialer"> - <permission name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"/> - <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/> - <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/> - <permission name="android.permission.MODIFY_PHONE_STATE"/> - <permission name="android.permission.STATUS_BAR"/> - <permission name="android.permission.STOP_APP_SWITCHES"/> - <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/> - <permission name="com.android.voicemail.permission.WRITE_VOICEMAIL"/> - </privapp-permissions> - <privapp-permissions package="com.android.emergency"> <!-- Required to place emergency calls from emergency info screen. --> <permission name="android.permission.CALL_PRIVILEGED"/> @@ -301,6 +290,7 @@ applications that come with the platform <permission name="android.permission.MOUNT_FORMAT_FILESYSTEMS"/> <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <permission name="android.permission.MOVE_PACKAGE"/> + <permission name="android.permission.OBSERVE_APP_USAGE"/> <permission name="android.permission.PACKAGE_USAGE_STATS" /> <permission name="android.permission.POWER_SAVER" /> <permission name="android.permission.READ_FRAME_BUFFER"/> diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 080c5d5e8368..18f0cae4733c 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -25,6 +25,7 @@ import android.annotation.UnsupportedAppUsage; import android.annotation.WorkerThread; import android.content.res.ResourcesImpl; import android.hardware.HardwareBuffer; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.os.StrictMode; @@ -77,7 +78,7 @@ public final class Bitmap implements Parcelable { */ private boolean mRequestPremultiplied; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769491) private byte[] mNinePatchChunk; // may be null @UnsupportedAppUsage private NinePatch.InsetStruct mNinePatchInsets; // may be null diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index d0fe022616c5..15f53f286261 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -42,7 +42,13 @@ namespace uirenderer { namespace skiapipeline { SkiaVulkanPipeline::SkiaVulkanPipeline(renderthread::RenderThread& thread) - : SkiaPipeline(thread), mVkManager(thread.vulkanManager()) {} + : SkiaPipeline(thread), mVkManager(thread.vulkanManager()) { + thread.renderState().registerContextCallback(this); +} + +SkiaVulkanPipeline::~SkiaVulkanPipeline() { + mRenderThread.renderState().removeContextCallback(this); +} MakeCurrentResult SkiaVulkanPipeline::makeCurrent() { return MakeCurrentResult::AlreadyCurrent; @@ -53,6 +59,8 @@ Frame SkiaVulkanPipeline::getFrame() { "drawRenderNode called on a context with no surface!"); SkSurface* backBuffer = mVkManager.getBackbufferSurface(&mVkSurface); + LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr, + "drawRenderNode called on a context with an invalid surface"); if (backBuffer == nullptr) { SkDebugf("failed to get backbuffer"); return Frame(-1, -1, 0); @@ -153,6 +161,13 @@ sk_sp<Bitmap> SkiaVulkanPipeline::allocateHardwareBitmap(renderthread::RenderThr return nullptr; } +void SkiaVulkanPipeline::onContextDestroyed() { + if (mVkSurface) { + mVkManager.destroySurface(mVkSurface); + mVkSurface = nullptr; + } +} + } /* namespace skiapipeline */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index 934307636913..2c24edddb7e0 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -19,14 +19,16 @@ #include "SkiaPipeline.h" #include "renderthread/VulkanManager.h" +#include "renderstate/RenderState.h" + namespace android { namespace uirenderer { namespace skiapipeline { -class SkiaVulkanPipeline : public SkiaPipeline { +class SkiaVulkanPipeline : public SkiaPipeline, public IGpuContextCallback { public: explicit SkiaVulkanPipeline(renderthread::RenderThread& thread); - virtual ~SkiaVulkanPipeline() {} + virtual ~SkiaVulkanPipeline(); renderthread::MakeCurrentResult makeCurrent() override; renderthread::Frame getFrame() override; @@ -49,6 +51,9 @@ public: static sk_sp<Bitmap> allocateHardwareBitmap(renderthread::RenderThread& thread, SkBitmap& skBitmap); +protected: + void onContextDestroyed() override; + private: renderthread::VulkanManager& mVkManager; renderthread::VulkanSurface* mVkSurface = nullptr; diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp index 2f8d381f15f5..fe2d41ef630b 100644 --- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp @@ -43,7 +43,9 @@ VkFunctorDrawHandler::VkFunctorDrawHandler(sp<WebViewFunctor::Handle> functor_ha , mImageInfo(image_info) {} VkFunctorDrawHandler::~VkFunctorDrawHandler() { - mFunctorHandle->postDrawVk(); + if (mDrawn) { + mFunctorHandle->postDrawVk(); + } } void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) { @@ -77,6 +79,7 @@ void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) { params.format = vulkan_info.fFormat; mFunctorHandle->drawVk(params); + mDrawn = true; vulkan_info.fDrawBounds->offset.x = mClip.fLeft; vulkan_info.fDrawBounds->offset.y = mClip.fTop; diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.h b/libs/hwui/pipeline/skia/VkFunctorDrawable.h index 1a53c8fd55db..d3f97773b91d 100644 --- a/libs/hwui/pipeline/skia/VkFunctorDrawable.h +++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.h @@ -44,6 +44,8 @@ private: const SkMatrix mMatrix; const SkIRect mClip; const SkImageInfo mImageInfo; + + bool mDrawn = false; }; /** diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 8bef35915c4d..3b37c83a6acc 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -203,11 +203,17 @@ void RenderThread::requireGlContext() { void RenderThread::destroyRenderingContext() { mFunctorManager.onContextDestroyed(); - if (mEglManager->hasEglContext()) { - setGrContext(nullptr); - mEglManager->destroy(); + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) { + if (mEglManager->hasEglContext()) { + setGrContext(nullptr); + mEglManager->destroy(); + } + } else { + if (vulkanManager().hasVkContext()) { + setGrContext(nullptr); + vulkanManager().destroy(); + } } - vulkanManager().destroy(); } void RenderThread::dumpGraphicsMemory(int fd) { diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 582d51e6af94..90397fddf618 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -89,7 +89,7 @@ void VulkanManager::destroy() { mPhysicalDeviceFeatures2 = {}; } -bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFeatures2& features) { +void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFeatures2& features) { VkResult err; constexpr VkApplicationInfo app_info = { @@ -107,15 +107,11 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe uint32_t extensionCount = 0; err = mEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); - if (VK_SUCCESS != err) { - return false; - } + LOG_ALWAYS_FATAL_IF(VK_SUCCESS != err); std::unique_ptr<VkExtensionProperties[]> extensions( new VkExtensionProperties[extensionCount]); err = mEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.get()); - if (VK_SUCCESS != err) { - return false; - } + LOG_ALWAYS_FATAL_IF(VK_SUCCESS != err); bool hasKHRSurfaceExtension = false; bool hasKHRAndroidSurfaceExtension = false; for (uint32_t i = 0; i < extensionCount; ++i) { @@ -127,10 +123,7 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe hasKHRAndroidSurfaceExtension = true; } } - if (!hasKHRSurfaceExtension || !hasKHRAndroidSurfaceExtension) { - this->destroy(); - return false; - } + LOG_ALWAYS_FATAL_IF(!hasKHRSurfaceExtension || !hasKHRAndroidSurfaceExtension); } const VkInstanceCreateInfo instance_create = { @@ -146,10 +139,7 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe GET_PROC(CreateInstance); err = mCreateInstance(&instance_create, nullptr, &mInstance); - if (err < 0) { - this->destroy(); - return false; - } + LOG_ALWAYS_FATAL_IF(err < 0); GET_INST_PROC(DestroyInstance); GET_INST_PROC(EnumeratePhysicalDevices); @@ -166,39 +156,23 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe GET_INST_PROC(GetPhysicalDeviceSurfacePresentModesKHR); uint32_t gpuCount; - err = mEnumeratePhysicalDevices(mInstance, &gpuCount, nullptr); - if (err) { - this->destroy(); - return false; - } - if (!gpuCount) { - this->destroy(); - return false; - } + LOG_ALWAYS_FATAL_IF(mEnumeratePhysicalDevices(mInstance, &gpuCount, nullptr)); + LOG_ALWAYS_FATAL_IF(!gpuCount); // Just returning the first physical device instead of getting the whole array. Since there // should only be one device on android. gpuCount = 1; err = mEnumeratePhysicalDevices(mInstance, &gpuCount, &mPhysicalDevice); // VK_INCOMPLETE is returned when the count we provide is less than the total device count. - if (err && VK_INCOMPLETE != err) { - this->destroy(); - return false; - } + LOG_ALWAYS_FATAL_IF(err && VK_INCOMPLETE != err); VkPhysicalDeviceProperties physDeviceProperties; mGetPhysicalDeviceProperties(mPhysicalDevice, &physDeviceProperties); - if (physDeviceProperties.apiVersion < VK_MAKE_VERSION(1, 1, 0)) { - this->destroy(); - return false; - } + LOG_ALWAYS_FATAL_IF(physDeviceProperties.apiVersion < VK_MAKE_VERSION(1, 1, 0)); // query to get the initial queue props size uint32_t queueCount; mGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, nullptr); - if (!queueCount) { - this->destroy(); - return false; - } + LOG_ALWAYS_FATAL_IF(!queueCount); // now get the actual queue props std::unique_ptr<VkQueueFamilyProperties[]> queueProps(new VkQueueFamilyProperties[queueCount]); @@ -212,10 +186,7 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe break; } } - if (mGraphicsQueueIndex == queueCount) { - this->destroy(); - return false; - } + LOG_ALWAYS_FATAL_IF(mGraphicsQueueIndex == queueCount); // All physical devices and queue families on Android must be capable of // presentation with any native window. So just use the first one. @@ -225,18 +196,12 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe uint32_t extensionCount = 0; err = mEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, &extensionCount, nullptr); - if (VK_SUCCESS != err) { - this->destroy(); - return false; - } + LOG_ALWAYS_FATAL_IF(VK_SUCCESS != err); std::unique_ptr<VkExtensionProperties[]> extensions( new VkExtensionProperties[extensionCount]); err = mEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, &extensionCount, extensions.get()); - if (VK_SUCCESS != err) { - this->destroy(); - return false; - } + LOG_ALWAYS_FATAL_IF(VK_SUCCESS != err); bool hasKHRSwapchainExtension = false; for (uint32_t i = 0; i < extensionCount; ++i) { mDeviceExtensions.push_back(extensions[i].extensionName); @@ -244,10 +209,7 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe hasKHRSwapchainExtension = true; } } - if (!hasKHRSwapchainExtension) { - this->destroy(); - return false; - } + LOG_ALWAYS_FATAL_IF(!hasKHRSwapchainExtension); } auto getProc = [] (const char* proc_name, VkInstance instance, VkDevice device) { @@ -259,10 +221,7 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe grExtensions.init(getProc, mInstance, mPhysicalDevice, mInstanceExtensions.size(), mInstanceExtensions.data(), mDeviceExtensions.size(), mDeviceExtensions.data()); - if (!grExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) { - this->destroy(); - return false; - } + LOG_ALWAYS_FATAL_IF(!grExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)); memset(&features, 0, sizeof(VkPhysicalDeviceFeatures2)); features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; @@ -332,11 +291,7 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe nullptr, // ppEnabledFeatures }; - err = mCreateDevice(mPhysicalDevice, &deviceInfo, nullptr, &mDevice); - if (err) { - this->destroy(); - return false; - } + LOG_ALWAYS_FATAL_IF(mCreateDevice(mPhysicalDevice, &deviceInfo, nullptr, &mDevice)); GET_DEV_PROC(GetDeviceQueue); GET_DEV_PROC(DeviceWaitIdle); @@ -366,8 +321,6 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe GET_DEV_PROC(DestroyFence); GET_DEV_PROC(WaitForFences); GET_DEV_PROC(ResetFences); - - return true; } void VulkanManager::initialize() { @@ -381,7 +334,7 @@ void VulkanManager::initialize() { LOG_ALWAYS_FATAL_IF(instanceVersion < VK_MAKE_VERSION(1, 1, 0)); GrVkExtensions extensions; - LOG_ALWAYS_FATAL_IF(!this->setupDevice(extensions, mPhysicalDeviceFeatures2)); + this->setupDevice(extensions, mPhysicalDeviceFeatures2); mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue); @@ -419,7 +372,7 @@ void VulkanManager::initialize() { if (!setupDummyCommandBuffer()) { this->destroy(); - return; + // Pass through will crash on next line. } LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE); @@ -520,6 +473,9 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface** surfaceOut) { destroySurface(surface); *surfaceOut = createSurface(window, colorMode, colorSpace, colorType); surface = *surfaceOut; + if (!surface) { + return nullptr; + } } VulkanSurface::BackbufferInfo* backbuffer = getAvailableBackbuffer(surface); diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index 6426fe2808b7..1fe6c65b35b8 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -151,7 +151,7 @@ private: // Sets up the VkInstance and VkDevice objects. Also fills out the passed in // VkPhysicalDeviceFeatures struct. - bool setupDevice(GrVkExtensions&, VkPhysicalDeviceFeatures2&); + void setupDevice(GrVkExtensions&, VkPhysicalDeviceFeatures2&); void destroyBuffers(VulkanSurface* surface); diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index e795b8119760..e91012fe0afe 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -85,12 +85,13 @@ interface ILocationManager boolean stopGnssBatch(); boolean injectLocation(in Location location); - // --- deprecated --- List<String> getAllProviders(); List<String> getProviders(in Criteria criteria, boolean enabledOnly); String getBestProvider(in Criteria criteria, boolean enabledOnly); ProviderProperties getProviderProperties(String provider); String getNetworkProviderPackage(); + boolean isProviderPackage(String packageName); + void setLocationControllerExtraPackage(String packageName); String getLocationControllerExtraPackage(); void setLocationControllerExtraPackageEnabled(boolean enabled); diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 63b57d166f1f..5aba22f4c6b1 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -2425,6 +2425,22 @@ public class LocationManager { } /** + * Returns true if the given package name matches a location provider package, and false + * otherwise. + * + * @hide + */ + @SystemApi + public boolean isProviderPackage(String packageName) { + try { + return mService.isProviderPackage(packageName); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + return false; + } + } + + /** * Set the extra location controller package for location services on the device. * * @hide diff --git a/location/java/com/android/internal/location/ILocationProviderManager.aidl b/location/java/com/android/internal/location/ILocationProviderManager.aidl index b1b8f0c7c3f7..79166ae3a9b0 100644 --- a/location/java/com/android/internal/location/ILocationProviderManager.aidl +++ b/location/java/com/android/internal/location/ILocationProviderManager.aidl @@ -26,6 +26,8 @@ import com.android.internal.location.ProviderProperties; */ interface ILocationProviderManager { + void onSetAdditionalProviderPackages(in List<String> packageNames); + void onSetEnabled(boolean enabled); void onSetProperties(in ProviderProperties properties); diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt index c7212183a37e..67d64965ac96 100644 --- a/location/lib/api/current.txt +++ b/location/lib/api/current.txt @@ -19,6 +19,7 @@ package com.android.location.provider { method protected boolean onSendExtraCommand(@Nullable String, @Nullable android.os.Bundle); method protected abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource); method public void reportLocation(android.location.Location); + method public void setAdditionalProviderPackages(java.util.List<java.lang.String>); method public void setEnabled(boolean); method public void setProperties(com.android.location.provider.ProviderPropertiesUnbundled); field public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation"; diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java index 5bcec92c4fba..7cd7207c26a0 100644 --- a/location/lib/java/com/android/location/provider/LocationProviderBase.java +++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java @@ -36,6 +36,8 @@ import com.android.internal.location.ProviderRequest; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; /** * Base class for location providers implemented as unbundled services. @@ -88,6 +90,7 @@ public abstract class LocationProviderBase { @Nullable private volatile ILocationProviderManager mManager; private volatile ProviderProperties mProperties; private volatile boolean mEnabled; + private final ArrayList<String> mAdditionalProviderPackages; public LocationProviderBase(String tag, ProviderPropertiesUnbundled properties) { mTag = tag; @@ -99,6 +102,7 @@ public abstract class LocationProviderBase { mManager = null; mProperties = properties.getProviderProperties(); mEnabled = true; + mAdditionalProviderPackages = new ArrayList<>(0); } public IBinder getBinder() { @@ -160,6 +164,29 @@ public abstract class LocationProviderBase { } /** + * Sets a list of additional packages that should be considered as part of this location + * provider for the purposes of generating locations. This should generally only be used when + * another package may issue location requests on behalf of this package in the course of + * providing location. This will inform location services to treat the other packages as + * location providers as well. + */ + public void setAdditionalProviderPackages(List<String> packageNames) { + synchronized (mBinder) { + mAdditionalProviderPackages.clear(); + mAdditionalProviderPackages.addAll(packageNames); + } + + ILocationProviderManager manager = mManager; + if (manager != null) { + try { + manager.onSetAdditionalProviderPackages(mAdditionalProviderPackages); + } catch (RemoteException | RuntimeException e) { + Log.w(mTag, e); + } + } + } + + /** * Returns true if this provider has been set as enabled. This will be true unless explicitly * set otherwise. */ @@ -275,6 +302,9 @@ public abstract class LocationProviderBase { public void setLocationProviderManager(ILocationProviderManager manager) { synchronized (mBinder) { try { + if (!mAdditionalProviderPackages.isEmpty()) { + manager.onSetAdditionalProviderPackages(mAdditionalProviderPackages); + } manager.onSetProperties(mProperties); manager.onSetEnabled(mEnabled); } catch (RemoteException e) { diff --git a/media/Android.bp b/media/Android.bp index 1aefebe0a378..141d415cbaf3 100644 --- a/media/Android.bp +++ b/media/Android.bp @@ -102,7 +102,8 @@ metalava_updatable_media_args = " --error UnhiddenSystemApi " + "--hide MissingPermission --hide BroadcastBehavior " + "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " + "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo " + - "--hide HiddenTypedefConstant " + "--hide HiddenTypedefConstant --show-annotation android.annotation.SystemApi " + + " --show-annotation android.annotation.TestApi " droidstubs { name: "updatable-media-stubs", @@ -110,8 +111,7 @@ droidstubs { ":updatable-media-srcs-without-aidls", ":framework-media-annotation-srcs", ], - args: metalava_updatable_media_args + " --show-annotation android.annotation.SystemApi " + - " --show-annotation android.annotation.TestApi ", + args: metalava_updatable_media_args, // Ideally, sdk_version here should be "current_system", but "current - 1" is used // to avoid dependency cycle with framework. sdk_version: "28", diff --git a/media/apex/java/android/media/MediaPlayer2.java b/media/apex/java/android/media/MediaPlayer2.java index 925ca0dd8a7d..f7afa31e6173 100644 --- a/media/apex/java/android/media/MediaPlayer2.java +++ b/media/apex/java/android/media/MediaPlayer2.java @@ -86,6 +86,12 @@ import java.util.concurrent.atomic.AtomicLong; /** * MediaPlayer2 class can be used to control playback of audio/video files and streams. * + * <p> + * This API is not generally intended for third party application developers. + * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> + * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a> + * for consistent behavior across all devices. + * * <p>Topics covered here are: * <ol> * <li><a href="#PlayerStates">Player states</a> diff --git a/media/java/android/media/AudioRecordingConfiguration.java b/media/java/android/media/AudioRecordingConfiguration.java index 1d763ced3ca0..0af47e8902a3 100644 --- a/media/java/android/media/AudioRecordingConfiguration.java +++ b/media/java/android/media/AudioRecordingConfiguration.java @@ -402,7 +402,7 @@ public final class AudioRecordingConfiguration implements Parcelable { && (mClientPortId == that.mClientPortId) && (mClientSilenced == that.mClientSilenced) && (mDeviceSource == that.mDeviceSource) - && (mClientEffects.equals(that.mClientEffects)) - && (mDeviceEffects.equals(that.mDeviceEffects))); + && (Arrays.equals(mClientEffects, that.mClientEffects)) + && (Arrays.equals(mDeviceEffects, that.mDeviceEffects))); } } diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java index 4f74ec911499..963b1d1504e2 100644 --- a/media/java/android/media/CamcorderProfile.java +++ b/media/java/android/media/CamcorderProfile.java @@ -97,9 +97,33 @@ public class CamcorderProfile */ public static final int QUALITY_2160P = 8; + /** + * Quality level corresponding to the VGA (640 x 480) resolution. + * @hide + */ + public static final int QUALITY_VGA = 9; + + /** + * Quality level corresponding to 4k-DCI (4096 x 2160) resolution. + * @hide + */ + public static final int QUALITY_4KDCI = 10; + + /** + * Quality level corresponding to QHD (2560 x 1440) resolution + * @hide + */ + public static final int QUALITY_QHD = 11; + + /** + * Quality level corresponding to 2K (2048 x 1080) resolution + * @hide + */ + public static final int QUALITY_2K = 12; + // Start and end of quality list private static final int QUALITY_LIST_START = QUALITY_LOW; - private static final int QUALITY_LIST_END = QUALITY_2160P; + private static final int QUALITY_LIST_END = QUALITY_2K; /** * Time lapse quality level corresponding to the lowest available resolution. @@ -146,9 +170,34 @@ public class CamcorderProfile */ public static final int QUALITY_TIME_LAPSE_2160P = 1008; + /** + * Time lapse quality level corresponding to the VGA (640 x 480) resolution. + * @hide + */ + public static final int QUALITY_TIME_LAPSE_VGA = 1009; + + /** + * Time lapse quality level corresponding to the 4k-DCI (4096 x 2160) resolution. + * @hide + */ + public static final int QUALITY_TIME_LAPSE_4KDCI = 1010; + + /** + * Time lapse quality level corresponding to the QHD (2560 x 1440) resolution. + * @hide + */ + public static final int QUALITY_TIME_LAPSE_QHD = 1011; + + /** + * Time lapse quality level corresponding to the 2K (2048 x 1080) resolution. + * @hide + */ + public static final int QUALITY_TIME_LAPSE_2K = 1012; + + // Start and end of timelapse quality list private static final int QUALITY_TIME_LAPSE_LIST_START = QUALITY_TIME_LAPSE_LOW; - private static final int QUALITY_TIME_LAPSE_LIST_END = QUALITY_TIME_LAPSE_2160P; + private static final int QUALITY_TIME_LAPSE_LIST_END = QUALITY_TIME_LAPSE_2K; /** * High speed ( >= 100fps) quality level corresponding to the lowest available resolution. @@ -204,9 +253,27 @@ public class CamcorderProfile */ public static final int QUALITY_HIGH_SPEED_2160P = 2005; + /** + * High speed ( >= 100fps) quality level corresponding to the CIF (352 x 288) + * @hide + */ + public static final int QUALITY_HIGH_SPEED_CIF = 2006; + + /** + * High speed ( >= 100fps) quality level corresponding to the VGA (640 x 480) + * @hide + */ + public static final int QUALITY_HIGH_SPEED_VGA = 2007; + + /** + * High speed ( >= 100fps) quality level corresponding to the 4K-DCI (4096 x 2160) + * @hide + */ + public static final int QUALITY_HIGH_SPEED_4KDCI = 2008; + // Start and end of high speed quality list private static final int QUALITY_HIGH_SPEED_LIST_START = QUALITY_HIGH_SPEED_LOW; - private static final int QUALITY_HIGH_SPEED_LIST_END = QUALITY_HIGH_SPEED_2160P; + private static final int QUALITY_HIGH_SPEED_LIST_END = QUALITY_HIGH_SPEED_4KDCI; /** * Default recording duration in seconds before the session is terminated. diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java index ce631a43327b..cf5711d73278 100644 --- a/media/java/android/media/MediaCas.java +++ b/media/java/android/media/MediaCas.java @@ -18,8 +18,12 @@ package android.media; import android.annotation.NonNull; import android.annotation.Nullable; -import android.hardware.cas.V1_0.*; +import android.hardware.cas.V1_0.HidlCasPluginDescriptor; +import android.hardware.cas.V1_1.ICas; +import android.hardware.cas.V1_1.ICasListener; +import android.hardware.cas.V1_1.IMediaCasService; import android.media.MediaCasException.*; +import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.IHwBinder; @@ -128,6 +132,9 @@ public final class MediaCas implements AutoCloseable { private class EventHandler extends Handler { private static final int MSG_CAS_EVENT = 0; + private static final int MSG_CAS_SESSION_EVENT = 1; + private static final String SESSION_KEY = "sessionId"; + private static final String DATA_KEY = "data"; public EventHandler(Looper looper) { super(looper); @@ -138,6 +145,12 @@ public final class MediaCas implements AutoCloseable { if (msg.what == MSG_CAS_EVENT) { mListener.onEvent(MediaCas.this, msg.arg1, msg.arg2, toBytes((ArrayList<Byte>) msg.obj)); + } else if (msg.what == MSG_CAS_SESSION_EVENT) { + Bundle bundle = msg.getData(); + ArrayList<Byte> sessionId = toByteArray(bundle.getByteArray(SESSION_KEY)); + mListener.onSessionEvent(MediaCas.this, + createFromSessionId(sessionId), msg.arg1, msg.arg2, + bundle.getByteArray(DATA_KEY)); } } } @@ -149,6 +162,20 @@ public final class MediaCas implements AutoCloseable { mEventHandler.sendMessage(mEventHandler.obtainMessage( EventHandler.MSG_CAS_EVENT, event, arg, data)); } + @Override + public void onSessionEvent(@NonNull ArrayList<Byte> sessionId, + int event, int arg, @Nullable ArrayList<Byte> data) + throws RemoteException { + Message msg = mEventHandler.obtainMessage(); + msg.what = EventHandler.MSG_CAS_SESSION_EVENT; + msg.arg1 = event; + msg.arg2 = arg; + Bundle bundle = new Bundle(); + bundle.putByteArray(EventHandler.SESSION_KEY, toBytes(sessionId)); + bundle.putByteArray(EventHandler.DATA_KEY, toBytes(data)); + msg.setData(bundle); + mEventHandler.sendMessage(msg); + } }; /** @@ -222,6 +249,20 @@ public final class MediaCas implements AutoCloseable { } /** + * Query if an object equal current Session object. + * + * @param obj an object to compare to current Session object. + * + * @return Whether input object equal current Session object. + */ + public boolean equals(Object obj) { + if (obj instanceof Session) { + return mSessionId.equals(((Session) obj).mSessionId); + } + return false; + } + + /** * Set the private data for a session. * * @param data byte array of the private data. @@ -282,6 +323,30 @@ public final class MediaCas implements AutoCloseable { } /** + * Send a session event to a CA system. The format of the event is + * scheme-specific and is opaque to the framework. + * + * @param event an integer denoting a scheme-specific event to be sent. + * @param arg a scheme-specific integer argument for the event. + * @param data a byte array containing scheme-specific data for the event. + * + * @throws IllegalStateException if the MediaCas instance is not valid. + * @throws MediaCasException for CAS-specific errors. + * @throws MediaCasStateException for CAS-specific state exceptions. + */ + public void sendSessionEvent(int event, int arg, @Nullable byte[] data) + throws MediaCasException { + validateInternalStates(); + + try { + MediaCasException.throwExceptionIfNeeded( + mICas.sendSessionEvent(mSessionId, event, arg, toByteArray(data))); + } catch (RemoteException e) { + cleanupAndRethrowIllegalState(); + } + } + + /** * Close the session. * * @throws IllegalStateException if the MediaCas instance is not valid. @@ -362,7 +427,7 @@ public final class MediaCas implements AutoCloseable { */ public MediaCas(int CA_system_id) throws UnsupportedCasException { try { - mICas = getService().createPlugin(CA_system_id, mBinder); + mICas = getService().createPluginExt(CA_system_id, mBinder); } catch(Exception e) { Log.e(TAG, "Failed to create plugin: " + e); mICas = null; @@ -388,13 +453,28 @@ public final class MediaCas implements AutoCloseable { /** * Notify the listener of a scheme-specific event from the CA system. * - * @param MediaCas the MediaCas object to receive this event. + * @param mediaCas the MediaCas object to receive this event. + * @param event an integer whose meaning is scheme-specific. + * @param arg an integer whose meaning is scheme-specific. + * @param data a byte array of data whose format and meaning are + * scheme-specific. + */ + void onEvent(@NonNull MediaCas mediaCas, int event, int arg, @Nullable byte[] data); + + /** + * Notify the listener of a scheme-specific session event from CA system. + * + * @param mediaCas the MediaCas object to receive this event. + * @param session session object which the event is for. * @param event an integer whose meaning is scheme-specific. * @param arg an integer whose meaning is scheme-specific. * @param data a byte array of data whose format and meaning are * scheme-specific. */ - void onEvent(MediaCas MediaCas, int event, int arg, @Nullable byte[] data); + default void onSessionEvent(@NonNull MediaCas mediaCas, @NonNull Session session, + int event, int arg, @Nullable byte[] data) { + Log.d(TAG, "Received MediaCas Session event"); + } } /** @@ -543,7 +623,7 @@ public final class MediaCas implements AutoCloseable { } } - /** + /** * Initiate a provisioning operation for a CA system. * * @param provisionString string containing information needed for the @@ -603,4 +683,4 @@ public final class MediaCas implements AutoCloseable { protected void finalize() { close(); } -}
\ No newline at end of file +} diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index 0c3d6255f871..c6c2fdd00674 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -3373,6 +3373,8 @@ final public class MediaCodec { /** * Change a video encoder's target bitrate on the fly. The value is an * Integer object containing the new bitrate in bps. + * + * @see #setParameters(Bundle) */ public static final String PARAMETER_KEY_VIDEO_BITRATE = "video-bitrate"; @@ -3384,12 +3386,43 @@ final public class MediaCodec { * input-side of the encoder in that case. * The value is an Integer object containing the value 1 to suspend * or the value 0 to resume. + * + * @see #setParameters(Bundle) */ public static final String PARAMETER_KEY_SUSPEND = "drop-input-frames"; /** + * When {@link #PARAMETER_KEY_SUSPEND} is present, the client can also + * optionally use this key to specify the timestamp (in micro-second) + * at which the suspend/resume operation takes effect. + * + * Note that the specified timestamp must be greater than or equal to the + * timestamp of any previously queued suspend/resume operations. + * + * The value is a long int, indicating the timestamp to suspend/resume. + * + * @see #setParameters(Bundle) + */ + public static final String PARAMETER_KEY_SUSPEND_TIME = "drop-start-time-us"; + + /** + * Specify an offset (in micro-second) to be added on top of the timestamps + * onward. A typical use case is to apply an adjust to the timestamps after + * a period of pause by the user. + * + * This parameter can only be used on an encoder in "surface-input" mode. + * + * The value is a long int, indicating the timestamp offset to be applied. + * + * @see #setParameters(Bundle) + */ + public static final String PARAMETER_KEY_OFFSET_TIME = "time-offset-us"; + + /** * Request that the encoder produce a sync frame "soon". * Provide an Integer with the value 0. + * + * @see #setParameters(Bundle) */ public static final String PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync"; diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index c82b5f6f12a1..4ca0216c80f2 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -463,6 +463,63 @@ public final class MediaFormat { = "repeat-previous-frame-after"; /** + * Instruct the video encoder in "surface-input" mode to drop excessive + * frames from the source, so that the input frame rate to the encoder + * does not exceed the specified fps. + * + * The associated value is a float, representing the max frame rate to + * feed the encoder at. + * + * @hide + */ + public static final String KEY_MAX_FPS_TO_ENCODER + = "max-fps-to-encoder"; + + /** + * Instruct the video encoder in "surface-input" mode to limit the gap of + * timestamp between any two adjacent frames fed to the encoder to the + * specified amount (in micro-second). + * + * The associated value is a long int. When positive, it represents the max + * timestamp gap between two adjacent frames fed to the encoder. When negative, + * the absolute value represents a fixed timestamp gap between any two adjacent + * frames fed to the encoder. Note that this will also apply even when the + * original timestamp goes backward in time. Under normal conditions, such frames + * would be dropped and not sent to the encoder. + * + * The output timestamp will be restored to the original timestamp and will + * not be affected. + * + * This is used in some special scenarios where input frames arrive sparingly + * but it's undesirable to allocate more bits to any single frame, or when it's + * important to ensure all frames are captured (rather than captured in the + * correct order). + * + * @hide + */ + public static final String KEY_MAX_PTS_GAP_TO_ENCODER + = "max-pts-gap-to-encoder"; + + /** + * If specified when configuring a video encoder that's in "surface-input" + * mode, it will instruct the encoder to put the surface source in suspended + * state when it's connected. No video frames will be accepted until a resume + * operation (see {@link MediaCodec#PARAMETER_KEY_SUSPEND}), optionally with + * timestamp specified via {@link MediaCodec#PARAMETER_KEY_SUSPEND_TIME}, is + * received. + * + * The value is an integer, with 1 indicating to create with the surface + * source suspended, or 0 otherwise. The default value is 0. + * + * If this key is not set or set to 0, the surface source will accept buffers + * as soon as it's connected to the encoder (although they may not be encoded + * immediately). This key can be used when the client wants to prepare the + * encoder session in advance, but do not want to accept buffers immediately. + */ + public static final String KEY_CREATE_INPUT_SURFACE_SUSPENDED + = "create-input-buffers-suspended"; + + /** * If specified when configuring a video decoder rendering to a surface, * causes the decoder to output "blank", i.e. black frames to the surface * when stopped to clear out any previously displayed contents. diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp index 5f513207bd5f..e08dab48ce39 100644 --- a/media/jni/soundpool/SoundPool.cpp +++ b/media/jni/soundpool/SoundPool.cpp @@ -26,7 +26,6 @@ #include <media/AudioTrack.h> #include "SoundPool.h" #include "SoundPoolThread.h" -#include <media/AudioPolicyHelper.h> #include <media/NdkMediaCodec.h> #include <media/NdkMediaExtractor.h> #include <media/NdkMediaFormat.h> @@ -746,7 +745,8 @@ void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftV // initialize track size_t afFrameCount; uint32_t afSampleRate; - audio_stream_type_t streamType = audio_attributes_to_stream_type(mSoundPool->attributes()); + audio_stream_type_t streamType = + AudioSystem::attributesToStreamType(*mSoundPool->attributes()); if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) { afFrameCount = kDefaultFrameCount; } diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java index b7d8492b09dd..b6b229c770b2 100644 --- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java +++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java @@ -105,6 +105,7 @@ public class Assistant extends NotificationAssistantService { protected AssistantSettings.Factory mSettingsFactory = AssistantSettings.FACTORY; @VisibleForTesting protected AssistantSettings mSettings; + private SmsHelper mSmsHelper; public Assistant() { } @@ -122,6 +123,18 @@ public class Assistant extends NotificationAssistantService { mAgingHelper = new AgingHelper(getContext(), mNotificationCategorizer, new AgingCallback()); + mSmsHelper = new SmsHelper(this); + mSmsHelper.initialize(); + } + + @Override + public void onDestroy() { + // This null check is only for the unit tests as ServiceTestCase.tearDown calls onDestroy + // without having first called onCreate. + if (mSmsHelper != null) { + mSmsHelper.destroy(); + } + super.onDestroy(); } private void loadFile() { @@ -215,7 +228,7 @@ public class Assistant extends NotificationAssistantService { return null; } NotificationEntry entry = - new NotificationEntry(mPackageManager, sbn, channel, SmsHelper.getInstance(this)); + new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper); SmartActionsHelper.SmartSuggestions suggestions = mSmartActionsHelper.suggest(entry); return createEnqueuedNotificationAdjustment( entry, suggestions.actions, suggestions.replies); @@ -262,7 +275,7 @@ public class Assistant extends NotificationAssistantService { Ranking ranking = getRanking(sbn.getKey(), rankingMap); if (ranking != null && ranking.getChannel() != null) { NotificationEntry entry = new NotificationEntry(mPackageManager, - sbn, ranking.getChannel(), SmsHelper.getInstance(this)); + sbn, ranking.getChannel(), mSmsHelper); String key = getKey( sbn.getPackageName(), sbn.getUserId(), ranking.getChannel().getId()); ChannelImpressions ci = mkeyToImpressions.getOrDefault(key, @@ -398,7 +411,6 @@ public class Assistant extends NotificationAssistantService { @Override public void onListenerConnected() { if (DEBUG) Log.i(TAG, "CONNECTED"); - SmsHelper.getInstance(this).initialize(); try { mFile = new AtomicFile(new File(new File( Environment.getDataUserCePackageDirectory( @@ -415,7 +427,6 @@ public class Assistant extends NotificationAssistantService { @Override public void onListenerDisconnected() { - SmsHelper.getInstance(this).destroy(); if (mAgingHelper != null) { mAgingHelper.onDestroy(); } diff --git a/packages/ExtServices/src/android/ext/services/notification/SmsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmsHelper.java index b0771380f7ad..07be0b8e5aa9 100644 --- a/packages/ExtServices/src/android/ext/services/notification/SmsHelper.java +++ b/packages/ExtServices/src/android/ext/services/notification/SmsHelper.java @@ -33,23 +33,11 @@ import com.android.internal.telephony.SmsApplication; public class SmsHelper { private static final String TAG = "SmsHelper"; - private static SmsHelper sSmsHelper; - private static final Object sLock = new Object(); - private final Context mContext; private ComponentName mDefaultSmsApplication; private BroadcastReceiver mBroadcastReceiver; - static SmsHelper getInstance(Context context) { - synchronized (sLock) { - if (sSmsHelper == null) { - sSmsHelper = new SmsHelper(context); - } - return sSmsHelper; - } - } - - private SmsHelper(Context context) { + SmsHelper(Context context) { mContext = context.getApplicationContext(); } diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index c03268381367..e8c728d358ea 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -166,6 +166,7 @@ <uses-permission android:name="android.permission.CONTROL_KEYGUARD" /> <uses-permission android:name="android.permission.SUSPEND_APPS" /> + <uses-permission android:name="android.permission.OBSERVE_APP_USAGE" /> <uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" /> <!-- Permission needed to wipe the device for Test Harness Mode --> <uses-permission android:name="android.permission.ENABLE_TEST_HARNESS_MODE" /> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 815ae9ac225c..866b46f732d8 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -631,6 +631,14 @@ android:exported="true"> </provider> + <!-- Provides list and realistic previews of clock faces for the picker app. --> + <provider + android:name="com.android.keyguard.clock.ClockOptionsProvider" + android:authorities="com.android.keyguard.clock" + android:exported="true" + android:grantUriPermissions="true"> + </provider> + <receiver android:name=".statusbar.KeyboardShortcutsReceiver"> <intent-filter> diff --git a/packages/SystemUI/docs/physics-animation-layout.md b/packages/SystemUI/docs/physics-animation-layout.md index a67b5e873b2e..300f63a3d285 100644 --- a/packages/SystemUI/docs/physics-animation-layout.md +++ b/packages/SystemUI/docs/physics-animation-layout.md @@ -26,7 +26,7 @@ Returns a SpringForce instance to use for animations of the given property. This ### Animation Control Methods  -Once the layout has used the controller’s configuration properties to build the animations, the controller can use them to actually run animations. This is done for two reasons - reacting to a view being added or removed, or responding to another class (such as a touch handler or broadcast receiver) requesting an animation. ```onChildAdded``` and ```onChildRemoved``` are called automatically by the layout, giving the controller the opportunity to animate the child in/out. Custom methods are called by anyone with access to the controller instance to do things like expand, collapse, or move the child views. +Once the layout has used the controller’s configuration properties to build the animations, the controller can use them to actually run animations. This is done for two reasons - reacting to a view being added or removed, or responding to another class (such as a touch handler or broadcast receiver) requesting an animation. ```onChildAdded```, ```onChildRemoved```, and ```setChildVisibility``` are called automatically by the layout, giving the controller the opportunity to animate the child in/out/visible/gone. Custom methods are called by anyone with access to the controller instance to do things like expand, collapse, or move the child views. In either case, the controller has access to the layout’s protected ```animateValueForChildAtIndex(ViewProperty property, int index, float value)``` method. This method is used to actually run an animation. diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_preview.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_preview.png Binary files differnew file mode 100644 index 000000000000..67f072f54795 --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_preview.png diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_thumbnail.png Binary files differnew file mode 100644 index 000000000000..8d0e6ed493e6 --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_thumbnail.png diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_preview.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_preview.png Binary files differnew file mode 100644 index 000000000000..035a4dffb026 --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_preview.png diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_thumbnail.png Binary files differnew file mode 100644 index 000000000000..1ac01135c211 --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_thumbnail.png diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_preview.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_preview.png Binary files differnew file mode 100644 index 000000000000..63927bc4deaf --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_preview.png diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_thumbnail.png Binary files differnew file mode 100644 index 000000000000..83d714bfcb05 --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_thumbnail.png diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_preview.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_preview.png Binary files differnew file mode 100644 index 000000000000..a538149ca3d8 --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_preview.png diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_thumbnail.png Binary files differnew file mode 100644 index 000000000000..2bfd655e37de --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_thumbnail.png diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml index 41acf82a4567..1f333073d8e0 100644 --- a/packages/SystemUI/res-keyguard/values/strings.xml +++ b/packages/SystemUI/res-keyguard/values/strings.xml @@ -485,4 +485,17 @@ number">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. <item>Fifty\nNine</item> </string-array> + <!-- Title for default clock face that will appear in the picker app next to a preview image of + the clock face. [CHAR LIMIT=8] --> + <string name="clock_title_default" translatable="false">Default</string> + <!-- Title for Bubble clock face that will appear in the picker app next to a preview image of + the clock face. [CHAR LIMIT=8] --> + <string name="clock_title_bubble" translatable="false">Bubble</string> + <!-- Title for Stretch clock face that will appear in the picker app next to a preview image of + the clock face. [CHAR LIMIT=8] --> + <string name="clock_title_stretch" translatable="false">Stretch</string> + <!-- Title for Typographic clock face that will appear in the picker app next to a preview image of + the clock face. [CHAR LIMIT=8] --> + <string name="clock_title_type" translatable="false">Type</string> + </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 1060211cebf5..81c00bc86ddc 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -23,8 +23,9 @@ <dimen name="navigation_bar_size">@*android:dimen/navigation_bar_height</dimen> <!-- Minimum swipe distance to catch the swipe gestures to invoke assist or switch tasks. --> <dimen name="navigation_bar_min_swipe_distance">48dp</dimen> - <!-- The distance from a side of device of the navigation bar to start an edge swipe --> - <dimen name="navigation_bar_edge_swipe_threshold">48dp</dimen> + <!-- The default distance from a side of the device to start an edge swipe from --> + <dimen name="navigation_bar_default_edge_width">48dp</dimen> + <dimen name="navigation_bar_default_edge_height">500dp</dimen> <!-- thickness (height) of the dead zone at the top of the navigation bar, reducing false presses on navbar buttons; approx 2mm --> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 56d9bf4c5f8f..5365dcf72f11 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1829,6 +1829,9 @@ <!-- SysUI Tuner: Button that leads to the navigation bar customization screen [CHAR LIMIT=60] --> <string name="nav_bar">Navigation bar</string> + <!-- Label for navigation edge panel for gestures [CHAR LIMIT=60] --> + <string name="nav_bar_edge_panel" translatable="false">Navigation bar Edge Panel</string> + <!-- SysUI Tuner: Button that controls layout of navigation bar [CHAR LIMIT=60] --> <string name="nav_bar_layout">Layout</string> diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java index 2ce69650b65c..d5bd2b202803 100644 --- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java +++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java @@ -21,12 +21,15 @@ import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.wifi.WifiManager; +import android.os.Handler; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; +import androidx.annotation.VisibleForTesting; + import com.android.internal.telephony.IccCardConstants; import com.android.internal.telephony.TelephonyIntents; import com.android.settingslib.WirelessUtils; @@ -206,6 +209,7 @@ public class CarrierTextController { protected void updateCarrierText() { boolean allSimsMissing = true; boolean anySimReadyAndInService = false; + boolean missingSimsWithSubs = false; CharSequence displayText = null; List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false); @@ -252,6 +256,7 @@ public class CarrierTextController { // described above. displayText = makeCarrierStringOnEmergencyCapable( getMissingSimMessage(), subs.get(0).getCarrierName()); + missingSimsWithSubs = true; } else { // We don't have a SubscriptionInfo to get the emergency calls only from. // Grab it from the old sticky broadcast if possible instead. We can use it @@ -288,12 +293,14 @@ public class CarrierTextController { displayText = getAirplaneModeMessage(); } + Handler handler = Dependency.get(Dependency.MAIN_HANDLER); + final CarrierTextCallbackInfo info = new CarrierTextCallbackInfo( + displayText, + displayText.toString().split(mSeparator.toString()), + anySimReadyAndInService && !missingSimsWithSubs, + subsIds); if (mCarrierTextCallback != null) { - mCarrierTextCallback.updateCarrierInfo(new CarrierTextCallbackInfo( - displayText, - displayText.toString().split(mSeparator.toString()), - anySimReadyAndInService, - subsIds)); + handler.post(() -> mCarrierTextCallback.updateCarrierInfo(info)); } } @@ -487,7 +494,8 @@ public class CarrierTextController { public final boolean anySimReady; public final int[] subscriptionIds; - CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers, + @VisibleForTesting + public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers, boolean anySimReady, int[] subscriptionIds) { this.carrierText = carrierText; this.listOfCarriers = listOfCarriers; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java index 64c5b1754fa8..261f391839b3 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java @@ -264,7 +264,8 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView */ private boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm, final boolean shouldIncludeAuxiliarySubtypes) { - final List<InputMethodInfo> enabledImis = imm.getEnabledInputMethodList(); + final List<InputMethodInfo> enabledImis = + imm.getEnabledInputMethodListAsUser(KeyguardUpdateMonitor.getCurrentUser()); // Number of the filtered IMEs int filteredImisCount = 0; diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockInfo.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockInfo.java new file mode 100644 index 000000000000..812f2156d5d9 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockInfo.java @@ -0,0 +1,117 @@ +/* + * 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 com.android.keyguard.clock; + +import android.graphics.Bitmap; + +import java.util.function.Supplier; + +/** + * Metadata about an available clock face. + */ +final class ClockInfo { + + private final String mName; + private final String mTitle; + private final String mId; + private final Supplier<Bitmap> mThumbnail; + private final Supplier<Bitmap> mPreview; + + private ClockInfo(String name, String title, String id, + Supplier<Bitmap> thumbnail, Supplier<Bitmap> preview) { + mName = name; + mTitle = title; + mId = id; + mThumbnail = thumbnail; + mPreview = preview; + } + + /** + * Gets the non-internationalized name for the clock face. + */ + String getName() { + return mName; + } + + /** + * Gets the name (title) of the clock face to be shown in the picker app. + */ + String getTitle() { + return mTitle; + } + + /** + * Gets the ID of the clock face, used by the picker to set the current selection. + */ + String getId() { + return mId; + } + + /** + * Gets a thumbnail image of the clock. + */ + Bitmap getThumbnail() { + return mThumbnail.get(); + } + + /** + * Gets a potentially realistic preview image of the clock face. + */ + Bitmap getPreview() { + return mPreview.get(); + } + + static Builder builder() { + return new Builder(); + } + + static class Builder { + private String mName; + private String mTitle; + private String mId; + private Supplier<Bitmap> mThumbnail; + private Supplier<Bitmap> mPreview; + + public ClockInfo build() { + return new ClockInfo(mName, mTitle, mId, mThumbnail, mPreview); + } + + public Builder setName(String name) { + mName = name; + return this; + } + + public Builder setTitle(String title) { + mTitle = title; + return this; + } + + public Builder setId(String id) { + mId = id; + return this; + } + + public Builder setThumbnail(Supplier<Bitmap> thumbnail) { + mThumbnail = thumbnail; + return this; + } + + public Builder setPreview(Supplier<Bitmap> preview) { + mPreview = preview; + return this; + } + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java index 3217ca6f489c..95981427b642 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java @@ -17,12 +17,15 @@ package com.android.keyguard.clock; import android.content.ContentResolver; import android.content.Context; +import android.content.res.Resources; import android.database.ContentObserver; +import android.graphics.BitmapFactory; import android.os.Handler; import android.os.Looper; import android.provider.Settings; import android.view.LayoutInflater; +import com.android.keyguard.R; import com.android.systemui.plugins.ClockPlugin; import com.android.systemui.statusbar.policy.ExtensionController; import com.android.systemui.statusbar.policy.ExtensionController.Extension; @@ -45,6 +48,7 @@ public final class ClockManager { private final LayoutInflater mLayoutInflater; private final ContentResolver mContentResolver; + private final List<ClockInfo> mClockInfos = new ArrayList<>(); /** * Observe settings changes to know when to switch the clock face. */ @@ -76,6 +80,36 @@ public final class ClockManager { mExtensionController = extensionController; mLayoutInflater = LayoutInflater.from(context); mContentResolver = context.getContentResolver(); + + Resources res = context.getResources(); + mClockInfos.add(ClockInfo.builder() + .setName("default") + .setTitle(res.getString(R.string.clock_title_default)) + .setId("default") + .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.default_thumbnail)) + .setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.default_preview)) + .build()); + mClockInfos.add(ClockInfo.builder() + .setName("bubble") + .setTitle(res.getString(R.string.clock_title_bubble)) + .setId(BubbleClockController.class.getName()) + .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.bubble_thumbnail)) + .setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.bubble_preview)) + .build()); + mClockInfos.add(ClockInfo.builder() + .setName("stretch") + .setTitle(res.getString(R.string.clock_title_stretch)) + .setId(StretchAnalogClockController.class.getName()) + .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.stretch_thumbnail)) + .setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.stretch_preview)) + .build()); + mClockInfos.add(ClockInfo.builder() + .setName("type") + .setTitle(res.getString(R.string.clock_title_type)) + .setId(TypeClockController.class.getName()) + .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.type_thumbnail)) + .setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.type_preview)) + .build()); } /** @@ -101,6 +135,13 @@ public final class ClockManager { } } + /** + * Get information about available clock faces. + */ + List<ClockInfo> getClockInfos() { + return mClockInfos; + } + private void setClockPlugin(ClockPlugin plugin) { for (int i = 0; i < mListeners.size(); i++) { // It probably doesn't make sense to supply the same plugin instances to multiple diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockOptionsProvider.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockOptionsProvider.java new file mode 100644 index 000000000000..5ef35be8df51 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockOptionsProvider.java @@ -0,0 +1,185 @@ +/* + * 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 com.android.keyguard.clock; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; +import android.os.ParcelFileDescriptor.AutoCloseOutputStream; +import android.text.TextUtils; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.Dependency; + +import java.io.FileNotFoundException; +import java.util.List; +import java.util.function.Supplier; + +/** + * Exposes custom clock face options and provides realistic preview images. + * + * APIs: + * + * /list_options: List the available clock faces, which has the following columns + * name: name of the clock face + * title: title of the clock face + * id: value used to set the clock face + * thumbnail: uri of the thumbnail image, should be /thumbnail/{name} + * preview: uri of the preview image, should be /preview/{name} + * + * /thumbnail/{id}: Opens a file stream for the thumbnail image for clock face {id}. + * + * /preview/{id}: Opens a file stream for the preview image for clock face {id}. + */ +public final class ClockOptionsProvider extends ContentProvider { + + private static final String TAG = "ClockOptionsProvider"; + private static final String KEY_LIST_OPTIONS = "/list_options"; + private static final String KEY_PREVIEW = "preview"; + private static final String KEY_THUMBNAIL = "thumbnail"; + private static final String COLUMN_NAME = "name"; + private static final String COLUMN_TITLE = "title"; + private static final String COLUMN_ID = "id"; + private static final String COLUMN_THUMBNAIL = "thumbnail"; + private static final String COLUMN_PREVIEW = "preview"; + private static final String MIME_TYPE_PNG = "image/png"; + private static final String CONTENT_SCHEME = "content"; + private static final String AUTHORITY = "com.android.keyguard.clock"; + + private final Supplier<List<ClockInfo>> mClocksSupplier; + + public ClockOptionsProvider() { + this(() -> Dependency.get(ClockManager.class).getClockInfos()); + } + + @VisibleForTesting + ClockOptionsProvider(Supplier<List<ClockInfo>> clocksSupplier) { + mClocksSupplier = clocksSupplier; + } + + @Override + public boolean onCreate() { + return true; + } + + @Override + public String getType(Uri uri) { + List<String> segments = uri.getPathSegments(); + if (segments.size() > 0 && (KEY_PREVIEW.equals(segments.get(0)) + || KEY_THUMBNAIL.equals(segments.get(0)))) { + return MIME_TYPE_PNG; + } + return "vnd.android.cursor.dir/clock_faces"; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, + String[] selectionArgs, String sortOrder) { + if (!KEY_LIST_OPTIONS.equals(uri.getPath())) { + return null; + } + MatrixCursor cursor = new MatrixCursor(new String[] { + COLUMN_NAME, COLUMN_TITLE, COLUMN_ID, COLUMN_THUMBNAIL, COLUMN_PREVIEW}); + List<ClockInfo> clocks = mClocksSupplier.get(); + for (int i = 0; i < clocks.size(); i++) { + ClockInfo clock = clocks.get(i); + cursor.newRow() + .add(COLUMN_NAME, clock.getName()) + .add(COLUMN_TITLE, clock.getTitle()) + .add(COLUMN_ID, clock.getId()) + .add(COLUMN_THUMBNAIL, createThumbnailUri(clock)) + .add(COLUMN_PREVIEW, createPreviewUri(clock)); + } + return cursor; + } + + @Override + public Uri insert(Uri uri, ContentValues initialValues) { + return null; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { + List<String> segments = uri.getPathSegments(); + if (segments.size() != 2 || !(KEY_PREVIEW.equals(segments.get(0)) + || KEY_THUMBNAIL.equals(segments.get(0)))) { + throw new FileNotFoundException("Invalid preview url"); + } + String id = segments.get(1); + if (TextUtils.isEmpty(id)) { + throw new FileNotFoundException("Invalid preview url, missing id"); + } + ClockInfo clock = null; + List<ClockInfo> clocks = mClocksSupplier.get(); + for (int i = 0; i < clocks.size(); i++) { + if (id.equals(clocks.get(i).getId())) { + clock = clocks.get(i); + break; + } + } + if (clock == null) { + throw new FileNotFoundException("Invalid preview url, id not found"); + } + return openPipeHelper(uri, MIME_TYPE_PNG, null, KEY_PREVIEW.equals(segments.get(0)) + ? clock.getPreview() : clock.getThumbnail(), new MyWriter()); + } + + private Uri createThumbnailUri(ClockInfo clock) { + return new Uri.Builder() + .scheme(CONTENT_SCHEME) + .authority(AUTHORITY) + .appendPath(KEY_THUMBNAIL) + .appendPath(clock.getId()) + .build(); + } + + private Uri createPreviewUri(ClockInfo clock) { + return new Uri.Builder() + .scheme(CONTENT_SCHEME) + .authority(AUTHORITY) + .appendPath(KEY_PREVIEW) + .appendPath(clock.getId()) + .build(); + } + + private static class MyWriter implements ContentProvider.PipeDataWriter<Bitmap> { + @Override + public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType, + Bundle opts, Bitmap bitmap) { + try (AutoCloseOutputStream os = new AutoCloseOutputStream(output)) { + bitmap.compress(Bitmap.CompressFormat.PNG, 100, os); + } catch (Exception e) { + Log.w(TAG, "fail to write to pipe", e); + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 404f2e541ba2..9a9a52f40227 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -186,7 +186,12 @@ public class BubbleController { * Set a listener to be notified of bubble expand events. */ public void setExpandListener(BubbleExpandListener listener) { - mExpandListener = listener; + mExpandListener = ((isExpanding, key) -> { + if (listener != null) { + listener.onBubbleExpandChanged(isExpanding, key); + } + mStatusBarWindowController.setBubbleExpanded(isExpanding); + }); if (mStackView != null) { mStackView.setExpandListener(mExpandListener); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java index 4f870f6ceffc..164406494250 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java @@ -38,6 +38,12 @@ public class ExpandedAnimationController extends PhysicsAnimationLayout.PhysicsAnimationController { /** + * How much to translate the bubbles when they're animating in/out. This value is multiplied by + * the bubble size. + */ + private static final int ANIMATE_TRANSLATION_FACTOR = 4; + + /** * The stack position from which the bubbles were expanded. Saved in {@link #expandFromStack} * and used to return to stack form in {@link #collapseBackToStack}. */ @@ -125,7 +131,10 @@ public class ExpandedAnimationController Set<DynamicAnimation.ViewProperty> getAnimatedProperties() { return Sets.newHashSet( DynamicAnimation.TRANSLATION_X, - DynamicAnimation.TRANSLATION_Y); + DynamicAnimation.TRANSLATION_Y, + DynamicAnimation.SCALE_X, + DynamicAnimation.SCALE_Y, + DynamicAnimation.ALPHA); } @Override @@ -147,13 +156,55 @@ public class ExpandedAnimationController @Override void onChildAdded(View child, int index) { - // TODO: Animate the new bubble into the row, and push the other bubbles out of the way. - child.setTranslationY(getExpandedY()); + // Pop in from the top. + // TODO: Reverse this when bubbles are at the bottom. + child.setTranslationX(getXForChildAtIndex(index)); + child.setTranslationY(getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR); + mLayout.animateValueForChild(DynamicAnimation.TRANSLATION_Y, child, getExpandedY()); + + // Animate the remaining bubbles to the correct X position. + for (int i = index + 1; i < mLayout.getChildCount(); i++) { + mLayout.animateValueForChildAtIndex( + DynamicAnimation.TRANSLATION_X, i, getXForChildAtIndex(i)); + } + } + + @Override + void onChildRemoved(View child, int index, Runnable finishRemoval) { + // Bubble pops out to the top. + // TODO: Reverse this when bubbles are at the bottom. + mLayout.animateValueForChild( + DynamicAnimation.ALPHA, child, 0f, finishRemoval); + mLayout.animateValueForChild( + DynamicAnimation.TRANSLATION_Y, + child, + getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR); + + // Animate the remaining bubbles to the correct X position. + for (int i = index; i < mLayout.getChildCount(); i++) { + mLayout.animateValueForChildAtIndex( + DynamicAnimation.TRANSLATION_X, i, getXForChildAtIndex(i)); + } } @Override - void onChildToBeRemoved(View child, int index, Runnable actuallyRemove) { - // TODO: Animate the bubble out, and pull the other bubbles into its position. - actuallyRemove.run(); + protected void setChildVisibility(View child, int index, int visibility) { + if (visibility == View.VISIBLE) { + // Set alpha to 0 but then become visible immediately so the animation is visible. + child.setAlpha(0f); + child.setVisibility(View.VISIBLE); + } + + // Fade in. + mLayout.animateValueForChild( + DynamicAnimation.ALPHA, + child, + /* value */ visibility == View.GONE ? 0f : 1f, + () -> super.setChildVisibility(child, index, visibility)); + } + + /** Returns the appropriate X translation value for a bubble at the given index. */ + private float getXForChildAtIndex(int index) { + return mBubblePaddingPx + (mBubbleSizePx + mBubblePaddingPx) * index; } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java index e4e6bc913cfd..a4ddbf752316 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java @@ -92,14 +92,18 @@ public class PhysicsAnimationLayout extends FrameLayout { abstract void onChildAdded(View child, int index); /** - * Called when a child is to be removed from the layout. Controllers can use this - * opportunity to animate out the new view before calling the provided callback to actually - * remove it. + * Called with a child view that has been removed from the layout, from the given index. The + * passed view has been removed from the layout and added back as a transient view, which + * renders normally, but is not part of the normal view hierarchy and will not be considered + * by getChildAt() and getChildCount(). * - * Controllers should be careful to ensure that actuallyRemove is called on all code paths - * or child views will never be removed. + * The controller can perform animations on the child (either manually, or by using + * {@link #animateValueForChild}), and then call finishRemoval when complete. + * + * finishRemoval must be called by implementations of this method, or transient views will + * never be removed. */ - abstract void onChildToBeRemoved(View child, int index, Runnable actuallyRemove); + abstract void onChildRemoved(View child, int index, Runnable finishRemoval); protected PhysicsAnimationLayout mLayout; @@ -112,6 +116,15 @@ public class PhysicsAnimationLayout extends FrameLayout { protected PhysicsAnimationLayout getLayout() { return mLayout; } + + /** + * Sets the child's visibility when it moves beyond or within the limits set by a call to + * {@link PhysicsAnimationLayout#setMaxRenderedChildren}. This can be overridden to animate + * this transition. + */ + protected void setChildVisibility(View child, int index, int visibility) { + child.setVisibility(visibility); + } } /** @@ -236,7 +249,7 @@ public class PhysicsAnimationLayout extends FrameLayout { // Tell the controller to animate this view out, and call the callback when it's // finished. - mController.onChildToBeRemoved(view, index, () -> { + mController.onChildRemoved(view, index, () -> { // Done animating, remove the transient view. removeTransientView(view); @@ -457,11 +470,16 @@ public class PhysicsAnimationLayout extends FrameLayout { /** Hides children beyond the max rendering count. */ private void setChildrenVisibility() { for (int i = 0; i < getChildCount(); i++) { - getChildAt(i).setVisibility( - // Ignore views that are animating out when calculating whether to hide the - // view. That is, if we're supposed to render 5 views, but 4 are animating out - // and will soon be removed, render up to 9 views temporarily. - i < mMaxRenderedChildren ? View.VISIBLE : View.GONE); + final int targetVisibility = i < mMaxRenderedChildren ? View.VISIBLE : View.GONE; + final View targetView = getChildAt(i); + + if (targetView.getVisibility() != targetVisibility) { + if (mController != null) { + mController.setChildVisibility(targetView, i, targetVisibility); + } else { + targetView.setVisibility(targetVisibility); + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java index 23c6fd3c6a9b..0c089a75aece 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java @@ -316,10 +316,10 @@ public class StackAnimationController extends } @Override - void onChildToBeRemoved(View child, int index, Runnable actuallyRemove) { + void onChildRemoved(View child, int index, Runnable finishRemoval) { // Animate the child out, actually removing it once its alpha is zero. mLayout.animateValueForChild( - DynamicAnimation.ALPHA, child, 0f, actuallyRemove); + DynamicAnimation.ALPHA, child, 0f, finishRemoval); mLayout.animateValueForChild(DynamicAnimation.SCALE_X, child, ANIMATE_IN_STARTING_SCALE); mLayout.animateValueForChild(DynamicAnimation.SCALE_Y, child, ANIMATE_IN_STARTING_SCALE); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java index c0ed4b97eaff..b865ce8d261a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java @@ -54,6 +54,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.settingslib.Utils; import com.android.settingslib.drawable.UserIconDrawable; import com.android.settingslib.graph.SignalDrawable; +import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.R.dimen; import com.android.systemui.plugins.ActivityStarter; @@ -134,6 +135,15 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, mDeviceProvisionedController = deviceProvisionedController; } + @VisibleForTesting + public QSFooterImpl(Context context, AttributeSet attrs) { + this(context, attrs, + Dependency.get(ActivityStarter.class), + Dependency.get(UserInfoController.class), + Dependency.get(NetworkController.class), + Dependency.get(DeviceProvisionedController.class)); + } + @Override protected void onFinishInflate() { super.onFinishInflate(); @@ -476,32 +486,62 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, mInfos[0].visible && mInfos[1].visible ? View.VISIBLE : View.GONE); } + @VisibleForTesting + protected int getSlotIndex(int subscriptionId) { + return SubscriptionManager.getSlotIndex(subscriptionId); + } + @Override public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) { if (info.anySimReady) { boolean[] slotSeen = new boolean[SIM_SLOTS]; - for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; i++) { - int slot = SubscriptionManager.getSlotIndex(info.subscriptionIds[i]); - mInfos[slot].visible = true; - slotSeen[slot] = true; - mCarrierTexts[slot].setText(info.listOfCarriers[i].toString().trim()); - mCarrierGroups[slot].setVisibility(View.VISIBLE); - } - for (int i = 0; i < SIM_SLOTS; i++) { - if (!slotSeen[i]) { + if (info.listOfCarriers.length == info.subscriptionIds.length) { + for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; i++) { + int slot = getSlotIndex(info.subscriptionIds[i]); + if (slot >= SIM_SLOTS) { + Log.w(TAG, "updateInfoCarrier - slot: " + slot); + continue; + } + if (slot == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { + Log.e(TAG, + "Invalid SIM slot index for subscription: " + + info.subscriptionIds[i]); + continue; + } + mInfos[slot].visible = true; + slotSeen[slot] = true; + mCarrierTexts[slot].setText(info.listOfCarriers[i].toString().trim()); + mCarrierGroups[slot].setVisibility(View.VISIBLE); + } + for (int i = 0; i < SIM_SLOTS; i++) { + if (!slotSeen[i]) { + mInfos[i].visible = false; + mCarrierGroups[i].setVisibility(View.GONE); + } + } + } else { + // If there are sims ready but there are not the same number of carrier names as + // subscription ids, just show the full text in the first slot + mInfos[0].visible = true; + mCarrierTexts[0].setText(info.carrierText); + mCarrierGroups[0].setVisibility(View.VISIBLE); + for (int i = 1; i < SIM_SLOTS; i++) { mInfos[i].visible = false; + mCarrierTexts[i].setText(""); mCarrierGroups[i].setVisibility(View.GONE); } } - handleUpdateState(); } else { mInfos[0].visible = false; - mInfos[1].visible = false; mCarrierTexts[0].setText(info.carrierText); mCarrierGroups[0].setVisibility(View.VISIBLE); - mCarrierGroups[1].setVisibility(View.GONE); - handleUpdateState(); + for (int i = 1; i < SIM_SLOTS; i++) { + mInfos[i].visible = false; + mCarrierTexts[i].setText(""); + mCarrierGroups[i].setVisibility(View.GONE); + } } + handleUpdateState(); } @Override @@ -510,9 +550,14 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, int qsType, boolean activityIn, boolean activityOut, String typeContentDescription, String description, boolean isWide, int subId, boolean roaming) { - int slotIndex = SubscriptionManager.getSlotIndex(subId); + int slotIndex = getSlotIndex(subId); if (slotIndex >= SIM_SLOTS) { - Log.e(TAG, "setMobileDataIndicators - slot: " + slotIndex); + Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex); + return; + } + if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { + Log.e(TAG, "Invalid SIM slot index for subscription: " + subId); + return; } mInfos[slotIndex].visible = statusIcon.visible; mInfos[slotIndex].mobileSignalIconId = statusIcon.icon; @@ -539,7 +584,6 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, boolean roaming; } - /** * TextView that changes its ellipsize value with its visibility. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index 31d16211f521..491f310cedf2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -54,6 +54,7 @@ import com.android.systemui.Dumpable; 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.collection.NotificationEntry.EditedSuggestionInfo; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.phone.ShadeController; @@ -231,7 +232,8 @@ public class NotificationRemoteInputManager implements Dumpable { return false; } - return activateRemoteInput(view, inputs, input, pendingIntent); + return activateRemoteInput(view, inputs, input, pendingIntent, + null /* editedSuggestionInfo */); } }; @@ -291,6 +293,19 @@ public class NotificationRemoteInputManager implements Dumpable { } try { mBarService.onNotificationDirectReplied(entry.notification.getKey()); + if (entry.editedSuggestionInfo != null) { + boolean modifiedBeforeSending = + !TextUtils.equals(entry.remoteInputText, + entry.editedSuggestionInfo.originalText); + mBarService.onNotificationSmartReplySent( + entry.notification.getKey(), + entry.editedSuggestionInfo.index, + entry.editedSuggestionInfo.originalText, + NotificationLogger + .getNotificationLocation(entry) + .toMetricsEventEnum(), + modifiedBeforeSending); + } } catch (RemoteException e) { // Nothing to do, system going down } @@ -310,10 +325,12 @@ public class NotificationRemoteInputManager implements Dumpable { * @param inputs The remote inputs that need to be sent to the app. * @param input The remote input that needs to be activated. * @param pendingIntent The pending intent to be sent to the app. + * @param editedSuggestionInfo The smart reply that should be inserted in the remote input, or + * {@code null} if the user is not editing a smart reply. * @return Whether the {@link RemoteInput} was activated. */ public boolean activateRemoteInput(View view, RemoteInput[] inputs, RemoteInput input, - PendingIntent pendingIntent) { + PendingIntent pendingIntent, @Nullable EditedSuggestionInfo editedSuggestionInfo) { ViewParent p = view.getParent(); RemoteInputView riv = null; @@ -386,7 +403,7 @@ public class NotificationRemoteInputManager implements Dumpable { riv.setRevealParameters(cx, cy, r); riv.setPendingIntent(pendingIntent); - riv.setRemoteInput(inputs, input); + riv.setRemoteInput(inputs, input, editedSuggestionInfo); riv.focusAnimated(); return true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java index 5a8f71d6627d..736b9ebea5c3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java @@ -56,13 +56,12 @@ public class SmartReplyController { * Notifies StatusBarService a smart reply is sent. */ public void smartReplySent(NotificationEntry entry, int replyIndex, CharSequence reply, - boolean generatedByAssistant, int notificationLocation) { + int notificationLocation, boolean modifiedBeforeSending) { mCallback.onSmartReplySent(entry, reply); mSendingKeys.add(entry.key); try { - mBarService.onNotificationSmartReplySent( - entry.notification.getKey(), replyIndex, reply, generatedByAssistant, - notificationLocation); + mBarService.onNotificationSmartReplySent(entry.notification.getKey(), replyIndex, reply, + notificationLocation, modifiedBeforeSending); } catch (RemoteException e) { // Nothing to do, system going down } @@ -100,10 +99,10 @@ public class SmartReplyController { * Smart Replies and Actions have been added to the UI. */ public void smartSuggestionsAdded(final NotificationEntry entry, int replyCount, - int actionCount, boolean generatedByAssistant) { + int actionCount, boolean generatedByAssistant, boolean editBeforeSending) { try { - mBarService.onNotificationSmartSuggestionsAdded( - entry.notification.getKey(), replyCount, actionCount, generatedByAssistant); + mBarService.onNotificationSmartSuggestionsAdded(entry.notification.getKey(), replyCount, + actionCount, generatedByAssistant, editBeforeSending); } catch (RemoteException e) { // Nothing to do, system going down } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index db9fcc89f4a9..9f1693c71459 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -105,6 +105,14 @@ public final class NotificationEntry { /** Smart replies provided by the NotificationAssistantService. */ @NonNull public CharSequence[] systemGeneratedSmartReplies = new CharSequence[0]; + + /** + * If {@link android.app.RemoteInput#getEditChoicesBeforeSending} is enabled, and the user is + * currently editing a choice (smart reply), then this field contains the information about the + * suggestion being edited. Otherwise <code>null</code>. + */ + public EditedSuggestionInfo editedSuggestionInfo; + @VisibleForTesting public int suppressedVisualEffects; public boolean suspended; @@ -746,4 +754,23 @@ public final class NotificationEntry { private static boolean isCategory(String category, Notification n) { return Objects.equals(n.category, category); } + + /** Information about a suggestion that is being edited. */ + public static class EditedSuggestionInfo { + + /** + * The value of the suggestion (before any user edits). + */ + public final CharSequence originalText; + + /** + * The index of the suggestion that is being edited. + */ + public final int index; + + public EditedSuggestionInfo(CharSequence originalText, int index) { + this.originalText = originalText; + this.index = index; + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index 878d533e91a6..80956159c20b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -1509,8 +1509,13 @@ public class NotificationContentView extends FrameLayout { boolean fromAssistant = smartRepliesAndActions.smartReplies == null ? smartRepliesAndActions.smartActions.fromAssistant : smartRepliesAndActions.smartReplies.fromAssistant; + boolean editBeforeSending = smartRepliesAndActions.smartReplies != null + && mSmartReplyConstants.getEffectiveEditChoicesBeforeSending( + smartRepliesAndActions.smartReplies.remoteInput + .getEditChoicesBeforeSending()); + mSmartReplyController.smartSuggestionsAdded(entry, numSmartReplies, - numSmartActions, fromAssistant); + numSmartActions, fromAssistant, editBeforeSending); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java new file mode 100644 index 000000000000..dae4da7355c7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java @@ -0,0 +1,78 @@ +/* + * 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 com.android.systemui.statusbar.phone; + +import android.annotation.NonNull; +import android.content.Context; +import android.graphics.PixelFormat; +import android.view.View; +import android.view.WindowManager; + +import com.android.systemui.R; + +public class NavigationBarEdgePanel extends View { + private static final String TAG = "NavigationBarEdgePanel"; + + public static NavigationBarEdgePanel create(@NonNull Context context, int width, int height, + int gravity) { + final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height, + WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, + WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, + PixelFormat.TRANSLUCENT); + lp.gravity = gravity; + lp.setTitle(TAG + context.getDisplayId()); + lp.accessibilityTitle = context.getString(R.string.nav_bar_edge_panel); + lp.windowAnimations = 0; + NavigationBarEdgePanel panel = new NavigationBarEdgePanel(context); + panel.setLayoutParams(lp); + return panel; + } + + private NavigationBarEdgePanel(Context context) { + super(context); + } + + public void setWindowFlag(int flags, boolean enable) { + WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams(); + if (lp == null || enable == ((lp.flags & flags) != 0)) { + return; + } + if (enable) { + lp.flags |= flags; + } else { + lp.flags &= ~flags; + } + updateLayout(lp); + } + + public void setDimensions(int width, int height) { + final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams(); + if (lp.width != width || lp.height != height) { + lp.width = width; + lp.height = height; + updateLayout(lp); + } + } + + private void updateLayout(WindowManager.LayoutParams lp) { + WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); + wm.updateViewLayout(this, lp); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 02683c16d935..651670cbf2c0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -18,6 +18,8 @@ package com.android.systemui.statusbar.phone; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT; import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_QUICK_SCRUB; import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON; @@ -35,6 +37,8 @@ import android.animation.PropertyValuesHolder; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.annotation.DrawableRes; +import android.annotation.IntDef; +import android.annotation.SuppressLint; import android.app.StatusBarManager; import android.content.Context; import android.content.res.Configuration; @@ -51,6 +55,7 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.view.Display; +import android.view.Gravity; import android.view.MotionEvent; import android.view.Surface; import android.view.View; @@ -87,12 +92,21 @@ import com.android.systemui.statusbar.policy.KeyButtonDrawable; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.function.Consumer; public class NavigationBarView extends FrameLayout implements PluginListener<NavGesture> { final static boolean DEBUG = false; final static String TAG = "StatusBar/NavBarView"; + @Retention(RetentionPolicy.SOURCE) + @IntDef({WINDOW_TARGET_BOTTOM, WINDOW_TARGET_LEFT, WINDOW_TARGET_RIGHT}) + public @interface WindowTarget{} + public static final int WINDOW_TARGET_BOTTOM = 0; + public static final int WINDOW_TARGET_LEFT = 1; + public static final int WINDOW_TARGET_RIGHT = 2; + // slippery nav bar when everything is disabled, e.g. during setup final static boolean SLIPPERY_WHEN_DISABLED = true; @@ -109,6 +123,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav int mNavigationIconHints = 0; private @NavigationBarCompat.HitTarget int mDownHitTarget = HIT_TARGET_NONE; + private @WindowTarget int mWindowHitTarget = WINDOW_TARGET_BOTTOM; private Rect mHomeButtonBounds = new Rect(); private Rect mBackButtonBounds = new Rect(); private Rect mRecentsButtonBounds = new Rect(); @@ -160,6 +175,9 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav private NavigationAssistantAction mAssistantAction; private NavigationNotificationPanelAction mNotificationPanelAction; + private NavigationBarEdgePanel mLeftEdgePanel; + private NavigationBarEdgePanel mRightEdgePanel; + /** * Helper that is responsible for showing the right toast when a disallowed activity operation * occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in @@ -222,6 +240,18 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } }; + private final OnTouchListener mEdgePanelTouchListener = new OnTouchListener() { + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getActionMasked() == ACTION_DOWN) { + mWindowHitTarget = v == mLeftEdgePanel ? WINDOW_TARGET_LEFT : WINDOW_TARGET_RIGHT; + mDownHitTarget = HIT_TARGET_NONE; + } + return mGestureHelper.onTouchEvent(event); + } + }; + private class H extends Handler { public void handleMessage(Message m) { switch (m.what) { @@ -297,6 +327,16 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mColorAdaptionController.end(); } } + + @Override + public void onEdgeSensitivityChanged(int width, int height) { + if (mLeftEdgePanel != null) { + mLeftEdgePanel.setDimensions(width, height); + } + if (mRightEdgePanel != null) { + mRightEdgePanel.setDimensions(width, height); + } + } }; public NavigationBarView(Context context, AttributeSet attrs) { @@ -433,6 +473,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav int x = (int) event.getX(); int y = (int) event.getY(); mDownHitTarget = HIT_TARGET_NONE; + mWindowHitTarget = WINDOW_TARGET_BOTTOM; if (deadZoneConsumed) { mDownHitTarget = HIT_TARGET_DEAD_ZONE; } else if (getBackButton().isVisible() && mBackButtonBounds.contains(x, y)) { @@ -483,6 +524,10 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav return mDownHitTarget; } + public @WindowTarget int getWindowTarget() { + return mWindowHitTarget; + } + public void abortCurrentGesture() { getHomeButton().abortCurrentGesture(); } @@ -837,24 +882,32 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } private void setSlippery(boolean slippery) { - boolean changed = false; + setWindowFlag(WindowManager.LayoutParams.FLAG_SLIPPERY, slippery); + } + + public void setWindowTouchable(boolean flag) { + setWindowFlag(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, !flag); + if (mLeftEdgePanel != null) { + mLeftEdgePanel.setWindowFlag(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, !flag); + } + if (mRightEdgePanel != null) { + mRightEdgePanel.setWindowFlag(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, !flag); + } + } + + private void setWindowFlag(int flags, boolean enable) { final ViewGroup navbarView = ((ViewGroup) getParent()); - final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) navbarView - .getLayoutParams(); - if (lp == null) { + WindowManager.LayoutParams lp = (WindowManager.LayoutParams) navbarView.getLayoutParams(); + if (lp == null || enable == ((lp.flags & flags) != 0)) { return; } - if (slippery && (lp.flags & WindowManager.LayoutParams.FLAG_SLIPPERY) == 0) { - lp.flags |= WindowManager.LayoutParams.FLAG_SLIPPERY; - changed = true; - } else if (!slippery && (lp.flags & WindowManager.LayoutParams.FLAG_SLIPPERY) != 0) { - lp.flags &= ~WindowManager.LayoutParams.FLAG_SLIPPERY; - changed = true; - } - if (changed) { - WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE); - wm.updateViewLayout(navbarView, lp); + if (enable) { + lp.flags |= flags; + } else { + lp.flags &= ~flags; } + WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); + wm.updateViewLayout(navbarView, lp); } public void setMenuVisibility(final boolean show) { @@ -1016,6 +1069,17 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } catch (RemoteException e) { Slog.e(TAG, "Failed to get nav bar position.", e); } + + // For landscape, hide the panel that would interfere with navigation bar layout + if (mLeftEdgePanel != null && mRightEdgePanel != null) { + mLeftEdgePanel.setVisibility(VISIBLE); + mRightEdgePanel.setVisibility(VISIBLE); + if (navBarPos == NAV_BAR_LEFT) { + mLeftEdgePanel.setVisibility(GONE); + } else if (navBarPos == NAV_BAR_RIGHT) { + mRightEdgePanel.setVisibility(GONE); + } + } mGestureHelper.setBarState(isRtl, navBarPos); } @@ -1142,6 +1206,21 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav NavGesture.class, false /* Only one */); setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled()); mColorAdaptionController.start(); + + if (mPrototypeController.isEnabled()) { + WindowManager wm = (WindowManager) getContext() + .getSystemService(Context.WINDOW_SERVICE); + int width = mPrototypeController.getEdgeSensitivityWidth(); + int height = mPrototypeController.getEdgeSensitivityHeight(); + mLeftEdgePanel = NavigationBarEdgePanel.create(getContext(), width, height, + Gravity.START | Gravity.BOTTOM); + mRightEdgePanel = NavigationBarEdgePanel.create(getContext(), width, height, + Gravity.END | Gravity.BOTTOM); + mLeftEdgePanel.setOnTouchListener(mEdgePanelTouchListener); + mRightEdgePanel.setOnTouchListener(mEdgePanelTouchListener); + wm.addView(mLeftEdgePanel, mLeftEdgePanel.getLayoutParams()); + wm.addView(mRightEdgePanel, mRightEdgePanel.getLayoutParams()); + } } @Override @@ -1157,6 +1236,17 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav for (int i = 0; i < mButtonDispatchers.size(); ++i) { mButtonDispatchers.valueAt(i).onDestroy(); } + + if (mPrototypeController.isEnabled()) { + WindowManager wm = (WindowManager) getContext() + .getSystemService(Context.WINDOW_SERVICE); + if (mLeftEdgePanel != null) { + wm.removeView(mLeftEdgePanel); + } + if (mRightEdgePanel != null) { + wm.removeView(mRightEdgePanel); + } + } } private void setUpSwipeUpOnboarding(boolean connectedToOverviewProxy) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java index b4feb25fba7f..8421e23e97b0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone; import android.annotation.IntDef; import android.content.Context; +import android.content.res.Resources; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; @@ -34,7 +35,12 @@ import java.lang.annotation.RetentionPolicy; public class NavigationPrototypeController extends ContentObserver { private static final String HIDE_BACK_BUTTON_SETTING = "quickstepcontroller_hideback"; private static final String HIDE_HOME_BUTTON_SETTING = "quickstepcontroller_hidehome"; + private static final String PROTOTYPE_ENABLED = "prototype_enabled"; + private static final String EDGE_SENSITIVITY_HEIGHT_SETTING = + "quickstepcontroller_edge_height_sensitivity"; + public static final String EDGE_SENSITIVITY_WIDTH_SETTING = + "quickstepcontroller_edge_width_sensitivity"; private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map"; public static final String NAV_COLOR_ADAPT_ENABLE_SETTING = "navbar_color_adapt_enable"; @@ -79,6 +85,8 @@ public class NavigationPrototypeController extends ContentObserver { registerObserver(HIDE_HOME_BUTTON_SETTING); registerObserver(GESTURE_MATCH_SETTING); registerObserver(NAV_COLOR_ADAPT_ENABLE_SETTING); + registerObserver(EDGE_SENSITIVITY_WIDTH_SETTING); + registerObserver(EDGE_SENSITIVITY_HEIGHT_SETTING); } /** @@ -106,10 +114,26 @@ public class NavigationPrototypeController extends ContentObserver { } else if (path.endsWith(NAV_COLOR_ADAPT_ENABLE_SETTING)) { mListener.onColorAdaptChanged( NavBarTintController.isEnabled(mContext)); + } else if (path.endsWith(EDGE_SENSITIVITY_WIDTH_SETTING) + || path.endsWith(EDGE_SENSITIVITY_HEIGHT_SETTING)) { + mListener.onEdgeSensitivityChanged(getEdgeSensitivityWidth(), + getEdgeSensitivityHeight()); } } } + public int getEdgeSensitivityWidth() { + return convertDpToPixel(getGlobalInt(EDGE_SENSITIVITY_WIDTH_SETTING, 0)); + } + + public int getEdgeSensitivityHeight() { + return convertDpToPixel(getGlobalInt(EDGE_SENSITIVITY_HEIGHT_SETTING, 0)); + } + + public boolean isEnabled() { + return getGlobalBool(PROTOTYPE_ENABLED, false); + } + /** * Retrieve the action map to apply to the quick step controller * @return an action map @@ -144,15 +168,24 @@ public class NavigationPrototypeController extends ContentObserver { return Settings.Global.getInt(mContext.getContentResolver(), name, defaultVal ? 1 : 0) == 1; } + private int getGlobalInt(String name, int defaultVal) { + return Settings.Global.getInt(mContext.getContentResolver(), name, defaultVal); + } + private void registerObserver(String name) { mContext.getContentResolver() .registerContentObserver(Settings.Global.getUriFor(name), false, this); } + private static int convertDpToPixel(float dp) { + return (int) (dp * Resources.getSystem().getDisplayMetrics().density); + } + public interface OnPrototypeChangedListener { void onGestureRemap(@GestureAction int[] actions); void onBackButtonVisibilityChanged(boolean visible); void onHomeButtonVisibilityChanged(boolean visible); void onColorAdaptChanged(boolean enabled); + void onEdgeSensitivityChanged(int width, int height); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java index d5d283c46b0a..84f1cef19b77 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java @@ -28,9 +28,12 @@ import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_ import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_OVERVIEW; +import static com.android.systemui.statusbar.phone.NavigationBarView.WINDOW_TARGET_BOTTOM; +import static com.android.systemui.statusbar.phone.NavigationPrototypeController.EDGE_SENSITIVITY_WIDTH_SETTING; import android.annotation.Nullable; import android.content.Context; +import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Rect; @@ -42,12 +45,9 @@ import android.util.Log; import android.view.InputDevice; import android.view.MotionEvent; import android.view.View; -import android.view.ViewGroup; +import android.view.ViewConfiguration; import android.view.ViewPropertyAnimator; -import android.view.WindowManager; -import android.view.WindowManager.LayoutParams; - import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; @@ -72,6 +72,7 @@ public class QuickStepController implements GestureHelper { /** Experiment to swipe home button left to execute a back key press */ private static final String HIDE_BACK_BUTTON_PROP = "quickstepcontroller_hideback"; private static final String ENABLE_CLICK_THROUGH_NAV_PROP = "quickstepcontroller_clickthrough"; + private static final String GESTURE_REGION_THRESHOLD_SETTING = "gesture_region_threshold"; private static final long BACK_BUTTON_FADE_IN_ALPHA = 150; private static final long CLICK_THROUGH_TAP_DELAY = 70; private static final long CLICK_THROUGH_TAP_RESET_DELAY = 100; @@ -109,10 +110,10 @@ public class QuickStepController implements GestureHelper { private float mMaxDragLimit; private float mMinDragLimit; private float mDragDampeningFactor; - private float mEdgeSwipeThreshold; private boolean mClickThroughPressed; private float mClickThroughPressX; private float mClickThroughPressY; + private int mGestureRegionThreshold; private NavigationGestureAction mCurrentAction; private NavigationGestureAction[] mGestureActions = new NavigationGestureAction[MAX_GESTURES]; @@ -139,7 +140,7 @@ public class QuickStepController implements GestureHelper { }; private final Runnable mClickThroughResetTap = () -> { - setWindowTouchable(true); + mNavigationBarView.setWindowTouchable(true); mClickThroughPressed = false; }; @@ -210,7 +211,8 @@ public class QuickStepController implements GestureHelper { // The same down event was just sent on intercept and therefore can be ignored here final boolean ignoreProxyDownEvent = event.getAction() == MotionEvent.ACTION_DOWN - && mOverviewEventSender.getProxy() != null; + && mOverviewEventSender.getProxy() != null + && mNavigationBarView.getWindowTarget() == WINDOW_TARGET_BOTTOM; return ignoreProxyDownEvent || handleTouchEvent(event); } @@ -268,12 +270,15 @@ public class QuickStepController implements GestureHelper { mNavigationBarView.transformMatrixToLocal(mTransformLocalMatrix); mAllowGestureDetection = true; mNotificationsVisibleOnDown = !mNavigationBarView.isNotificationsFullyCollapsed(); - mEdgeSwipeThreshold = mContext.getResources() - .getDimensionPixelSize(R.dimen.navigation_bar_edge_swipe_threshold); + final int defaultRegionThreshold = mContext.getResources() + .getDimensionPixelOffset(R.dimen.navigation_bar_default_edge_width); + mGestureRegionThreshold = convertDpToPixel(getIntGlobalSetting(mContext, + EDGE_SENSITIVITY_WIDTH_SETTING, defaultRegionThreshold)); break; } case MotionEvent.ACTION_MOVE: { - if (!mAllowGestureDetection) { + if (!mAllowGestureDetection + || mNavigationBarView.getWindowTarget() != WINDOW_TARGET_BOTTOM) { break; } int x = (int) event.getX(); @@ -330,18 +335,12 @@ public class QuickStepController implements GestureHelper { } else if (exceededSwipeHorizontalTouchSlop) { if (mDragHPositive ? (posH < touchDownH) : (posH > touchDownH)) { // Swiping left (rtl) gesture - int index = mGestureActions[ACTION_SWIPE_LEFT_FROM_EDGE_INDEX] != null - && isEdgeSwipeAlongNavBar(touchDownH, !mDragHPositive) - ? ACTION_SWIPE_LEFT_FROM_EDGE_INDEX : ACTION_SWIPE_LEFT_INDEX; - tryToStartGesture(mGestureActions[index], true /* alignedWithNavBar */, - event); + tryToStartGesture(mGestureActions[ACTION_SWIPE_LEFT_INDEX], + true /* alignedWithNavBar */, event); } else { // Swiping right (ltr) gesture - int index = mGestureActions[ACTION_SWIPE_RIGHT_FROM_EDGE_INDEX] != null - && isEdgeSwipeAlongNavBar(touchDownH, mDragHPositive) - ? ACTION_SWIPE_RIGHT_FROM_EDGE_INDEX : ACTION_SWIPE_RIGHT_INDEX; - tryToStartGesture(mGestureActions[index], true /* alignedWithNavBar */, - event); + tryToStartGesture(mGestureActions[ACTION_SWIPE_RIGHT_INDEX], + true /* alignedWithNavBar */, event); } } } @@ -354,24 +353,34 @@ public class QuickStepController implements GestureHelper { case MotionEvent.ACTION_UP: if (mCurrentAction != null) { mCurrentAction.endGesture(); - } else if (action == MotionEvent.ACTION_UP - && getBoolGlobalSetting(mContext, ENABLE_CLICK_THROUGH_NAV_PROP) - && !mClickThroughPressed) { - // Enable click through functionality where no gesture has been detected and not - // passed the drag slop so inject a touch event at the same location - // after making the navigation bar window untouchable. After a some time, the - // navigation bar will be able to take input events again - float diffX = Math.abs(event.getX() - mTouchDownX); - float diffY = Math.abs(event.getY() - mTouchDownY); - - if ((diffX <= NavigationBarCompat.getQuickStepDragSlopPx() - && diffY <= NavigationBarCompat.getQuickStepDragSlopPx())) { - setWindowTouchable(false); - mClickThroughPressX = event.getRawX(); - mClickThroughPressY = event.getRawY(); - mClickThroughPressed = true; - mNavigationBarView.postDelayed(mClickThroughSendTap, - CLICK_THROUGH_TAP_DELAY); + } else if (action == MotionEvent.ACTION_UP) { + if (canTriggerEdgeSwipe(event)) { + int index = mNavigationBarView.getWindowTarget() == NAV_BAR_LEFT + ? ACTION_SWIPE_RIGHT_FROM_EDGE_INDEX + : ACTION_SWIPE_LEFT_FROM_EDGE_INDEX; + tryToStartGesture(mGestureActions[index], false /* alignedWithNavBar */, + event); + if (mCurrentAction != null) { + mCurrentAction.endGesture(); + } + } else if (getBoolGlobalSetting(mContext, ENABLE_CLICK_THROUGH_NAV_PROP) + && !mClickThroughPressed) { + // Enable click through functionality where no gesture has been detected and + // not passed the drag slop so inject a touch event at the same location + // after making the navigation bar window untouchable. After a some time, + // the navigation bar will be able to take input events again + float diffX = Math.abs(event.getX() - mTouchDownX); + float diffY = Math.abs(event.getY() - mTouchDownY); + + if ((diffX <= NavigationBarCompat.getQuickStepDragSlopPx() + && diffY <= NavigationBarCompat.getQuickStepDragSlopPx())) { + mNavigationBarView.setWindowTouchable(false); + mClickThroughPressX = event.getRawX(); + mClickThroughPressY = event.getRawY(); + mClickThroughPressed = true; + mNavigationBarView.postDelayed(mClickThroughSendTap, + CLICK_THROUGH_TAP_DELAY); + } } } @@ -403,30 +412,6 @@ public class QuickStepController implements GestureHelper { return mCurrentAction != null || deadZoneConsumed; } - private void setWindowTouchable(boolean flag) { - final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) - ((ViewGroup) mNavigationBarView.getParent()).getLayoutParams(); - if (flag) { - lp.flags &= ~LayoutParams.FLAG_NOT_TOUCHABLE; - } else { - lp.flags |= LayoutParams.FLAG_NOT_TOUCHABLE; - } - final WindowManager wm = (WindowManager) mNavigationBarView.getContext() - .getSystemService(Context.WINDOW_SERVICE); - wm.updateViewLayout((View) mNavigationBarView.getParent(), lp); - } - - private boolean isEdgeSwipeAlongNavBar(int touchDown, boolean dragPositiveDirection) { - // Detect edge swipe from side of 0 -> threshold - if (dragPositiveDirection) { - return touchDown < mEdgeSwipeThreshold; - } - // Detect edge swipe from side of size -> (size - threshold) - final int largeSide = isNavBarVertical() - ? mNavigationBarView.getHeight() : mNavigationBarView.getWidth(); - return touchDown > largeSide - mEdgeSwipeThreshold; - } - private void handleDragHitTarget(int position, int touchDown) { // Drag the hit target if gesture action requires it if (mHitTarget != null && (mGestureVerticalDragsButton || mGestureHorizontalDragsButton)) { @@ -448,6 +433,10 @@ public class QuickStepController implements GestureHelper { } private boolean shouldProxyEvents(int action) { + // Do not send events for side navigation bar panels + if (mNavigationBarView.getWindowTarget() != WINDOW_TARGET_BOTTOM) { + return false; + } final boolean actionValid = (mCurrentAction == null || !mCurrentAction.disableProxyEvents()); if (actionValid && !mIsInScreenPinning) { @@ -619,6 +608,32 @@ public class QuickStepController implements GestureHelper { } } + /** + * To trigger an edge swipe, the user must start from the left or right edges of certain height + * from the bottom then past the drag slope towards the center of the screen, followed by either + * a timed trigger for fast swipes or distance if held on the screen longer. + * For time, user must swipe up quickly before the Tap Timeout (typically 100ms) and for + * distance, the user can drag back to cancel if the touch up has not past the threshold. + * @param event Touch up event + * @return whether or not edge swipe gesture occurs + */ + private boolean canTriggerEdgeSwipe(MotionEvent event) { + if (mNavigationBarView.getWindowTarget() == WINDOW_TARGET_BOTTOM) { + return false; + } + int x = (int) event.getX(); + int y = (int) event.getY(); + int xDiff = Math.abs(x - mTouchDownX); + int yDiff = Math.abs(y - mTouchDownY); + final boolean exceededSwipeTouchSlop = xDiff > NavigationBarCompat.getQuickStepDragSlopPx() + && xDiff > yDiff; + if (exceededSwipeTouchSlop) { + long timeDiff = event.getEventTime() - event.getDownTime(); + return xDiff > mGestureRegionThreshold || timeDiff < ViewConfiguration.getTapTimeout(); + } + return false; + } + private boolean canPerformAnyAction() { for (NavigationGestureAction action: mGestureActions) { if (action != null && action.isEnabled()) { @@ -684,10 +699,18 @@ public class QuickStepController implements GestureHelper { return mNavBarPosition == NAV_BAR_LEFT || mNavBarPosition == NAV_BAR_RIGHT; } + private static int convertDpToPixel(float dp) { + return (int) (dp * Resources.getSystem().getDisplayMetrics().density); + } + static boolean getBoolGlobalSetting(Context context, String key) { return Settings.Global.getInt(context.getContentResolver(), key, 0) != 0; } + static int getIntGlobalSetting(Context context, String key, int defaultValue) { + return Settings.Global.getInt(context.getContentResolver(), key, defaultValue); + } + public static boolean shouldhideBackButton(Context context) { return getBoolGlobalSetting(context, HIDE_BACK_BUTTON_PROP); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSwitchAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSwitchAction.java index 40f2392d2610..974de4b87ebc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSwitchAction.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSwitchAction.java @@ -22,7 +22,6 @@ import static com.android.systemui.recents.OverviewProxyService.TAG_OPS; import android.annotation.NonNull; import android.graphics.Rect; import android.os.RemoteException; -import android.provider.Settings; import android.util.Log; import android.view.MotionEvent; @@ -34,7 +33,6 @@ import com.android.systemui.shared.recents.utilities.Utilities; */ public class QuickSwitchAction extends NavigationGestureAction { private static final String TAG = "QuickSwitchAction"; - private static final String QUICKSWITCH_ENABLED_SETTING = "QUICK_SWITCH"; protected final Rect mDragOverRect = new Rect(); @@ -71,10 +69,6 @@ public class QuickSwitchAction extends NavigationGestureAction { @Override protected void onGestureStart(MotionEvent event) { - // Temporarily enable launcher to allow quick switch instead of quick scrub - Settings.Global.putInt(mNavigationBarView.getContext().getContentResolver(), - QUICKSWITCH_ENABLED_SETTING, 1 /* enabled */); - startQuickGesture(event); } @@ -105,10 +99,6 @@ public class QuickSwitchAction extends NavigationGestureAction { @Override protected void onGestureEnd() { endQuickGesture(true /* animate */); - - // Disable launcher to use quick switch instead of quick scrub - Settings.Global.putInt(mNavigationBarView.getContext().getContentResolver(), - QUICKSWITCH_ENABLED_SETTING, 0 /* disabled */); } protected void startQuickGesture(MotionEvent event) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java index ffaa236218fb..86e17f33fc77 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java @@ -202,7 +202,8 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat private void applyFocusableFlag(State state) { boolean panelFocusable = state.statusBarFocusable && state.panelExpanded; if (state.bouncerShowing && (state.keyguardOccluded || state.keyguardNeedsInput) - || ENABLE_REMOTE_INPUT && state.remoteInputActive) { + || ENABLE_REMOTE_INPUT && state.remoteInputActive + || state.bubbleExpanded) { mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; } else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) { @@ -486,6 +487,21 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat return mCurrentState.bubblesShowing; } + /** + * Sets if there is a bubble being expanded on the screen. + */ + public void setBubbleExpanded(boolean bubbleExpanded) { + mCurrentState.bubbleExpanded = bubbleExpanded; + apply(mCurrentState); + } + + /** + * The bubble is shown in expanded state for the status bar. + */ + public boolean getBubbleExpanded() { + return mCurrentState.bubbleExpanded; + } + public void setStateListener(OtherwisedCollapsedListener listener) { mListener = listener; } @@ -539,6 +555,7 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat boolean wallpaperSupportsAmbientMode; boolean notTouchable; boolean bubblesShowing; + boolean bubbleExpanded; /** * The {@link StatusBar} state from the status bar. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index 7881df97df2a..1e090630efdb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.policy; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.Notification; import android.app.PendingIntent; @@ -59,6 +60,7 @@ import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.NotificationEntry.EditedSuggestionInfo; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; @@ -269,10 +271,24 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mPendingIntent = pendingIntent; } - public void setRemoteInput(RemoteInput[] remoteInputs, RemoteInput remoteInput) { + /** + * Sets the remote input for this view. + * + * @param remoteInputs The remote inputs that need to be sent to the app. + * @param remoteInput The remote input that needs to be activated. + * @param editedSuggestionInfo The smart reply that should be inserted in the remote input, or + * {@code null} if the user is not editing a smart reply. + */ + public void setRemoteInput(RemoteInput[] remoteInputs, RemoteInput remoteInput, + @Nullable EditedSuggestionInfo editedSuggestionInfo) { mRemoteInputs = remoteInputs; mRemoteInput = remoteInput; mEditText.setHint(mRemoteInput.getLabel()); + + mEntry.editedSuggestionInfo = editedSuggestionInfo; + if (editedSuggestionInfo != null) { + mEntry.remoteInputText = editedSuggestionInfo.originalText; + } } public void focusAnimated() { @@ -389,7 +405,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene public void stealFocusFrom(RemoteInputView other) { other.close(); setPendingIntent(other.mPendingIntent); - setRemoteInput(other.mRemoteInputs, other.mRemoteInput); + setRemoteInput(other.mRemoteInputs, other.mRemoteInput, mEntry.editedSuggestionInfo); setRevealParameters(other.mRevealCx, other.mRevealCy, other.mRevealR); focus(); } @@ -429,7 +445,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene continue; } setPendingIntent(a.actionIntent); - setRemoteInput(inputs, input); + setRemoteInput(inputs, input, null /* editedSuggestionInfo*/); return true; } return false; 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 1d2d6f784620..45d215ef309c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java @@ -38,6 +38,7 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.NotificationEntry.EditedSuggestionInfo; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; @@ -252,16 +253,17 @@ public class SmartReplyView extends ViewGroup { OnDismissAction action = () -> { if (mConstants.getEffectiveEditChoicesBeforeSending( smartReplies.remoteInput.getEditChoicesBeforeSending())) { - entry.remoteInputText = choice; + EditedSuggestionInfo editedSuggestionInfo = + new EditedSuggestionInfo(choice, replyIndex); mRemoteInputManager.activateRemoteInput(b, new RemoteInput[] { smartReplies.remoteInput }, smartReplies.remoteInput, - smartReplies.pendingIntent); + smartReplies.pendingIntent, editedSuggestionInfo); return false; } smartReplyController.smartReplySent(entry, replyIndex, b.getText(), - smartReplies.fromAssistant, - NotificationLogger.getNotificationLocation(entry).toMetricsEventEnum()); + NotificationLogger.getNotificationLocation(entry).toMetricsEventEnum(), + false /* modifiedBeforeSending */); Bundle results = new Bundle(); results.putString(smartReplies.remoteInput.getResultKey(), choice.toString()); Intent intent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockInfoTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockInfoTest.java new file mode 100644 index 000000000000..d2b265401cc4 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockInfoTest.java @@ -0,0 +1,84 @@ +/* + * 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 com.android.keyguard.clock; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.verify; + +import android.graphics.Bitmap; +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.function.Supplier; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +public final class ClockInfoTest extends SysuiTestCase { + + @Mock + private Supplier<Bitmap> mMockSupplier; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testGetName() { + final String name = "name"; + ClockInfo info = ClockInfo.builder().setName(name).build(); + assertThat(info.getName()).isEqualTo(name); + } + + @Test + public void testGetTitle() { + final String title = "title"; + ClockInfo info = ClockInfo.builder().setTitle(title).build(); + assertThat(info.getTitle()).isEqualTo(title); + } + + @Test + public void testGetId() { + final String id = "id"; + ClockInfo info = ClockInfo.builder().setId(id).build(); + assertThat(info.getId()).isEqualTo(id); + } + + @Test + public void testGetThumbnail() { + ClockInfo info = ClockInfo.builder().setThumbnail(mMockSupplier).build(); + info.getThumbnail(); + verify(mMockSupplier).get(); + } + + @Test + public void testGetPreview() { + ClockInfo info = ClockInfo.builder().setPreview(mMockSupplier).build(); + info.getPreview(); + verify(mMockSupplier).get(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockOptionsProviderTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockOptionsProviderTest.java new file mode 100644 index 000000000000..0cd6f9af8ffb --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockOptionsProviderTest.java @@ -0,0 +1,189 @@ +/* + * 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 com.android.keyguard.clock; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.verify; + +import android.database.Cursor; +import android.graphics.Bitmap; +import android.net.Uri; +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +public final class ClockOptionsProviderTest extends SysuiTestCase { + + private static final String CONTENT_SCHEME = "content"; + private static final String AUTHORITY = "com.android.keyguard.clock"; + private static final String LIST_OPTIONS = "list_options"; + private static final String PREVIEW = "preview"; + private static final String THUMBNAIL = "thumbnail"; + private static final String MIME_TYPE_LIST_OPTIONS = "vnd.android.cursor.dir/clock_faces"; + private static final String MIME_TYPE_PNG = "image/png"; + private static final String NAME_COLUMN = "name"; + private static final String TITLE_COLUMN = "title"; + private static final String ID_COLUMN = "id"; + private static final String PREVIEW_COLUMN = "preview"; + private static final String THUMBNAIL_COLUMN = "thumbnail"; + + private ClockOptionsProvider mProvider; + private Supplier<List<ClockInfo>> mMockSupplier; + private List<ClockInfo> mClocks; + private Uri mListOptionsUri; + @Mock + private Supplier<Bitmap> mMockBitmapSupplier; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mClocks = new ArrayList<>(); + mProvider = new ClockOptionsProvider(() -> mClocks); + mListOptionsUri = new Uri.Builder() + .scheme(CONTENT_SCHEME) + .authority(AUTHORITY) + .appendPath(LIST_OPTIONS) + .build(); + } + + @Test + public void testGetType_listOptions() { + Uri uri = new Uri.Builder() + .scheme(CONTENT_SCHEME) + .authority(AUTHORITY) + .appendPath(LIST_OPTIONS) + .build(); + assertThat(mProvider.getType(uri)).isEqualTo(MIME_TYPE_LIST_OPTIONS); + } + + @Test + public void testGetType_preview() { + Uri uri = new Uri.Builder() + .scheme(CONTENT_SCHEME) + .authority(AUTHORITY) + .appendPath(PREVIEW) + .appendPath("id") + .build(); + assertThat(mProvider.getType(uri)).isEqualTo(MIME_TYPE_PNG); + } + + @Test + public void testGetType_thumbnail() { + Uri uri = new Uri.Builder() + .scheme(CONTENT_SCHEME) + .authority(AUTHORITY) + .appendPath(THUMBNAIL) + .appendPath("id") + .build(); + assertThat(mProvider.getType(uri)).isEqualTo(MIME_TYPE_PNG); + } + + @Test + public void testQuery_noClocks() { + Cursor cursor = mProvider.query(mListOptionsUri, null, null, null); + assertThat(cursor.getCount()).isEqualTo(0); + } + + @Test + public void testQuery_listOptions() { + mClocks.add(ClockInfo.builder() + .setName("name_a") + .setTitle("title_a") + .setId("id_a") + .build()); + mClocks.add(ClockInfo.builder() + .setName("name_b") + .setTitle("title_b") + .setId("id_b") + .build()); + Cursor cursor = mProvider.query(mListOptionsUri, null, null, null); + assertThat(cursor.getCount()).isEqualTo(2); + cursor.moveToFirst(); + assertThat(cursor.getString( + cursor.getColumnIndex(NAME_COLUMN))).isEqualTo("name_a"); + assertThat(cursor.getString( + cursor.getColumnIndex(TITLE_COLUMN))).isEqualTo("title_a"); + assertThat(cursor.getString( + cursor.getColumnIndex(ID_COLUMN))).isEqualTo("id_a"); + assertThat(cursor.getString( + cursor.getColumnIndex(PREVIEW_COLUMN))) + .isEqualTo("content://com.android.keyguard.clock/preview/id_a"); + assertThat(cursor.getString( + cursor.getColumnIndex(THUMBNAIL_COLUMN))) + .isEqualTo("content://com.android.keyguard.clock/thumbnail/id_a"); + cursor.moveToNext(); + assertThat(cursor.getString( + cursor.getColumnIndex(NAME_COLUMN))).isEqualTo("name_b"); + assertThat(cursor.getString( + cursor.getColumnIndex(TITLE_COLUMN))).isEqualTo("title_b"); + assertThat(cursor.getString( + cursor.getColumnIndex(ID_COLUMN))).isEqualTo("id_b"); + assertThat(cursor.getString( + cursor.getColumnIndex(PREVIEW_COLUMN))) + .isEqualTo("content://com.android.keyguard.clock/preview/id_b"); + assertThat(cursor.getString( + cursor.getColumnIndex(THUMBNAIL_COLUMN))) + .isEqualTo("content://com.android.keyguard.clock/thumbnail/id_b"); + } + + @Test + public void testOpenFile_preview() throws Exception { + mClocks.add(ClockInfo.builder() + .setId("id") + .setPreview(mMockBitmapSupplier) + .build()); + Uri uri = new Uri.Builder() + .scheme(CONTENT_SCHEME) + .authority(AUTHORITY) + .appendPath(PREVIEW) + .appendPath("id") + .build(); + mProvider.openFile(uri, "r").close(); + verify(mMockBitmapSupplier).get(); + } + + @Test + public void testOpenFile_thumbnail() throws Exception { + mClocks.add(ClockInfo.builder() + .setId("id") + .setThumbnail(mMockBitmapSupplier) + .build()); + Uri uri = new Uri.Builder() + .scheme(CONTENT_SCHEME) + .authority(AUTHORITY) + .appendPath(THUMBNAIL) + .appendPath("id") + .build(); + mProvider.openFile(uri, "r").close(); + verify(mMockBitmapSupplier).get(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java index e32d48d2ab94..49b4641399f1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -168,12 +168,14 @@ public class BubbleControllerTest extends SysuiTestCase { // We should have bubbles & their notifs should show in the shade assertTrue(mBubbleController.hasBubbles()); assertTrue(mRow.getEntry().showInShadeWhenBubble()); + assertFalse(mStatusBarWindowController.getBubbleExpanded()); // Expand the stack BubbleStackView stackView = mBubbleController.getStackView(); stackView.expandStack(); assertTrue(mBubbleController.isStackExpanded()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key); + assertTrue(mStatusBarWindowController.getBubbleExpanded()); // Make sure it's no longer in the shade assertFalse(mRow.getEntry().showInShadeWhenBubble()); @@ -182,6 +184,7 @@ public class BubbleControllerTest extends SysuiTestCase { stackView.collapseStack(); verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key); assertFalse(mBubbleController.isStackExpanded()); + assertFalse(mStatusBarWindowController.getBubbleExpanded()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java index 1bb7ef4a657b..c0aac7ee8793 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java @@ -55,11 +55,12 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); mBubblePadding = res.getDimensionPixelSize(R.dimen.bubble_padding); mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size); + + mExpansionPoint = new PointF(100, 100); } @Test public void testExpansionAndCollapse() throws InterruptedException { - mExpansionPoint = new PointF(100, 100); Runnable afterExpand = Mockito.mock(Runnable.class); mExpandedController.expandFromStack(mExpansionPoint, afterExpand); @@ -77,27 +78,48 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC Mockito.verify(afterExpand).run(); } + @Test + public void testOnChildRemoved() throws InterruptedException { + Runnable afterExpand = Mockito.mock(Runnable.class); + mExpandedController.expandFromStack(mExpansionPoint, afterExpand); + waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); + testExpanded(); + + // Remove some views and see if the remaining child views still pass the expansion test. + mLayout.removeView(mViews.get(0)); + mLayout.removeView(mViews.get(3)); + waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); + testExpanded(); + } + /** Check that children are in the correct positions for being stacked. */ private void testStackedAtPosition(float x, float y, int offsetMultiplier) { // Make sure the rest of the stack moved again, including the first bubble not moving, and // is stacked to the right now that we're on the right side of the screen. for (int i = 0; i < mLayout.getChildCount(); i++) { assertEquals(x + i * offsetMultiplier * mStackOffset, - mViews.get(i).getTranslationX(), 2f); - assertEquals(y, mViews.get(i).getTranslationY(), 2f); + mLayout.getChildAt(i).getTranslationX(), 2f); + assertEquals(y, mLayout.getChildAt(i).getTranslationY(), 2f); + + if (i < mMaxRenderedBubbles) { + assertEquals(1f, mLayout.getChildAt(i).getAlpha(), .01f); + } } } /** Check that children are in the correct positions for being expanded. */ private void testExpanded() { - // Make sure the rest of the stack moved again, including the first bubble not moving, and - // is stacked to the right now that we're on the right side of the screen. - for (int i = 0; i < mLayout.getChildCount(); i++) { + // Check all the visible bubbles to see if they're in the right place. + for (int i = 0; i < Math.min(mLayout.getChildCount(), mMaxRenderedBubbles); i++) { assertEquals(mBubblePadding + (i * (mBubbleSize + mBubblePadding)), - mViews.get(i).getTranslationX(), + mLayout.getChildAt(i).getTranslationX(), 2f); assertEquals(mBubblePadding + mCutoutInsetSize, - mViews.get(i).getTranslationY(), 2f); + mLayout.getChildAt(i).getTranslationY(), 2f); + + if (i < mMaxRenderedBubbles) { + assertEquals(1f, mLayout.getChildAt(i).getAlpha(), .01f); + } } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTest.java index 5be991f06586..c32140401fb2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTest.java @@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.never; import android.os.SystemClock; import android.support.test.filters.SmallTest; @@ -100,9 +101,9 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase { mTestableController.setRemoveImmediately(true); mLayout.removeView(mViews.get(1)); mLayout.removeView(mViews.get(2)); - Mockito.verify(mTestableController).onChildToBeRemoved( + Mockito.verify(mTestableController).onChildRemoved( eq(mViews.get(1)), eq(1), any()); - Mockito.verify(mTestableController).onChildToBeRemoved( + Mockito.verify(mTestableController).onChildRemoved( eq(mViews.get(2)), eq(1), any()); // Make sure we still get view added notifications after doing some removals. @@ -345,6 +346,24 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase { assertTrue(mViews.get(0).getTranslationY() < 1000); } + @Test + public void testSetChildVisibility() throws InterruptedException { + mLayout.setController(mTestableController); + addOneMoreThanRenderLimitBubbles(); + + // The last view should have been set to GONE by the controller, since we added one more + // than the limit and it got pushed off. None of the first children should have been set + // VISIBLE, since they would have been animated in by onChildAdded. + Mockito.verify(mTestableController).setChildVisibility( + mViews.get(mViews.size() - 1), 5, View.GONE); + Mockito.verify(mTestableController, never()).setChildVisibility( + any(View.class), anyInt(), eq(View.VISIBLE)); + + // Remove the first view, which should cause the last view to become visible again. + mLayout.removeView(mViews.get(0)); + Mockito.verify(mTestableController).setChildVisibility( + mViews.get(mViews.size() - 1), 4, View.VISIBLE); + } /** Standard test of chained translation animations. */ private void testChainedTranslationAnimations() throws InterruptedException { @@ -440,10 +459,15 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase { void onChildAdded(View child, int index) {} @Override - void onChildToBeRemoved(View child, int index, Runnable actuallyRemove) { + void onChildRemoved(View child, int index, Runnable finishRemoval) { if (mRemoveImmediately) { - actuallyRemove.run(); + finishRemoval.run(); } } + + @Override + protected void setChildVisibility(View child, int index, int visibility) { + super.setChildVisibility(child, index, visibility); + } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java index 850396299ae5..374a73c1d451 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java @@ -16,27 +16,35 @@ package com.android.systemui.qs; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.support.test.filters.SmallTest; +import android.telephony.SubscriptionManager; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.view.LayoutInflater; import android.view.View; +import com.android.keyguard.CarrierTextController.CarrierTextCallbackInfo; import com.android.systemui.R; import com.android.systemui.R.id; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.utils.leaks.LeakCheckedTest; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; @RunWith(AndroidTestingRunner.class) @RunWithLooper @@ -68,4 +76,117 @@ public class QSFooterImplTest extends LeakCheckedTest { // Verify Settings wasn't launched. verify(mActivityStarter, never()).startActivity(any(), anyBoolean()); } + + @Test // throws no Exception + public void testUpdateCarrierText_sameLengts() { + QSFooterImpl spiedFooter = Mockito.spy(mFooter); + when(spiedFooter.getSlotIndex(anyInt())).thenAnswer( + new Answer<Integer>() { + @Override + public Integer answer(InvocationOnMock invocationOnMock) throws Throwable { + return invocationOnMock.getArgument(0); + } + }); + + // listOfCarriers length 1, subscriptionIds length 1, anySims false + CarrierTextCallbackInfo c1 = new CarrierTextCallbackInfo( + "", + new CharSequence[]{""}, + false, + new int[]{0}); + spiedFooter.updateCarrierInfo(c1); + + // listOfCarriers length 1, subscriptionIds length 1, anySims true + CarrierTextCallbackInfo c2 = new CarrierTextCallbackInfo( + "", + new CharSequence[]{""}, + true, + new int[]{0}); + spiedFooter.updateCarrierInfo(c2); + + // listOfCarriers length 2, subscriptionIds length 2, anySims false + CarrierTextCallbackInfo c3 = new CarrierTextCallbackInfo( + "", + new CharSequence[]{"", ""}, + false, + new int[]{0, 1}); + spiedFooter.updateCarrierInfo(c3); + + // listOfCarriers length 2, subscriptionIds length 2, anySims true + CarrierTextCallbackInfo c4 = new CarrierTextCallbackInfo( + "", + new CharSequence[]{"", ""}, + true, + new int[]{0, 1}); + spiedFooter.updateCarrierInfo(c4); + } + + @Test // throws no Exception + public void testUpdateCarrierText_differentLength() { + QSFooterImpl spiedFooter = Mockito.spy(mFooter); + when(spiedFooter.getSlotIndex(anyInt())).thenAnswer( + new Answer<Integer>() { + @Override + public Integer answer(InvocationOnMock invocationOnMock) throws Throwable { + return invocationOnMock.getArgument(0); + } + }); + + // listOfCarriers length 2, subscriptionIds length 1, anySims false + CarrierTextCallbackInfo c1 = new CarrierTextCallbackInfo( + "", + new CharSequence[]{"", ""}, + false, + new int[]{0}); + spiedFooter.updateCarrierInfo(c1); + + // listOfCarriers length 2, subscriptionIds length 1, anySims true + CarrierTextCallbackInfo c2 = new CarrierTextCallbackInfo( + "", + new CharSequence[]{"", ""}, + true, + new int[]{0}); + spiedFooter.updateCarrierInfo(c2); + + // listOfCarriers length 1, subscriptionIds length 2, anySims false + CarrierTextCallbackInfo c3 = new CarrierTextCallbackInfo( + "", + new CharSequence[]{""}, + false, + new int[]{0, 1}); + spiedFooter.updateCarrierInfo(c3); + + // listOfCarriers length 1, subscriptionIds length 2, anySims true + CarrierTextCallbackInfo c4 = new CarrierTextCallbackInfo( + "", + new CharSequence[]{""}, + true, + new int[]{0, 1}); + spiedFooter.updateCarrierInfo(c4); + } + + @Test // throws no Exception + public void testUpdateCarrierText_invalidSim() { + QSFooterImpl spiedFooter = Mockito.spy(mFooter); + when(spiedFooter.getSlotIndex(anyInt())).thenReturn( + SubscriptionManager.INVALID_SIM_SLOT_INDEX); + CarrierTextCallbackInfo c4 = new CarrierTextCallbackInfo( + "", + new CharSequence[]{"", ""}, + true, + new int[]{0, 1}); + spiedFooter.updateCarrierInfo(c4); + } + + @Test // throws no Exception + public void testSetMobileDataIndicators_invalidSim() { + QSFooterImpl spiedFooter = Mockito.spy(mFooter); + when(spiedFooter.getSlotIndex(anyInt())).thenReturn( + SubscriptionManager.INVALID_SIM_SLOT_INDEX); + spiedFooter.setMobileDataIndicators( + mock(NetworkController.IconState.class), + mock(NetworkController.IconState.class), + 0, 0, true, true, "", "", true, 0, true); + } + } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java index f34e1a6b01e7..31cd28019ea9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java @@ -98,8 +98,8 @@ public class SmartReplyControllerTest extends SysuiTestCase { @Test public void testSendSmartReply_updatesRemoteInput() { - mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false, - MetricsEvent.LOCATION_UNKNOWN); + mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, + MetricsEvent.LOCATION_UNKNOWN, false /* modifiedBeforeSending */); // Sending smart reply should make calls to NotificationEntryManager // to update the notification with reply and spinner. @@ -109,48 +109,49 @@ public class SmartReplyControllerTest extends SysuiTestCase { @Test public void testSendSmartReply_logsToStatusBar() throws RemoteException { - mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false, - MetricsEvent.LOCATION_UNKNOWN); + mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, + MetricsEvent.LOCATION_UNKNOWN, false /* modifiedBeforeSending */); // Check we log the result to the status bar service. verify(mIStatusBarService).onNotificationSmartReplySent(mSbn.getKey(), - TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false, MetricsEvent.LOCATION_UNKNOWN); + TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, MetricsEvent.LOCATION_UNKNOWN, false); } @Test - public void testSendSmartReply_logsToStatusBar_generatedByAssistant() throws RemoteException { - mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, true, - MetricsEvent.LOCATION_UNKNOWN); + public void testSendSmartReply_logsToStatusBar_modifiedBeforeSending() throws RemoteException { + mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, + MetricsEvent.LOCATION_UNKNOWN, true /* modifiedBeforeSending */); // Check we log the result to the status bar service. verify(mIStatusBarService).onNotificationSmartReplySent(mSbn.getKey(), - TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, true, MetricsEvent.LOCATION_UNKNOWN); + TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, MetricsEvent.LOCATION_UNKNOWN, true); } @Test public void testShowSmartSuggestions_logsToStatusBar() throws RemoteException { final boolean generatedByAsssistant = true; + final boolean editBeforeSending = true; mSmartReplyController.smartSuggestionsAdded(mEntry, TEST_CHOICE_COUNT, TEST_ACTION_COUNT, - generatedByAsssistant); + generatedByAsssistant, editBeforeSending); // Check we log the result to the status bar service. verify(mIStatusBarService).onNotificationSmartSuggestionsAdded(mSbn.getKey(), - TEST_CHOICE_COUNT, TEST_ACTION_COUNT, generatedByAsssistant); + TEST_CHOICE_COUNT, TEST_ACTION_COUNT, generatedByAsssistant, editBeforeSending); } @Test public void testSendSmartReply_reportsSending() { - mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false, - MetricsEvent.LOCATION_UNKNOWN); + mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, + MetricsEvent.LOCATION_UNKNOWN, false /* modifiedBeforeSending */); assertTrue(mSmartReplyController.isSendingSmartReply(mSbn.getKey())); } @Test public void testSendingSmartReply_afterRemove_shouldReturnFalse() { - mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false, - MetricsEvent.LOCATION_UNKNOWN); + mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, + MetricsEvent.LOCATION_UNKNOWN, false /* modifiedBeforeSending */); mSmartReplyController.stopSending(mEntry); assertFalse(mSmartReplyController.isSendingSmartReply(mSbn.getKey())); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java index 382dde9ce043..dbf00a379c5f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java @@ -23,6 +23,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_DEAD_ZONE; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE; +import static com.android.systemui.statusbar.phone.NavigationBarView.WINDOW_TARGET_BOTTOM; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -63,7 +64,6 @@ import org.mockito.MockitoAnnotations; public class QuickStepControllerTest extends SysuiTestCase { private static final int NAVBAR_WIDTH = 1000; private static final int NAVBAR_HEIGHT = 300; - private static final int EDGE_THRESHOLD = 100; private QuickStepController mController; private NavigationBarView mNavigationBarView; @@ -77,8 +77,6 @@ public class QuickStepControllerTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); final ButtonDispatcher backButton = mock(ButtonDispatcher.class); mResources = mock(Resources.class); - doReturn(EDGE_THRESHOLD).when(mResources) - .getDimensionPixelSize(R.dimen.navigation_bar_edge_swipe_threshold); mProxyService = mock(OverviewProxyService.class); mProxy = mock(IOverviewProxy.Stub.class); @@ -95,6 +93,7 @@ public class QuickStepControllerTest extends SysuiTestCase { doReturn(true).when(mNavigationBarView).isNotificationsFullyCollapsed(); doReturn(true).when(mNavigationBarView).isQuickScrubEnabled(); doReturn(HIT_TARGET_NONE).when(mNavigationBarView).getDownHitTarget(); + doReturn(WINDOW_TARGET_BOTTOM).when(mNavigationBarView).getWindowTarget(); doReturn(backButton).when(mNavigationBarView).getBackButton(); doReturn(mResources).when(mNavigationBarView).getResources(); doReturn(mContext).when(mNavigationBarView).getContext(); @@ -196,10 +195,8 @@ public class QuickStepControllerTest extends SysuiTestCase { NavigationGestureAction swipeDown = mockAction(true); NavigationGestureAction swipeLeft = mockAction(true); NavigationGestureAction swipeRight = mockAction(true); - NavigationGestureAction swipeLeftFromEdge = mockAction(true); - NavigationGestureAction swipeRightFromEdge = mockAction(true); - mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge, - swipeRightFromEdge); + mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, + null /* leftEdgeSwipe */, null /* rightEdgeSwipe */); // Swipe Up assertGestureTriggersAction(swipeUp, 1, 100, 5, 1); @@ -209,10 +206,6 @@ public class QuickStepControllerTest extends SysuiTestCase { assertGestureTriggersAction(swipeLeft, NAVBAR_WIDTH / 2, 1, 5, 1); // Swipe Right assertGestureTriggersAction(swipeRight, NAVBAR_WIDTH / 2, 1, NAVBAR_WIDTH, 5); - // Swipe Left from Edge - assertGestureTriggersAction(swipeLeftFromEdge, NAVBAR_WIDTH, 1, 5, 1); - // Swipe Right from Edge - assertGestureTriggersAction(swipeRightFromEdge, 0, 1, NAVBAR_WIDTH, 5); } @Test @@ -224,10 +217,8 @@ public class QuickStepControllerTest extends SysuiTestCase { NavigationGestureAction swipeDown = mockAction(true); NavigationGestureAction swipeLeft = mockAction(true); NavigationGestureAction swipeRight = mockAction(true); - NavigationGestureAction swipeLeftFromEdge = mockAction(true); - NavigationGestureAction swipeRightFromEdge = mockAction(true); - mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge, - swipeRightFromEdge); + mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, + null /* leftEdgeSwipe */, null /* rightEdgeSwipe */); // In landscape mController.setBarState(false /* isRTL */, NAV_BAR_RIGHT); @@ -240,10 +231,6 @@ public class QuickStepControllerTest extends SysuiTestCase { assertGestureTriggersAction(swipeUp, 100, 1, 5, 1); // Swipe Right assertGestureTriggersAction(swipeDown, 1, 1, 100, 5); - // Swipe Up from Edge - assertGestureTriggersAction(swipeRightFromEdge, 1, NAVBAR_WIDTH, 5, 0); - // Swipe Down from Edge - assertGestureTriggersAction(swipeLeftFromEdge, 0, 1, 0, NAVBAR_WIDTH); } @Test @@ -256,10 +243,8 @@ public class QuickStepControllerTest extends SysuiTestCase { NavigationGestureAction swipeDown = mockAction(true); NavigationGestureAction swipeLeft = mockAction(true); NavigationGestureAction swipeRight = mockAction(true); - NavigationGestureAction swipeLeftFromEdge = mockAction(true); - NavigationGestureAction swipeRightFromEdge = mockAction(true); - mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge, - swipeRightFromEdge); + mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, + null /* leftEdgeSwipe */, null /* rightEdgeSwipe */); // Swipe Up assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, 1); @@ -269,10 +254,6 @@ public class QuickStepControllerTest extends SysuiTestCase { assertGestureTriggersAction(swipeDown, 100, 1, 5, 1); // Swipe Right assertGestureTriggersAction(swipeUp, 1, 1, 100, 5); - // Swipe Up from Edge - assertGestureTriggersAction(swipeLeftFromEdge, 1, NAVBAR_WIDTH, 5, 0); - // Swipe Down from Edge - assertGestureTriggersAction(swipeRightFromEdge, 0, 1, 0, NAVBAR_WIDTH); } @Test @@ -286,10 +267,8 @@ public class QuickStepControllerTest extends SysuiTestCase { NavigationGestureAction swipeDown = mockAction(true); NavigationGestureAction swipeLeft = mockAction(true); NavigationGestureAction swipeRight = mockAction(true); - NavigationGestureAction swipeLeftFromEdge = mockAction(true); - NavigationGestureAction swipeRightFromEdge = mockAction(true); - mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge, - swipeRightFromEdge); + mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, + null /* leftEdgeSwipe */, null /* rightEdgeSwipe */); // Swipe Up in RTL assertGestureTriggersAction(swipeUp, 1, 100, 5, 1); @@ -299,10 +278,6 @@ public class QuickStepControllerTest extends SysuiTestCase { assertGestureTriggersAction(swipeRight, NAVBAR_WIDTH / 2, 1, 5, 1); // Swipe Right in RTL assertGestureTriggersAction(swipeLeft, NAVBAR_WIDTH / 2, 1, NAVBAR_WIDTH, 0); - // Swipe Left from Edge - assertGestureTriggersAction(swipeRightFromEdge, NAVBAR_WIDTH, 1, 5, 1); - // Swipe Right from Edge - assertGestureTriggersAction(swipeLeftFromEdge, 0, 1, NAVBAR_WIDTH, 5); } @Test @@ -316,10 +291,8 @@ public class QuickStepControllerTest extends SysuiTestCase { NavigationGestureAction swipeDown = mockAction(true); NavigationGestureAction swipeLeft = mockAction(true); NavigationGestureAction swipeRight = mockAction(true); - NavigationGestureAction swipeLeftFromEdge = mockAction(true); - NavigationGestureAction swipeRightFromEdge = mockAction(true); - mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge, - swipeRightFromEdge); + mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, + null /* leftEdgeSwipe */, null /* rightEdgeSwipe */); // Swipe Up assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, 1); @@ -329,10 +302,6 @@ public class QuickStepControllerTest extends SysuiTestCase { assertGestureTriggersAction(swipeUp, 100, 1, 5, 1); // Swipe Right assertGestureTriggersAction(swipeDown, 1, 1, 100, 5); - // Swipe Up from Edge - assertGestureTriggersAction(swipeLeftFromEdge, 1, NAVBAR_WIDTH, 5, 0); - // Swipe Down from Edge - assertGestureTriggersAction(swipeRightFromEdge, 0, 1, 0, NAVBAR_WIDTH); } @Test @@ -346,10 +315,8 @@ public class QuickStepControllerTest extends SysuiTestCase { NavigationGestureAction swipeDown = mockAction(true); NavigationGestureAction swipeLeft = mockAction(true); NavigationGestureAction swipeRight = mockAction(true); - NavigationGestureAction swipeLeftFromEdge = mockAction(true); - NavigationGestureAction swipeRightFromEdge = mockAction(true); - mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge, - swipeRightFromEdge); + mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, + null /* leftEdgeSwipe */, null /* rightEdgeSwipe */); // Swipe Up assertGestureTriggersAction(swipeRight, 1, NAVBAR_WIDTH / 2, 5, 1); @@ -359,10 +326,6 @@ public class QuickStepControllerTest extends SysuiTestCase { assertGestureTriggersAction(swipeDown, 100, 1, 5, 1); // Swipe Right assertGestureTriggersAction(swipeUp, 1, 1, 100, 5); - // Swipe Up from Edge - assertGestureTriggersAction(swipeRightFromEdge, 1, NAVBAR_WIDTH, 5, 0); - // Swipe Down from Edge - assertGestureTriggersAction(swipeLeftFromEdge, 0, 1, 0, NAVBAR_WIDTH); } @Test @@ -602,25 +565,6 @@ public class QuickStepControllerTest extends SysuiTestCase { assertGestureDragsHitTargetAllDirections(buttonView, true /* isRTL */, NAV_BAR_LEFT); } - @Test - public void testNoEdgeActionsTriggerNormalActions() { - doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth(); - doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight(); - - NavigationGestureAction swipeUp = mockAction(true); - NavigationGestureAction swipeDown = mockAction(true); - NavigationGestureAction swipeLeft = mockAction(true); - NavigationGestureAction swipeRight = mockAction(true); - mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, - null /* swipeLeftFromEdgeAction */, - null /* swipeLeftFromEdgeAction */); - - // Swipe Left from Edge - assertGestureTriggersAction(swipeLeft, NAVBAR_WIDTH, 1, 5, 1); - // Swipe Right from Edge - assertGestureTriggersAction(swipeRight, 0, 1, NAVBAR_WIDTH, 5); - } - private void assertGestureDragsHitTargetAllDirections(View buttonView, boolean isRTL, int navPos) { mController.setBarState(isRTL, navPos); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java index ed98c62502a4..568ff55f6d34 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java @@ -96,7 +96,7 @@ public class RemoteInputViewTest extends SysuiTestCase { RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).build(); view.setPendingIntent(pendingIntent); - view.setRemoteInput(new RemoteInput[]{input}, input); + view.setRemoteInput(new RemoteInput[]{input}, input, null /* editedSuggestionInfo */); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java index 55701225af71..6793ecaabdd7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java @@ -191,15 +191,7 @@ public class SmartReplyViewTest extends SysuiTestCase { setSmartReplies(TEST_CHOICES); mView.getChildAt(2).performClick(); verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2], - false /* generatedByAsssitant */, MetricsEvent.LOCATION_UNKNOWN); - } - - @Test - public void testSendSmartReply_controllerCalled_generatedByAssistant() { - setSmartReplies(TEST_CHOICES, true); - mView.getChildAt(2).performClick(); - verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2], - true /* generatedByAsssitant */, MetricsEvent.LOCATION_UNKNOWN); + MetricsEvent.LOCATION_UNKNOWN, false /* modifiedBeforeSending */); } @Test diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index 76bb24d81e21..eb8710d6759d 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -6936,6 +6936,19 @@ message MetricsEvent { // OS: Q ACTION_SET_NEW_PARENT_PROFILE_PASSWORD = 1646; + // Tagged data for SMART_REPLY_VISIBLE and SMART_REPLY_ACTION. + // Whether the smart reply was / is to be sent via direct reply because + // getEditChoicesBeforeSending was enabled. + // OS: Q + NOTIFICATION_SMART_REPLY_EDIT_BEFORE_SENDING = 1647; + + // Tagged data for SMART_REPLY_ACTION. + // Whether the smart reply was modified by the user via the direct reply field (implies that + // getEditChoicesBeforeSending was enabled). + // actions/replies. + // OS: Q + NOTIFICATION_SMART_REPLY_MODIFIED_BEFORE_SENDING = 1648; + // ---- End Q Constants, all Q constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java index 7ee304780530..593bb2c41587 100644 --- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java +++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java @@ -20,6 +20,7 @@ import static com.android.server.backup.BackupManagerService.MORE_DEBUG; import static com.android.server.backup.BackupManagerService.TAG; import static com.android.server.backup.UserBackupManagerService.PACKAGE_MANAGER_SENTINEL; import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE; +import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import android.annotation.Nullable; import android.app.AppGlobals; @@ -40,11 +41,18 @@ import com.android.internal.backup.IBackupTransport; import com.android.internal.util.ArrayUtils; import com.android.server.backup.transport.TransportClient; +import com.google.android.collect.Sets; + +import java.util.Set; + /** * Utility methods wrapping operations on ApplicationInfo and PackageInfo. */ public class AppBackupUtils { private static final boolean DEBUG = false; + // Whitelist of system packages that are eligible for backup in non-system users. + private static final Set<String> systemPackagesWhitelistedForAllUsers = + Sets.newArraySet(PACKAGE_MANAGER_SENTINEL, PLATFORM_PACKAGE_NAME); /** * Returns whether app is eligible for backup. @@ -72,9 +80,9 @@ public class AppBackupUtils { // 2. they run as a system-level uid if (UserHandle.isCore(app.uid)) { - // and the backup is happening for non-system user - if (userId != UserHandle.USER_SYSTEM && !app.packageName.equals( - PACKAGE_MANAGER_SENTINEL)) { + // and the backup is happening for non-system user on a non-whitelisted package. + if (userId != UserHandle.USER_SYSTEM + && !systemPackagesWhitelistedForAllUsers.contains(app.packageName)) { return false; } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java index c8a27f165a99..7102b82d5e18 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -416,36 +416,5 @@ final class ContentCapturePerUserService // TODO(b/122595322): implement // TODO(b/119613670): log metrics } - - @Override - public void setActivityContentCaptureEnabled(ComponentName activity, boolean enabled) { - if (mMaster.verbose) { - Log.v(TAG, "setActivityContentCaptureEnabled(activity=" + activity + ", enabled=" - + enabled + ")"); - } - // TODO(b/122595322): implement - // TODO(b/119613670): log metrics - } - - @Override - public void setPackageContentCaptureEnabled(String packageName, boolean enabled) { - if (mMaster.verbose) { - Log.v(TAG, - "setPackageContentCaptureEnabled(packageName=" + packageName + ", enabled=" - + enabled + ")"); - } - // TODO(b/122595322): implement - // TODO(b/119613670): log metrics - } - - @Override - public void getContentCaptureDisabledActivities(IResultReceiver receiver) { - // TODO(b/122595322): implement - } - - @Override - public void getContentCaptureDisabledPackages(IResultReceiver receiver) { - // TODO(b/122595322): implement - } } } diff --git a/services/core/Android.bp b/services/core/Android.bp index f9de55466aeb..259aa50cdd2c 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -19,7 +19,6 @@ java_library_static { ":storaged_aidl", ":vold_aidl", ":gsiservice_aidl", - ":mediaupdateservice_aidl", "java/com/android/server/EventLogTags.logtags", "java/com/android/server/am/EventLogTags.logtags", ], diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index a36c7c0e89b0..1165b094f319 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -114,6 +114,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -669,7 +670,7 @@ public class LocationManagerService extends ILocationManager.Stub { // create a passive location provider, which is always enabled LocationProvider passiveProviderManager = new LocationProvider(PASSIVE_PROVIDER); addProviderLocked(passiveProviderManager); - mPassiveProvider = new PassiveProvider(passiveProviderManager); + mPassiveProvider = new PassiveProvider(mContext, passiveProviderManager); passiveProviderManager.attachLocked(mPassiveProvider); if (GnssLocationProvider.isSupported()) { @@ -805,7 +806,7 @@ public class LocationManagerService extends ILocationManager.Stub { Integer.parseInt(fragments[9]) /* accuracy */); LocationProvider testProviderManager = new LocationProvider(name); addProviderLocked(testProviderManager); - new MockProvider(testProviderManager, properties); + new MockProvider(mContext, testProviderManager, properties); } } @@ -895,15 +896,12 @@ public class LocationManagerService extends ILocationManager.Stub { } @GuardedBy("mLock") - @Nullable - public String getPackageLocked() { + public List<String> getPackagesLocked() { if (mProvider == null) { - return null; - } else if (mProvider instanceof LocationProviderProxy) { - // safe to not clear binder context since this doesn't call into the actual provider - return ((LocationProviderProxy) mProvider).getConnectedPackageName(); + return Collections.emptyList(); } else { - return mContext.getPackageName(); + // safe to not clear binder context since this doesn't call into the real provider + return mProvider.getProviderPackages(); } } @@ -2172,10 +2170,8 @@ public class LocationManagerService extends ILocationManager.Stub { return true; } - for (LocationProvider provider : mProviders) { - if (callerIdentity.mPackageName.equals(provider.getPackageLocked())) { - return true; - } + if (isProviderPackage(callerIdentity.mPackageName)) { + return true; } return false; @@ -2192,10 +2188,8 @@ public class LocationManagerService extends ILocationManager.Stub { return true; } - for (LocationProvider provider : mProviders) { - if (record.mReceiver.mCallerIdentity.mPackageName.equals(provider.getPackageLocked())) { - return true; - } + if (isProviderPackage(record.mReceiver.mCallerIdentity.mPackageName)) { + return true; } return false; @@ -2571,8 +2565,8 @@ public class LocationManagerService extends ILocationManager.Stub { if (provider == null) return null; // only the current user or location providers may get location this way - if (!isCurrentProfileLocked(UserHandle.getUserId(uid)) && !isLocationProviderLocked( - uid)) { + if (!isCurrentProfileLocked(UserHandle.getUserId(uid)) && !isProviderPackage( + packageName)) { return null; } @@ -2894,7 +2888,25 @@ public class LocationManagerService extends ILocationManager.Stub { if (provider == null) { return null; } - return provider.getPackageLocked(); + List<String> packages = provider.getPackagesLocked(); + if (packages.isEmpty()) { + return null; + } else { + return packages.get(0); + } + } + } + + @Override + public boolean isProviderPackage(String packageName) { + synchronized (mLock) { + for (LocationProvider provider : mProviders) { + if (provider.getPackagesLocked().contains(packageName)) { + return true; + } + } + + return false; } } @@ -2976,28 +2988,6 @@ public class LocationManagerService extends ILocationManager.Stub { } @GuardedBy("mLock") - private boolean isLocationProviderLocked(int uid) { - if (uid == Process.SYSTEM_UID) { - return true; - } - - String[] packageNames = mPackageManager.getPackagesForUid(uid); - if (packageNames == null) { - return false; - } - for (LocationProvider provider : mProviders) { - String packageName = provider.getPackageLocked(); - if (packageName == null) { - continue; - } - if (ArrayUtils.contains(packageNames, packageName)) { - return true; - } - } - return false; - } - - @GuardedBy("mLock") private static boolean shouldBroadcastSafeLocked( Location loc, Location lastLoc, UpdateRecord record, long now) { // Always broadcast the first update @@ -3099,7 +3089,7 @@ public class LocationManagerService extends ILocationManager.Stub { int receiverUserId = UserHandle.getUserId(receiver.mCallerIdentity.mUid); if (!isCurrentProfileLocked(receiverUserId) - && !isLocationProviderLocked(receiver.mCallerIdentity.mUid)) { + && !isProviderPackage(receiver.mCallerIdentity.mPackageName)) { if (D) { Log.d(TAG, "skipping loc update for background user " + receiverUserId + " (current user: " + mCurrentUserId + ", app: " + @@ -3288,7 +3278,8 @@ public class LocationManagerService extends ILocationManager.Stub { MockLocationProvider mockProviderManager = new MockLocationProvider(name); addProviderLocked(mockProviderManager); - mockProviderManager.attachLocked(new MockProvider(mockProviderManager, properties)); + mockProviderManager.attachLocked( + new MockProvider(mContext, mockProviderManager, properties)); } finally { Binder.restoreCallingIdentity(identity); } @@ -3428,7 +3419,7 @@ public class LocationManagerService extends ILocationManager.Stub { if (provider.mProvider instanceof LocationProviderProxy) { pw.println(" " + provider.getName() + ": " + ((LocationProviderProxy) provider.mProvider) - .getConnectedPackageName()); + .getProviderPackages()); } } pw.println(" Historical Records by Provider:"); diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index e731f345b704..ddbb421c7f0a 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -80,6 +80,7 @@ class SettingsToPropertiesMapper { DeviceConfig.ActivityManagerNativeBoot.NAMESPACE, DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT, DeviceConfig.NAMESPACE_NETD_NATIVE, + DeviceConfig.RuntimeNativeBoot.NAMESPACE, DeviceConfig.RuntimeNative.NAMESPACE, }; diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING index c84b5c74c1e7..117174a6465e 100644 --- a/services/core/java/com/android/server/am/TEST_MAPPING +++ b/services/core/java/com/android/server/am/TEST_MAPPING @@ -1,17 +1,6 @@ { "presubmit": [ { - "name": "CtsActivityManagerDeviceSdk25TestCases", - "options": [ - { - "include-annotation": "android.platform.test.annotations.Presubmit" - }, - { - "exclude-annotation": "androidx.test.filters.FlakyTest" - } - ] - }, - { "name": "CtsAppTestCases", "options": [ { @@ -38,20 +27,6 @@ "exclude-annotation": "androidx.test.filters.FlakyTest" } ] - }, - { - "name": "WmTests", - "options": [ - { - "include-filter": "com.android.server.am." - }, - { - "include-annotation": "android.platform.test.annotations.Presubmit" - }, - { - "exclude-annotation": "androidx.test.filters.FlakyTest" - } - ] } ], "postsubmit": [ diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index deaa9313fadd..a3bae521a162 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -15,6 +15,9 @@ */ package com.android.server.audio; +import static com.android.server.audio.AudioService.CONNECTION_STATE_CONNECTED; +import static com.android.server.audio.AudioService.CONNECTION_STATE_DISCONNECTED; + import android.annotation.NonNull; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothDevice; @@ -43,7 +46,7 @@ import java.util.ArrayList; /** @hide */ /*package*/ final class AudioDeviceBroker { - private static final String TAG = "AudioDeviceBroker"; + private static final String TAG = "AS.AudioDeviceBroker"; private static final long BROKER_WAKELOCK_TIMEOUT_MS = 5000; //5s @@ -62,27 +65,27 @@ import java.util.ArrayList; private int mForcedUseForCommExt; // Manages all connected devices, only ever accessed on the message loop - //### or make it synchronized private final AudioDeviceInventory mDeviceInventory; // Manages notifications to BT service private final BtHelper mBtHelper; //------------------------------------------------------------------- + // we use a different lock than mDeviceStateLock so as not to create + // lock contention between enqueueing a message and handling them private static final Object sLastDeviceConnectionMsgTimeLock = new Object(); + @GuardedBy("sLastDeviceConnectionMsgTimeLock") private static long sLastDeviceConnectMsgTime = 0; - private final Object mBluetoothA2dpEnabledLock = new Object(); + // General lock to be taken whenever the state of the audio devices is to be checked or changed + private final Object mDeviceStateLock = new Object(); + // Request to override default use of A2DP for media. - @GuardedBy("mBluetoothA2dpEnabledLock") + @GuardedBy("mDeviceStateLock") private boolean mBluetoothA2dpEnabled; - // lock always taken synchronized on mConnectedDevices - /*package*/ final Object mA2dpAvrcpLock = new Object(); - // lock always taken synchronized on mConnectedDevices - /*package*/ final Object mHearingAidLock = new Object(); - // lock always taken when accessing AudioService.mSetModeDeathHandlers + // TODO do not "share" the lock between AudioService and BtHelpr, see b/123769055 /*package*/ final Object mSetModeLock = new Object(); //------------------------------------------------------------------- @@ -109,13 +112,17 @@ import java.util.ArrayList; // All post* methods are asynchronous /*package*/ void onSystemReady() { - mBtHelper.onSystemReady(); + synchronized (mDeviceStateLock) { + mBtHelper.onSystemReady(); + } } /*package*/ void onAudioServerDied() { // Restore forced usage for communications and record - onSetForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, "onAudioServerDied"); - onSetForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm, "onAudioServerDied"); + synchronized (mDeviceStateLock) { + onSetForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, "onAudioServerDied"); + onSetForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm, "onAudioServerDied"); + } // restore devices sendMsgNoDelay(MSG_RESTORE_DEVICES, SENDMSG_REPLACE); } @@ -130,7 +137,9 @@ import java.util.ArrayList; } /*package*/ void disconnectAllBluetoothProfiles() { - mBtHelper.disconnectAllBluetoothProfiles(); + synchronized (mDeviceStateLock) { + mBtHelper.disconnectAllBluetoothProfiles(); + } } /** @@ -140,11 +149,13 @@ import java.util.ArrayList; * @param intent */ /*package*/ void receiveBtEvent(@NonNull Intent intent) { - mBtHelper.receiveBtEvent(intent); + synchronized (mDeviceStateLock) { + mBtHelper.receiveBtEvent(intent); + } } /*package*/ void setBluetoothA2dpOn_Async(boolean on, String source) { - synchronized (mBluetoothA2dpEnabledLock) { + synchronized (mDeviceStateLock) { if (mBluetoothA2dpEnabled == on) { return; } @@ -158,28 +169,34 @@ import java.util.ArrayList; } /*package*/ void setSpeakerphoneOn(boolean on, String eventSource) { - if (on) { - if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) { - setForceUse_Async(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, eventSource); + synchronized (mDeviceStateLock) { + if (on) { + if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) { + setForceUse_Async(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, eventSource); + } + mForcedUseForComm = AudioSystem.FORCE_SPEAKER; + } else if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) { + mForcedUseForComm = AudioSystem.FORCE_NONE; } - mForcedUseForComm = AudioSystem.FORCE_SPEAKER; - } else if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) { - mForcedUseForComm = AudioSystem.FORCE_NONE; - } - mForcedUseForCommExt = mForcedUseForComm; - setForceUse_Async(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource); + mForcedUseForCommExt = mForcedUseForComm; + setForceUse_Async(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource); + } } /*package*/ boolean isSpeakerphoneOn() { - return (mForcedUseForCommExt == AudioSystem.FORCE_SPEAKER); + synchronized (mDeviceStateLock) { + return (mForcedUseForCommExt == AudioSystem.FORCE_SPEAKER); + } } /*package*/ void setWiredDeviceConnectionState(int type, @AudioService.ConnectionState int state, String address, String name, String caller) { //TODO move logging here just like in setBluetooth* methods - mDeviceInventory.setWiredDeviceConnectionState(type, state, address, name, caller); + synchronized (mDeviceStateLock) { + mDeviceInventory.setWiredDeviceConnectionState(type, state, address, name, caller); + } } /*package*/ int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( @@ -192,22 +209,27 @@ import java.util.ArrayList; + " addr=" + device.getAddress() + " prof=" + profile + " supprNoisy=" + suppressNoisyIntent + " vol=" + a2dpVolume)).printLog(TAG)); - if (mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE, - new BtHelper.BluetoothA2dpDeviceInfo(device))) { - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "A2DP connection state ignored")); - return 0; + synchronized (mDeviceStateLock) { + if (mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE, + new BtHelper.BluetoothA2dpDeviceInfo(device))) { + AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( + "A2DP connection state ignored")); + return 0; + } + return mDeviceInventory.setBluetoothA2dpDeviceConnectionState( + device, state, profile, suppressNoisyIntent, + AudioSystem.DEVICE_NONE, a2dpVolume); } - return mDeviceInventory.setBluetoothA2dpDeviceConnectionState( - device, state, profile, suppressNoisyIntent, AudioSystem.DEVICE_NONE, a2dpVolume); } /*package*/ int handleBluetoothA2dpActiveDeviceChange( @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, int profile, boolean suppressNoisyIntent, int a2dpVolume) { - return mDeviceInventory.handleBluetoothA2dpActiveDeviceChange(device, state, profile, - suppressNoisyIntent, a2dpVolume); + synchronized (mDeviceStateLock) { + return mDeviceInventory.handleBluetoothA2dpActiveDeviceChange(device, state, profile, + suppressNoisyIntent, a2dpVolume); + } } /*package*/ int setBluetoothHearingAidDeviceConnectionState( @@ -218,57 +240,69 @@ import java.util.ArrayList; + " addr=" + device.getAddress() + " supprNoisy=" + suppressNoisyIntent + " src=" + eventSource)).printLog(TAG)); - return mDeviceInventory.setBluetoothHearingAidDeviceConnectionState( - device, state, suppressNoisyIntent, musicDevice); + synchronized (mDeviceStateLock) { + return mDeviceInventory.setBluetoothHearingAidDeviceConnectionState( + device, state, suppressNoisyIntent, musicDevice); + } } // never called by system components /*package*/ void setBluetoothScoOnByApp(boolean on) { - mForcedUseForCommExt = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE; + synchronized (mDeviceStateLock) { + mForcedUseForCommExt = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE; + } } /*package*/ boolean isBluetoothScoOnForApp() { - return mForcedUseForCommExt == AudioSystem.FORCE_BT_SCO; + synchronized (mDeviceStateLock) { + return mForcedUseForCommExt == AudioSystem.FORCE_BT_SCO; + } } /*package*/ void setBluetoothScoOn(boolean on, String eventSource) { //Log.i(TAG, "setBluetoothScoOnInt: " + on + " " + eventSource); - if (on) { - // do not accept SCO ON if SCO audio is not connected - if (!mBtHelper.isBluetoothScoOn()) { - mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO; - return; + synchronized (mDeviceStateLock) { + if (on) { + // do not accept SCO ON if SCO audio is not connected + if (!mBtHelper.isBluetoothScoOn()) { + mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO; + return; + } + mForcedUseForComm = AudioSystem.FORCE_BT_SCO; + } else if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) { + mForcedUseForComm = AudioSystem.FORCE_NONE; } - mForcedUseForComm = AudioSystem.FORCE_BT_SCO; - } else if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) { - mForcedUseForComm = AudioSystem.FORCE_NONE; + mForcedUseForCommExt = mForcedUseForComm; + AudioSystem.setParameters("BT_SCO=" + (on ? "on" : "off")); + sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE, + AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource); + sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE, + AudioSystem.FOR_RECORD, mForcedUseForComm, eventSource); + // Un-mute ringtone stream volume + mAudioService.setUpdateRingerModeServiceInt(); } - mForcedUseForCommExt = mForcedUseForComm; - AudioSystem.setParameters("BT_SCO=" + (on ? "on" : "off")); - sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE, - AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource); - sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE, - AudioSystem.FOR_RECORD, mForcedUseForComm, eventSource); - // Un-mute ringtone stream volume - mAudioService.setUpdateRingerModeServiceInt(); } /*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) { - return mDeviceInventory.startWatchingRoutes(observer); + synchronized (mDeviceStateLock) { + return mDeviceInventory.startWatchingRoutes(observer); + } } /*package*/ AudioRoutesInfo getCurAudioRoutes() { - return mDeviceInventory.getCurAudioRoutes(); + synchronized (mDeviceStateLock) { + return mDeviceInventory.getCurAudioRoutes(); + } } /*package*/ boolean isAvrcpAbsoluteVolumeSupported() { - synchronized (mA2dpAvrcpLock) { + synchronized (mDeviceStateLock) { return mBtHelper.isAvrcpAbsoluteVolumeSupported(); } } /*package*/ boolean isBluetoothA2dpOn() { - synchronized (mBluetoothA2dpEnabledLock) { + synchronized (mDeviceStateLock) { return mBluetoothA2dpEnabled; } } @@ -355,14 +389,12 @@ import java.util.ArrayList; sendMsgNoDelay(MSG_BROADCAST_AUDIO_BECOMING_NOISY, SENDMSG_REPLACE); } - //###TODO unify with handleSetA2dpSinkConnectionState /*package*/ void postA2dpSinkConnection(int state, @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) { sendILMsg(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE, SENDMSG_QUEUE, state, btDeviceInfo, delay); } - //###TODO unify with handleSetA2dpSourceConnectionState /*package*/ void postA2dpSourceConnection(int state, @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) { sendILMsg(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE, @@ -395,7 +427,7 @@ import java.util.ArrayList; .append(") from u/pid:").append(Binder.getCallingUid()).append("/") .append(Binder.getCallingPid()).append(" src:").append(source).toString(); - synchronized (mBluetoothA2dpEnabledLock) { + synchronized (mDeviceStateLock) { mBluetoothA2dpEnabled = on; mBrokerHandler.removeMessages(MSG_IIL_SET_FORCE_BT_A2DP_USE); onSetForceUse( @@ -407,25 +439,38 @@ import java.util.ArrayList; /*package*/ boolean handleDeviceConnection(boolean connect, int device, String address, String deviceName) { - return mDeviceInventory.handleDeviceConnection(connect, device, address, deviceName); + synchronized (mDeviceStateLock) { + return mDeviceInventory.handleDeviceConnection(connect, device, address, deviceName); + } } /*package*/ void handleDisconnectA2dp() { - mDeviceInventory.disconnectA2dp(); + synchronized (mDeviceStateLock) { + mDeviceInventory.disconnectA2dp(); + } } /*package*/ void handleDisconnectA2dpSink() { - mDeviceInventory.disconnectA2dpSink(); + synchronized (mDeviceStateLock) { + mDeviceInventory.disconnectA2dpSink(); + } + } + + /*package*/ void handleDisconnectHearingAid() { + synchronized (mDeviceStateLock) { + mDeviceInventory.disconnectHearingAid(); + } } /*package*/ void handleSetA2dpSinkConnectionState(@BluetoothProfile.BtProfileState int state, @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) { - final int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0; - //### DOESN'T HONOR SYNC ON DEVICES -> make a synchronized version? - // might be ok here because called on BT thread? + sync happening in - // checkSendBecomingNoisyIntent - final int delay = mDeviceInventory.checkSendBecomingNoisyIntent( - AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, intState, - AudioSystem.DEVICE_NONE); + final int intState = (state == BluetoothA2dp.STATE_CONNECTED) + ? CONNECTION_STATE_CONNECTED : CONNECTION_STATE_DISCONNECTED; + final int delay; + synchronized (mDeviceStateLock) { + delay = mDeviceInventory.checkSendBecomingNoisyIntent( + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, intState, + AudioSystem.DEVICE_NONE); + } final String addr = btDeviceInfo == null ? "null" : btDeviceInfo.getBtDevice().getAddress(); if (AudioService.DEBUG_DEVICES) { @@ -437,10 +482,6 @@ import java.util.ArrayList; state, btDeviceInfo, delay); } - /*package*/ void handleDisconnectHearingAid() { - mDeviceInventory.disconnectHearingAid(); - } - /*package*/ void handleSetA2dpSourceConnectionState(@BluetoothProfile.BtProfileState int state, @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) { final int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0; @@ -468,8 +509,6 @@ import java.util.ArrayList; sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, SENDMSG_QUEUE, btDeviceInfo); } - //### - // must be called synchronized on mConnectedDevices /*package*/ boolean hasScheduledA2dpDockTimeout() { return mBrokerHandler.hasMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT); } @@ -486,19 +525,19 @@ import java.util.ArrayList; } /*package*/ void setAvrcpAbsoluteVolumeSupported(boolean supported) { - synchronized (mA2dpAvrcpLock) { + synchronized (mDeviceStateLock) { mBtHelper.setAvrcpAbsoluteVolumeSupported(supported); } } /*package*/ boolean getBluetoothA2dpEnabled() { - synchronized (mBluetoothA2dpEnabledLock) { + synchronized (mDeviceStateLock) { return mBluetoothA2dpEnabled; } } /*package*/ int getA2dpCodec(@NonNull BluetoothDevice device) { - synchronized (mA2dpAvrcpLock) { + synchronized (mDeviceStateLock) { return mBtHelper.getA2dpCodec(device); } } @@ -579,71 +618,97 @@ import java.util.ArrayList; public void handleMessage(Message msg) { switch (msg.what) { case MSG_RESTORE_DEVICES: - mDeviceInventory.onRestoreDevices(); - synchronized (mBluetoothA2dpEnabledLock) { + synchronized (mDeviceStateLock) { + mDeviceInventory.onRestoreDevices(); mBtHelper.onAudioServerDiedRestoreA2dp(); } break; case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE: - mDeviceInventory.onSetWiredDeviceConnectionState( - (AudioDeviceInventory.WiredDeviceConnectionState) msg.obj); + synchronized (mDeviceStateLock) { + mDeviceInventory.onSetWiredDeviceConnectionState( + (AudioDeviceInventory.WiredDeviceConnectionState) msg.obj); + } break; case MSG_I_BROADCAST_BT_CONNECTION_STATE: - mBtHelper.onBroadcastScoConnectionState(msg.arg1); + synchronized (mDeviceStateLock) { + mBtHelper.onBroadcastScoConnectionState(msg.arg1); + } break; - case MSG_IIL_SET_FORCE_USE: // intented fall-through + case MSG_IIL_SET_FORCE_USE: // intended fall-through case MSG_IIL_SET_FORCE_BT_A2DP_USE: onSetForceUse(msg.arg1, msg.arg2, (String) msg.obj); break; case MSG_REPORT_NEW_ROUTES: - mDeviceInventory.onReportNewRoutes(); + synchronized (mDeviceStateLock) { + mDeviceInventory.onReportNewRoutes(); + } break; case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE: - mDeviceInventory.onSetA2dpSinkConnectionState( - (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1); + synchronized (mDeviceStateLock) { + mDeviceInventory.onSetA2dpSinkConnectionState( + (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1); + } break; case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE: - mDeviceInventory.onSetA2dpSourceConnectionState( - (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1); + synchronized (mDeviceStateLock) { + mDeviceInventory.onSetA2dpSourceConnectionState( + (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1); + } break; case MSG_IL_SET_HEARING_AID_CONNECTION_STATE: - mDeviceInventory.onSetHearingAidConnectionState( - (BluetoothDevice) msg.obj, msg.arg1); + synchronized (mDeviceStateLock) { + mDeviceInventory.onSetHearingAidConnectionState( + (BluetoothDevice) msg.obj, msg.arg1); + } break; case MSG_BT_HEADSET_CNCT_FAILED: - mBtHelper.resetBluetoothSco(); + synchronized (mDeviceStateLock) { + mBtHelper.resetBluetoothSco(); + } break; case MSG_IL_BTA2DP_DOCK_TIMEOUT: // msg.obj == address of BTA2DP device - mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1); + synchronized (mDeviceStateLock) { + mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1); + } break; case MSG_L_A2DP_DEVICE_CONFIG_CHANGE: final int a2dpCodec; final BluetoothDevice btDevice = (BluetoothDevice) msg.obj; - synchronized (mA2dpAvrcpLock) { + synchronized (mDeviceStateLock) { a2dpCodec = mBtHelper.getA2dpCodec(btDevice); + mDeviceInventory.onBluetoothA2dpDeviceConfigChange( + new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec)); } - mDeviceInventory.onBluetoothA2dpDeviceConfigChange( - new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec)); break; case MSG_BROADCAST_AUDIO_BECOMING_NOISY: onSendBecomingNoisyIntent(); break; case MSG_II_SET_HEARING_AID_VOLUME: - mBtHelper.setHearingAidVolume(msg.arg1, msg.arg2); + synchronized (mDeviceStateLock) { + mBtHelper.setHearingAidVolume(msg.arg1, msg.arg2); + } break; case MSG_I_SET_AVRCP_ABSOLUTE_VOLUME: - mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1); + synchronized (mDeviceStateLock) { + mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1); + } break; case MSG_I_DISCONNECT_BT_SCO: - mBtHelper.disconnectBluetoothSco(msg.arg1); + synchronized (mDeviceStateLock) { + mBtHelper.disconnectBluetoothSco(msg.arg1); + } break; case MSG_TOGGLE_HDMI: - mDeviceInventory.onToggleHdmi(); + synchronized (mDeviceStateLock) { + mDeviceInventory.onToggleHdmi(); + } break; case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE: - mDeviceInventory.onBluetoothA2dpActiveDeviceChange( - (BtHelper.BluetoothA2dpDeviceInfo) msg.obj); + synchronized (mDeviceStateLock) { + mDeviceInventory.onBluetoothA2dpActiveDeviceChange( + (BtHelper.BluetoothA2dpDeviceInfo) msg.obj); + } break; default: Log.wtf(TAG, "Invalid message " + msg.what); diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 97649a741f62..11fdc8f05033 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -162,10 +162,7 @@ public final class AudioDeviceInventory { "A2DP sink connected: device addr=" + address + " state=" + state + " vol=" + a2dpVolume)); - final int a2dpCodec; - synchronized (mDeviceBroker.mA2dpAvrcpLock) { - a2dpCodec = btInfo.getCodec(); - } + final int a2dpCodec = btInfo.getCodec(); synchronized (mConnectedDevices) { final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, @@ -508,22 +505,20 @@ public final class AudioDeviceInventory { /*package*/ void disconnectA2dp() { synchronized (mConnectedDevices) { - synchronized (mDeviceBroker.mA2dpAvrcpLock) { - final ArraySet<String> toRemove = new ArraySet<>(); - // Disconnect ALL DEVICE_OUT_BLUETOOTH_A2DP devices - mConnectedDevices.values().forEach(deviceInfo -> { - if (deviceInfo.mDeviceType == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) { - toRemove.add(deviceInfo.mDeviceAddress); - } - }); - if (toRemove.size() > 0) { - final int delay = checkSendBecomingNoisyIntentInt( - AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, - 0, AudioSystem.DEVICE_NONE); - toRemove.stream().forEach(deviceAddress -> - makeA2dpDeviceUnavailableLater(deviceAddress, delay) - ); + final ArraySet<String> toRemove = new ArraySet<>(); + // Disconnect ALL DEVICE_OUT_BLUETOOTH_A2DP devices + mConnectedDevices.values().forEach(deviceInfo -> { + if (deviceInfo.mDeviceType == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) { + toRemove.add(deviceInfo.mDeviceAddress); } + }); + if (toRemove.size() > 0) { + final int delay = checkSendBecomingNoisyIntentInt( + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, + AudioService.CONNECTION_STATE_DISCONNECTED, AudioSystem.DEVICE_NONE); + toRemove.stream().forEach(deviceAddress -> + makeA2dpDeviceUnavailableLater(deviceAddress, delay) + ); } } } @@ -543,22 +538,20 @@ public final class AudioDeviceInventory { /*package*/ void disconnectHearingAid() { synchronized (mConnectedDevices) { - synchronized (mDeviceBroker.mHearingAidLock) { - final ArraySet<String> toRemove = new ArraySet<>(); - // Disconnect ALL DEVICE_OUT_HEARING_AID devices - mConnectedDevices.values().forEach(deviceInfo -> { - if (deviceInfo.mDeviceType == AudioSystem.DEVICE_OUT_HEARING_AID) { - toRemove.add(deviceInfo.mDeviceAddress); - } - }); - if (toRemove.size() > 0) { - final int delay = checkSendBecomingNoisyIntentInt( - AudioSystem.DEVICE_OUT_HEARING_AID, 0, AudioSystem.DEVICE_NONE); - toRemove.stream().forEach(deviceAddress -> - // TODO delay not used? - makeHearingAidDeviceUnavailable(deviceAddress /*, delay*/) - ); + final ArraySet<String> toRemove = new ArraySet<>(); + // Disconnect ALL DEVICE_OUT_HEARING_AID devices + mConnectedDevices.values().forEach(deviceInfo -> { + if (deviceInfo.mDeviceType == AudioSystem.DEVICE_OUT_HEARING_AID) { + toRemove.add(deviceInfo.mDeviceAddress); } + }); + if (toRemove.size() > 0) { + final int delay = checkSendBecomingNoisyIntentInt( + AudioSystem.DEVICE_OUT_HEARING_AID, 0, AudioSystem.DEVICE_NONE); + toRemove.stream().forEach(deviceAddress -> + // TODO delay not used? + makeHearingAidDeviceUnavailable(deviceAddress /*, delay*/) + ); } } } @@ -566,7 +559,8 @@ public final class AudioDeviceInventory { // must be called before removing the device from mConnectedDevices // musicDevice argument is used when not AudioSystem.DEVICE_NONE instead of querying // from AudioSystem - /*package*/ int checkSendBecomingNoisyIntent(int device, int state, int musicDevice) { + /*package*/ int checkSendBecomingNoisyIntent(int device, + @AudioService.ConnectionState int state, int musicDevice) { synchronized (mConnectedDevices) { return checkSendBecomingNoisyIntentInt(device, state, musicDevice); } @@ -840,8 +834,9 @@ public final class AudioDeviceInventory { // musicDevice argument is used when not AudioSystem.DEVICE_NONE instead of querying // from AudioSystem @GuardedBy("mConnectedDevices") - private int checkSendBecomingNoisyIntentInt(int device, int state, int musicDevice) { - if (state != 0) { + private int checkSendBecomingNoisyIntentInt(int device, + @AudioService.ConnectionState int state, int musicDevice) { + if (state != AudioService.CONNECTION_STATE_DISCONNECTED) { return 0; } if ((device & mBecomingNoisyIntentDevices) == 0) { diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 9457fe3d84f1..de63d0ef8edf 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -786,7 +786,6 @@ public class AudioService extends IAudioService.Stub mPrescaleAbsoluteVolume[i] = preScale[i]; } } - } public void systemReady() { @@ -1711,7 +1710,7 @@ public class AudioService extends IAudioService.Stub Log.d(TAG, "adjustSreamVolume: postSetAvrcpAbsoluteVolumeIndex index=" + newIndex + "stream=" + streamType); } - mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(newIndex); + mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(newIndex / 10); } // Check if volume update should be send to Hearing Aid @@ -3821,7 +3820,6 @@ public class AudioService extends IAudioService.Stub private static void sendMsg(Handler handler, int msg, int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) { - if (existingMsgPolicy == SENDMSG_REPLACE) { handler.removeMessages(msg); } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) { diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index bf325013c7da..b63af8a31cd9 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -16,6 +16,7 @@ package com.android.server.audio; import android.annotation.NonNull; +import android.annotation.Nullable; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; @@ -35,8 +36,6 @@ import android.os.UserHandle; import android.provider.Settings; import android.util.Log; -import com.android.internal.annotations.GuardedBy; - import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; @@ -57,21 +56,40 @@ public class BtHelper { } // List of clients having issued a SCO start request - private final ArrayList<ScoClient> mScoClients = new ArrayList<ScoClient>(); + private final @NonNull ArrayList<ScoClient> mScoClients = new ArrayList<ScoClient>(); // BluetoothHeadset API to control SCO connection - private BluetoothHeadset mBluetoothHeadset; + private @Nullable BluetoothHeadset mBluetoothHeadset; // Bluetooth headset device - private BluetoothDevice mBluetoothHeadsetDevice; + private @Nullable BluetoothDevice mBluetoothHeadsetDevice; + + private @Nullable BluetoothHearingAid mHearingAid; + + // Reference to BluetoothA2dp to query for AbsoluteVolume. + private @Nullable BluetoothA2dp mA2dp; + + // If absolute volume is supported in AVRCP device + private boolean mAvrcpAbsVolSupported = false; + + // Current connection state indicated by bluetooth headset + private int mScoConnectionState; // Indicate if SCO audio connection is currently active and if the initiator is // audio service (internal) or bluetooth headset (external) private int mScoAudioState; + + // Indicates the mode used for SCO audio connection. The mode is virtual call if the request + // originated from an app targeting an API version before JB MR2 and raw audio after that. + private int mScoAudioMode; + // SCO audio state is not active private static final int SCO_STATE_INACTIVE = 0; // SCO audio activation request waiting for headset service to connect private static final int SCO_STATE_ACTIVATE_REQ = 1; + // SCO audio state is active due to an action in BT handsfree (either voice recognition or + // in call audio) + private static final int SCO_STATE_ACTIVE_EXTERNAL = 2; // SCO audio state is active or starting due to a request from AudioManager API private static final int SCO_STATE_ACTIVE_INTERNAL = 3; // SCO audio deactivation request waiting for headset service to connect @@ -79,39 +97,19 @@ public class BtHelper { // SCO audio deactivation in progress, waiting for Bluetooth audio intent private static final int SCO_STATE_DEACTIVATING = 5; - // SCO audio state is active due to an action in BT handsfree (either voice recognition or - // in call audio) - private static final int SCO_STATE_ACTIVE_EXTERNAL = 2; - - // Indicates the mode used for SCO audio connection. The mode is virtual call if the request - // originated from an app targeting an API version before JB MR2 and raw audio after that. - private int mScoAudioMode; // SCO audio mode is undefined - /*package*/ static final int SCO_MODE_UNDEFINED = -1; + /*package*/ static final int SCO_MODE_UNDEFINED = -1; // SCO audio mode is virtual voice call (BluetoothHeadset.startScoUsingVirtualVoiceCall()) /*package*/ static final int SCO_MODE_VIRTUAL_CALL = 0; // SCO audio mode is raw audio (BluetoothHeadset.connectAudio()) private static final int SCO_MODE_RAW = 1; // SCO audio mode is Voice Recognition (BluetoothHeadset.startVoiceRecognition()) private static final int SCO_MODE_VR = 2; - + // max valid SCO audio mode values private static final int SCO_MODE_MAX = 2; - // Current connection state indicated by bluetooth headset - private int mScoConnectionState; - private static final int BT_HEARING_AID_GAIN_MIN = -128; - @GuardedBy("mDeviceBroker.mHearingAidLock") - private BluetoothHearingAid mHearingAid; - - // Reference to BluetoothA2dp to query for AbsoluteVolume. - @GuardedBy("mDeviceBroker.mA2dpAvrcpLock") - private BluetoothA2dp mA2dp; - // If absolute volume is supported in AVRCP device - @GuardedBy("mDeviceBroker.mA2dpAvrcpLock") - private boolean mAvrcpAbsVolSupported = false; - //---------------------------------------------------------------------- /*package*/ static class BluetoothA2dpDeviceInfo { private final @NonNull BluetoothDevice mBtDevice; @@ -144,7 +142,7 @@ public class BtHelper { //---------------------------------------------------------------------- // Interface for AudioDeviceBroker - /*package*/ void onSystemReady() { + /*package*/ synchronized void onSystemReady() { mScoConnectionState = android.media.AudioManager.SCO_AUDIO_STATE_ERROR; resetBluetoothSco(); getBluetoothHeadset(); @@ -165,45 +163,39 @@ public class BtHelper { } } - @GuardedBy("mBluetoothA2dpEnabledLock") - /*package*/ void onAudioServerDiedRestoreA2dp() { + /*package*/ synchronized void onAudioServerDiedRestoreA2dp() { final int forMed = mDeviceBroker.getBluetoothA2dpEnabled() ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP; mDeviceBroker.setForceUse_Async(AudioSystem.FOR_MEDIA, forMed, "onAudioServerDied()"); } - @GuardedBy("mA2dpAvrcpLock") - /*package*/ boolean isAvrcpAbsoluteVolumeSupported() { + /*package*/ synchronized boolean isAvrcpAbsoluteVolumeSupported() { return (mA2dp != null && mAvrcpAbsVolSupported); } - @GuardedBy("mA2dpAvrcpLock") - /*package*/ void setAvrcpAbsoluteVolumeSupported(boolean supported) { + /*package*/ synchronized void setAvrcpAbsoluteVolumeSupported(boolean supported) { mAvrcpAbsVolSupported = supported; } - /*package*/ void setAvrcpAbsoluteVolumeIndex(int index) { - synchronized (mDeviceBroker.mA2dpAvrcpLock) { - if (mA2dp == null) { - if (AudioService.DEBUG_VOL) { - Log.d(TAG, "setAvrcpAbsoluteVolumeIndex: bailing due to null mA2dp"); - return; - } - } - if (!mAvrcpAbsVolSupported) { - return; - } + /*package*/ synchronized void setAvrcpAbsoluteVolumeIndex(int index) { + if (mA2dp == null) { if (AudioService.DEBUG_VOL) { - Log.i(TAG, "setAvrcpAbsoluteVolumeIndex index=" + index); + Log.d(TAG, "setAvrcpAbsoluteVolumeIndex: bailing due to null mA2dp"); + return; } - AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent( - AudioServiceEvents.VolumeEvent.VOL_SET_AVRCP_VOL, index / 10)); - mA2dp.setAvrcpAbsoluteVolume(index / 10); } + if (!mAvrcpAbsVolSupported) { + return; + } + if (AudioService.DEBUG_VOL) { + Log.i(TAG, "setAvrcpAbsoluteVolumeIndex index=" + index); + } + AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent( + AudioServiceEvents.VolumeEvent.VOL_SET_AVRCP_VOL, index)); + mA2dp.setAvrcpAbsoluteVolume(index); } - @GuardedBy("mA2dpAvrcpLock") - /*package*/ int getA2dpCodec(@NonNull BluetoothDevice device) { + /*package*/ synchronized int getA2dpCodec(@NonNull BluetoothDevice device) { if (mA2dp == null) { return AudioSystem.AUDIO_FORMAT_DEFAULT; } @@ -218,7 +210,7 @@ public class BtHelper { return mapBluetoothCodecToAudioFormat(btCodecConfig.getCodecType()); } - /*package*/ void receiveBtEvent(Intent intent) { + /*package*/ synchronized void receiveBtEvent(Intent intent) { final String action = intent.getAction(); if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) { BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); @@ -226,53 +218,51 @@ public class BtHelper { } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { boolean broadcast = false; int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR; - synchronized (mScoClients) { - int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); - // broadcast intent if the connection was initated by AudioService - if (!mScoClients.isEmpty() - && (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL - || mScoAudioState == SCO_STATE_ACTIVATE_REQ - || mScoAudioState == SCO_STATE_DEACTIVATE_REQ - || mScoAudioState == SCO_STATE_DEACTIVATING)) { - broadcast = true; - } - switch (btState) { - case BluetoothHeadset.STATE_AUDIO_CONNECTED: - scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED; - if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL - && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) { - mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; - } - mDeviceBroker.setBluetoothScoOn(true, "BtHelper.receiveBtEvent"); - break; - case BluetoothHeadset.STATE_AUDIO_DISCONNECTED: - mDeviceBroker.setBluetoothScoOn(false, "BtHelper.receiveBtEvent"); - scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED; - // startBluetoothSco called after stopBluetoothSco - if (mScoAudioState == SCO_STATE_ACTIVATE_REQ) { - if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null - && connectBluetoothScoAudioHelper(mBluetoothHeadset, - mBluetoothHeadsetDevice, mScoAudioMode)) { - mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; - broadcast = false; - break; - } - } - // Tear down SCO if disconnected from external - clearAllScoClients(0, mScoAudioState == SCO_STATE_ACTIVE_INTERNAL); - mScoAudioState = SCO_STATE_INACTIVE; - break; - case BluetoothHeadset.STATE_AUDIO_CONNECTING: - if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL - && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) { - mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; + int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); + // broadcast intent if the connection was initated by AudioService + if (!mScoClients.isEmpty() + && (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL + || mScoAudioState == SCO_STATE_ACTIVATE_REQ + || mScoAudioState == SCO_STATE_DEACTIVATE_REQ + || mScoAudioState == SCO_STATE_DEACTIVATING)) { + broadcast = true; + } + switch (btState) { + case BluetoothHeadset.STATE_AUDIO_CONNECTED: + scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED; + if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL + && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) { + mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; + } + mDeviceBroker.setBluetoothScoOn(true, "BtHelper.receiveBtEvent"); + break; + case BluetoothHeadset.STATE_AUDIO_DISCONNECTED: + mDeviceBroker.setBluetoothScoOn(false, "BtHelper.receiveBtEvent"); + scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED; + // startBluetoothSco called after stopBluetoothSco + if (mScoAudioState == SCO_STATE_ACTIVATE_REQ) { + if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null + && connectBluetoothScoAudioHelper(mBluetoothHeadset, + mBluetoothHeadsetDevice, mScoAudioMode)) { + mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; + broadcast = false; + break; } - break; - default: - // do not broadcast CONNECTING or invalid state - broadcast = false; - break; - } + } + // Tear down SCO if disconnected from external + clearAllScoClients(0, mScoAudioState == SCO_STATE_ACTIVE_INTERNAL); + mScoAudioState = SCO_STATE_INACTIVE; + break; + case BluetoothHeadset.STATE_AUDIO_CONNECTING: + if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL + && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) { + mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; + } + break; + default: + // do not broadcast CONNECTING or invalid state + broadcast = false; + break; } if (broadcast) { broadcastScoConnectionState(scoAudioState); @@ -289,15 +279,13 @@ public class BtHelper { * * @return false if SCO isn't connected */ - /*package*/ boolean isBluetoothScoOn() { - synchronized (mScoClients) { - if ((mBluetoothHeadset != null) - && (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) - != BluetoothHeadset.STATE_AUDIO_CONNECTED)) { - Log.w(TAG, "isBluetoothScoOn(true) returning false because " - + mBluetoothHeadsetDevice + " is not in audio connected mode"); - return false; - } + /*package*/ synchronized boolean isBluetoothScoOn() { + if ((mBluetoothHeadset != null) + && (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) + != BluetoothHeadset.STATE_AUDIO_CONNECTED)) { + Log.w(TAG, "isBluetoothScoOn(true) returning false because " + + mBluetoothHeadsetDevice + " is not in audio connected mode"); + return false; } return true; } @@ -308,17 +296,15 @@ public class BtHelper { * * @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept */ - /*package*/ void disconnectBluetoothSco(int exceptPid) { - synchronized (mScoClients) { - checkScoAudioState(); - if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) { - return; - } - clearAllScoClients(exceptPid, true); + /*package*/ synchronized void disconnectBluetoothSco(int exceptPid) { + checkScoAudioState(); + if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) { + return; } + clearAllScoClients(exceptPid, true); } - /*package*/ void startBluetoothScoForClient(IBinder cb, int scoAudioMode, + /*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode, @NonNull String eventSource) { ScoClient client = getScoClient(cb, true); // The calling identity must be cleared before calling ScoClient.incCount(). @@ -337,7 +323,8 @@ public class BtHelper { Binder.restoreCallingIdentity(ident); } - /*package*/ void stopBluetoothScoForClient(IBinder cb, @NonNull String eventSource) { + /*package*/ synchronized void stopBluetoothScoForClient(IBinder cb, + @NonNull String eventSource) { ScoClient client = getScoClient(cb, false); // The calling identity must be cleared before calling ScoClient.decCount(). // decCount() calls requestScoState() which in turn can call BluetoothHeadset APIs @@ -352,36 +339,29 @@ public class BtHelper { } - /*package*/ void setHearingAidVolume(int index, int streamType) { - synchronized (mDeviceBroker.mHearingAidLock) { - if (mHearingAid == null) { - if (AudioService.DEBUG_VOL) { - Log.i(TAG, "setHearingAidVolume: null mHearingAid"); - } - return; - } - //hearing aid expect volume value in range -128dB to 0dB - int gainDB = (int) AudioSystem.getStreamVolumeDB(streamType, index / 10, - AudioSystem.DEVICE_OUT_HEARING_AID); - if (gainDB < BT_HEARING_AID_GAIN_MIN) { - gainDB = BT_HEARING_AID_GAIN_MIN; - } + /*package*/ synchronized void setHearingAidVolume(int index, int streamType) { + if (mHearingAid == null) { if (AudioService.DEBUG_VOL) { - Log.i(TAG, "setHearingAidVolume: calling mHearingAid.setVolume idx=" - + index + " gain=" + gainDB); + Log.i(TAG, "setHearingAidVolume: null mHearingAid"); } - AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent( - AudioServiceEvents.VolumeEvent.VOL_SET_HEARING_AID_VOL, index, gainDB)); - mHearingAid.setVolume(gainDB); + return; } + //hearing aid expect volume value in range -128dB to 0dB + int gainDB = (int) AudioSystem.getStreamVolumeDB(streamType, index / 10, + AudioSystem.DEVICE_OUT_HEARING_AID); + if (gainDB < BT_HEARING_AID_GAIN_MIN) { + gainDB = BT_HEARING_AID_GAIN_MIN; + } + if (AudioService.DEBUG_VOL) { + Log.i(TAG, "setHearingAidVolume: calling mHearingAid.setVolume idx=" + + index + " gain=" + gainDB); + } + AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent( + AudioServiceEvents.VolumeEvent.VOL_SET_HEARING_AID_VOL, index, gainDB)); + mHearingAid.setVolume(gainDB); } - //---------------------------------------------------------------------- - private void broadcastScoConnectionState(int state) { - mDeviceBroker.broadcastScoConnectionState(state); - } - - /*package*/ void onBroadcastScoConnectionState(int state) { + /*package*/ synchronized void onBroadcastScoConnectionState(int state) { if (state == mScoConnectionState) { return; } @@ -393,6 +373,26 @@ public class BtHelper { mScoConnectionState = state; } + /*package*/ synchronized void disconnectAllBluetoothProfiles() { + mDeviceBroker.handleDisconnectA2dp(); + mDeviceBroker.handleDisconnectA2dpSink(); + disconnectHeadset(); + mDeviceBroker.handleDisconnectHearingAid(); + } + + /*package*/ synchronized void resetBluetoothSco() { + clearAllScoClients(0, false); + mScoAudioState = SCO_STATE_INACTIVE; + broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); + AudioSystem.setParameters("A2dpSuspended=false"); + mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco"); + } + + //---------------------------------------------------------------------- + private void broadcastScoConnectionState(int state) { + mDeviceBroker.broadcastScoConnectionState(state); + } + private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) { if (btDevice == null) { return true; @@ -437,25 +437,23 @@ public class BtHelper { } private void setBtScoActiveDevice(BluetoothDevice btDevice) { - synchronized (mScoClients) { - Log.i(TAG, "setBtScoActiveDevice: " + mBluetoothHeadsetDevice + " -> " + btDevice); - final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice; - if (Objects.equals(btDevice, previousActiveDevice)) { - return; - } - if (!handleBtScoActiveDeviceChange(previousActiveDevice, false)) { - Log.w(TAG, "setBtScoActiveDevice() failed to remove previous device " - + previousActiveDevice); - } - if (!handleBtScoActiveDeviceChange(btDevice, true)) { - Log.e(TAG, "setBtScoActiveDevice() failed to add new device " + btDevice); - // set mBluetoothHeadsetDevice to null when failing to add new device - btDevice = null; - } - mBluetoothHeadsetDevice = btDevice; - if (mBluetoothHeadsetDevice == null) { - resetBluetoothSco(); - } + Log.i(TAG, "setBtScoActiveDevice: " + mBluetoothHeadsetDevice + " -> " + btDevice); + final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice; + if (Objects.equals(btDevice, previousActiveDevice)) { + return; + } + if (!handleBtScoActiveDeviceChange(previousActiveDevice, false)) { + Log.w(TAG, "setBtScoActiveDevice() failed to remove previous device " + + previousActiveDevice); + } + if (!handleBtScoActiveDeviceChange(btDevice, true)) { + Log.e(TAG, "setBtScoActiveDevice() failed to add new device " + btDevice); + // set mBluetoothHeadsetDevice to null when failing to add new device + btDevice = null; + } + mBluetoothHeadsetDevice = btDevice; + if (mBluetoothHeadsetDevice == null) { + resetBluetoothSco(); } } @@ -466,7 +464,7 @@ public class BtHelper { List<BluetoothDevice> deviceList; switch(profile) { case BluetoothProfile.A2DP: - synchronized (mDeviceBroker.mA2dpAvrcpLock) { + synchronized (BtHelper.this) { mA2dp = (BluetoothA2dp) proxy; deviceList = mA2dp.getConnectedDevices(); if (deviceList.size() > 0) { @@ -495,7 +493,7 @@ public class BtHelper { break; case BluetoothProfile.HEADSET: - synchronized (mScoClients) { + synchronized (BtHelper.this) { // Discard timeout message mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService(); mBluetoothHeadset = (BluetoothHeadset) proxy; @@ -536,17 +534,19 @@ public class BtHelper { break; case BluetoothProfile.HEARING_AID: - mHearingAid = (BluetoothHearingAid) proxy; - deviceList = mHearingAid.getConnectedDevices(); - if (deviceList.size() > 0) { - btDevice = deviceList.get(0); - final @BluetoothProfile.BtProfileState int state = - mHearingAid.getConnectionState(btDevice); - mDeviceBroker.setBluetoothHearingAidDeviceConnectionState( - btDevice, state, - /*suppressNoisyIntent*/ false, - /*musicDevice*/ android.media.AudioSystem.DEVICE_NONE, - /*eventSource*/ "mBluetoothProfileServiceListener"); + synchronized (BtHelper.this) { + mHearingAid = (BluetoothHearingAid) proxy; + deviceList = mHearingAid.getConnectedDevices(); + if (deviceList.size() > 0) { + btDevice = deviceList.get(0); + final @BluetoothProfile.BtProfileState int state = + mHearingAid.getConnectionState(btDevice); + mDeviceBroker.setBluetoothHearingAidDeviceConnectionState( + btDevice, state, + /*suppressNoisyIntent*/ false, + /*musicDevice*/ android.media.AudioSystem.DEVICE_NONE, + /*eventSource*/ "mBluetoothProfileServiceListener"); + } } break; @@ -579,18 +579,9 @@ public class BtHelper { } }; - void disconnectAllBluetoothProfiles() { - mDeviceBroker.handleDisconnectA2dp(); - mDeviceBroker.handleDisconnectA2dpSink(); - disconnectHeadset(); - mDeviceBroker.handleDisconnectHearingAid(); - } - private void disconnectHeadset() { - synchronized (mScoClients) { - setBtScoActiveDevice(null); - mBluetoothHeadset = null; - } + setBtScoActiveDevice(null); + mBluetoothHeadset = null; } //---------------------------------------------------------------------- @@ -605,8 +596,12 @@ public class BtHelper { mStartcount = 0; } + @Override public void binderDied() { - synchronized (mScoClients) { + // this is the only place the implementation of ScoClient needs to be synchronized + // on the instance, as all other methods are directly or indirectly called from + // package-private methods, which are synchronized + synchronized (BtHelper.this) { Log.w(TAG, "SCO client died"); int index = mScoClients.indexOf(this); if (index < 0) { @@ -618,77 +613,69 @@ public class BtHelper { } } - public void incCount(int scoAudioMode) { - synchronized (mScoClients) { - requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode); - if (mStartcount == 0) { - try { - mCb.linkToDeath(this, 0); - } catch (RemoteException e) { - // client has already died! - Log.w(TAG, "ScoClient incCount() could not link to " - + mCb + " binder death"); - } + void incCount(int scoAudioMode) { + requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode); + if (mStartcount == 0) { + try { + mCb.linkToDeath(this, 0); + } catch (RemoteException e) { + // client has already died! + Log.w(TAG, "ScoClient incCount() could not link to " + + mCb + " binder death"); } - mStartcount++; } + mStartcount++; } - public void decCount() { - synchronized (mScoClients) { + void decCount() { + if (mStartcount == 0) { + Log.w(TAG, "ScoClient.decCount() already 0"); + } else { + mStartcount--; if (mStartcount == 0) { - Log.w(TAG, "ScoClient.decCount() already 0"); - } else { - mStartcount--; - if (mStartcount == 0) { - try { - mCb.unlinkToDeath(this, 0); - } catch (NoSuchElementException e) { - Log.w(TAG, "decCount() going to 0 but not registered to binder"); - } - } - requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 0); - } - } - } - - public void clearCount(boolean stopSco) { - synchronized (mScoClients) { - if (mStartcount != 0) { try { mCb.unlinkToDeath(this, 0); } catch (NoSuchElementException e) { - Log.w(TAG, "clearCount() mStartcount: " - + mStartcount + " != 0 but not registered to binder"); + Log.w(TAG, "decCount() going to 0 but not registered to binder"); } } - mStartcount = 0; - if (stopSco) { - requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 0); + requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 0); + } + } + + void clearCount(boolean stopSco) { + if (mStartcount != 0) { + try { + mCb.unlinkToDeath(this, 0); + } catch (NoSuchElementException e) { + Log.w(TAG, "clearCount() mStartcount: " + + mStartcount + " != 0 but not registered to binder"); } } + mStartcount = 0; + if (stopSco) { + requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 0); + } } - public int getCount() { + int getCount() { return mStartcount; } - public IBinder getBinder() { + IBinder getBinder() { return mCb; } - public int getPid() { + int getPid() { return mCreatorPid; } - public int totalCount() { - synchronized (mScoClients) { - int count = 0; - for (ScoClient mScoClient : mScoClients) { - count += mScoClient.getCount(); - } - return count; + private int totalCount() { + int count = 0; + for (ScoClient mScoClient : mScoClients) { + count += mScoClient.getCount(); } + return count; } private void requestScoState(int state, int scoAudioMode) { @@ -705,6 +692,7 @@ public class BtHelper { broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING); // Accept SCO audio activation only in NORMAL audio mode or if the mode is // currently controlled by the same client process. + // TODO do not sync that way, see b/123769055 synchronized (mDeviceBroker.mSetModeLock) { int modeOwnerPid = mDeviceBroker.getSetModeDeathHandlers().isEmpty() ? 0 : mDeviceBroker.getSetModeDeathHandlers().get(0).getPid(); @@ -857,61 +845,44 @@ public class BtHelper { } } - /*package*/ void resetBluetoothSco() { - synchronized (mScoClients) { - clearAllScoClients(0, false); - mScoAudioState = SCO_STATE_INACTIVE; - broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); - } - AudioSystem.setParameters("A2dpSuspended=false"); - mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco"); - } - - private void checkScoAudioState() { - synchronized (mScoClients) { - if (mBluetoothHeadset != null - && mBluetoothHeadsetDevice != null - && mScoAudioState == SCO_STATE_INACTIVE - && mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) - != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { - mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; - } + if (mBluetoothHeadset != null + && mBluetoothHeadsetDevice != null + && mScoAudioState == SCO_STATE_INACTIVE + && mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) + != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { + mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; } } private ScoClient getScoClient(IBinder cb, boolean create) { - synchronized (mScoClients) { - for (ScoClient existingClient : mScoClients) { - if (existingClient.getBinder() == cb) { - return existingClient; - } + for (ScoClient existingClient : mScoClients) { + if (existingClient.getBinder() == cb) { + return existingClient; } - if (create) { - ScoClient newClient = new ScoClient(cb); - mScoClients.add(newClient); - return newClient; - } - return null; } + if (create) { + ScoClient newClient = new ScoClient(cb); + mScoClients.add(newClient); + return newClient; + } + return null; } private void clearAllScoClients(int exceptPid, boolean stopSco) { - synchronized (mScoClients) { - ScoClient savedClient = null; - for (ScoClient cl : mScoClients) { - if (cl.getPid() != exceptPid) { - cl.clearCount(stopSco); - } else { - savedClient = cl; - } - } - mScoClients.clear(); - if (savedClient != null) { - mScoClients.add(savedClient); + ScoClient savedClient = null; + for (ScoClient cl : mScoClients) { + if (cl.getPid() != exceptPid) { + cl.clearCount(stopSco); + } else { + savedClient = cl; } } + mScoClients.clear(); + if (savedClient != null) { + mScoClients.add(savedClient); + } } private boolean getBluetoothHeadset() { diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java index 3f90dfec52ba..67109867c074 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java @@ -781,7 +781,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { int maxVolume = mService.getAudioManager().getStreamMaxVolume(AudioManager.STREAM_MUSIC); int minVolume = mService.getAudioManager().getStreamMinVolume(AudioManager.STREAM_MUSIC); int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume); - HdmiLogger.debug("Reporting volume %i (%i-%i) as CEC volume %i", volume, + HdmiLogger.debug("Reporting volume %d (%d-%d) as CEC volume %d", volume, minVolume, maxVolume, scaledVolume); mService.sendCecCommand( diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index d20508a5e704..f0dce78722a2 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -1645,10 +1645,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @Override - public List<InputMethodInfo> getEnabledInputMethodList() { - final int callingUserId = UserHandle.getCallingUserId(); + public List<InputMethodInfo> getEnabledInputMethodList(@UserIdInt int userId) { + if (UserHandle.getCallingUserId() != userId) { + mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); + } synchronized (mMethodMap) { - final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId, + final int[] resolvedUserIds = InputMethodUtils.resolveUserId(userId, mSettings.getCurrentUserId(), null); if (resolvedUserIds.length != 1) { return Collections.emptyList(); @@ -4697,7 +4699,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mSettings.getCurrentUserId(), mContext.getBasePackageName()); nextIme = mSettings.getSelectedInputMethod(); - nextEnabledImes = getEnabledInputMethodList(); + nextEnabledImes = mSettings.getEnabledInputMethodListLocked(); final PrintWriter pr = shellCommand.getOutPrintWriter(); pr.println("Reset current and enabled IMEs"); pr.println("Newly selected IME:"); diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index 3222143fc89f..109024d0d2f7 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -16,6 +16,7 @@ package com.android.server.inputmethod; +import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static java.lang.annotation.RetentionPolicy.SOURCE; @@ -1195,6 +1196,7 @@ public final class MultiClientInputMethodManagerService { * Takes care of IPCs exposed to the IME client. */ private static final class ApiCallbacks extends IInputMethodManager.Stub { + private final Context mContext; private final UserDataMap mUserDataMap; private final UserToInputMethodInfoMap mInputMethodInfoMap; private final AppOpsManager mAppOpsManager; @@ -1202,6 +1204,7 @@ public final class MultiClientInputMethodManagerService { ApiCallbacks(Context context, UserDataMap userDataMap, UserToInputMethodInfoMap inputMethodInfoMap) { + mContext = context; mUserDataMap = userDataMap; mInputMethodInfoMap = inputMethodInfoMap; mAppOpsManager = context.getSystemService(AppOpsManager.class); @@ -1239,8 +1242,11 @@ public final class MultiClientInputMethodManagerService { @BinderThread @Override - public List<InputMethodInfo> getEnabledInputMethodList() { - return mInputMethodInfoMap.getAsList(UserHandle.getUserId(Binder.getCallingUid())); + public List<InputMethodInfo> getEnabledInputMethodList(@UserIdInt int userId) { + if (UserHandle.getCallingUserId() != userId) { + mContext.enforceCallingPermission(INTERACT_ACROSS_USERS_FULL, null); + } + return mInputMethodInfoMap.getAsList(userId); } @BinderThread diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index cefe583fc5d1..19d10ecfb34d 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -509,7 +509,7 @@ public class JobSchedulerService extends com.android.server.SystemService private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f; private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f; private static final boolean DEFAULT_USE_HEARTBEATS = false; - private static final boolean DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true; + private static final boolean DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false; private static final long DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = 10 * 60 * 1000L; // 10 minutes private static final long DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java index b3f101848692..0edd17b1f777 100644 --- a/services/core/java/com/android/server/location/AbstractLocationProvider.java +++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java @@ -16,6 +16,7 @@ package com.android.server.location; +import android.content.Context; import android.location.Location; import android.location.LocationProvider; import android.os.Bundle; @@ -26,6 +27,7 @@ import com.android.internal.location.ProviderRequest; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.Collections; import java.util.List; /** @@ -65,9 +67,12 @@ public abstract class AbstractLocationProvider { void onReportLocation(List<Location> locations); } + protected final Context mContext; private final LocationProviderManager mLocationProviderManager; - protected AbstractLocationProvider(LocationProviderManager locationProviderManager) { + protected AbstractLocationProvider( + Context context, LocationProviderManager locationProviderManager) { + mContext = context; mLocationProviderManager = locationProviderManager; } @@ -101,6 +106,11 @@ public abstract class AbstractLocationProvider { mLocationProviderManager.onSetProperties(properties); } + /** Returns list of packages currently associated with this provider. */ + public List<String> getProviderPackages() { + return Collections.singletonList(mContext.getPackageName()); + } + /** * Called when the location service delivers a new request for fulfillment to the provider. * Replaces any previous requests completely. diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index ab9f711b4238..b18110e80c2d 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -368,7 +368,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private int mC2KServerPort; private boolean mSuplEsEnabled = false; - private final Context mContext; private final Looper mLooper; private final LocationExtras mLocationExtras = new LocationExtras(); private final GnssStatusListenerHelper mGnssStatusListenerHelper; @@ -562,9 +561,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements public GnssLocationProvider(Context context, LocationProviderManager locationProviderManager, Looper looper) { - super(locationProviderManager); + super(context, locationProviderManager); - mContext = context; mLooper = looper; // Create a wake lock diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java index 776beb50be62..34c8786b00b3 100644 --- a/services/core/java/com/android/server/location/LocationProviderProxy.java +++ b/services/core/java/com/android/server/location/LocationProviderProxy.java @@ -16,8 +16,11 @@ package com.android.server.location; +import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; + import android.annotation.Nullable; import android.content.Context; +import android.content.pm.PackageManager; import android.location.Location; import android.location.LocationProvider; import android.os.Bundle; @@ -39,6 +42,10 @@ import com.android.server.ServiceWatcher; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; /** * Proxy for ILocationProvider implementations. @@ -48,14 +55,21 @@ public class LocationProviderProxy extends AbstractLocationProvider { private static final String TAG = "LocationProviderProxy"; private static final boolean D = LocationManagerService.D; + // used to ensure that updates to mProviderPackages are atomic + private final Object mProviderPackagesLock = new Object(); + // used to ensure that updates to mRequest and mWorkSource are atomic private final Object mRequestLock = new Object(); - private final ServiceWatcher mServiceWatcher; - private final ILocationProviderManager.Stub mManager = new ILocationProviderManager.Stub() { // executed on binder thread @Override + public void onSetAdditionalProviderPackages(List<String> packageNames) { + LocationProviderProxy.this.onSetAdditionalProviderPackages(packageNames); + } + + // executed on binder thread + @Override public void onSetEnabled(boolean enabled) { LocationProviderProxy.this.setEnabled(enabled); } @@ -73,6 +87,11 @@ public class LocationProviderProxy extends AbstractLocationProvider { } }; + private final ServiceWatcher mServiceWatcher; + + @GuardedBy("mProviderPackagesLock") + private final CopyOnWriteArrayList<String> mProviderPackages = new CopyOnWriteArrayList<>(); + @GuardedBy("mRequestLock") @Nullable private ProviderRequest mRequest; @@ -101,7 +120,7 @@ public class LocationProviderProxy extends AbstractLocationProvider { private LocationProviderProxy(Context context, LocationProviderManager locationProviderManager, String action, int overlaySwitchResId, int defaultServicePackageNameResId, int initialPackageNamesResId) { - super(locationProviderManager); + super(context, locationProviderManager); mServiceWatcher = new ServiceWatcher(context, TAG, action, overlaySwitchResId, defaultServicePackageNameResId, initialPackageNamesResId, @@ -114,6 +133,7 @@ public class LocationProviderProxy extends AbstractLocationProvider { @Override protected void onUnbind() { + resetProviderPackages(Collections.emptyList()); setEnabled(false); setProperties(null); } @@ -131,6 +151,8 @@ public class LocationProviderProxy extends AbstractLocationProvider { ILocationProvider service = ILocationProvider.Stub.asInterface(binder); if (D) Log.d(TAG, "applying state to connected service " + mServiceWatcher); + resetProviderPackages(Collections.emptyList()); + service.setLocationProviderManager(mManager); synchronized (mRequestLock) { @@ -140,9 +162,11 @@ public class LocationProviderProxy extends AbstractLocationProvider { } } - @Nullable - public String getConnectedPackageName() { - return mServiceWatcher.getCurrentPackageName(); + @Override + public List<String> getProviderPackages() { + synchronized (mProviderPackagesLock) { + return mProviderPackages; + } } @Override @@ -193,4 +217,30 @@ public class LocationProviderProxy extends AbstractLocationProvider { service.sendExtraCommand(command, extras); }); } + + private void onSetAdditionalProviderPackages(List<String> packageNames) { + resetProviderPackages(packageNames); + } + + private void resetProviderPackages(List<String> additionalPackageNames) { + ArrayList<String> permittedPackages = new ArrayList<>(additionalPackageNames.size()); + for (String packageName : additionalPackageNames) { + try { + mContext.getPackageManager().getPackageInfo(packageName, MATCH_SYSTEM_ONLY); + permittedPackages.add(packageName); + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, mServiceWatcher + " specified unknown additional provider package: " + + packageName); + } + } + + synchronized (mProviderPackagesLock) { + mProviderPackages.clear(); + String myPackage = mServiceWatcher.getCurrentPackageName(); + if (myPackage != null) { + mProviderPackages.add(myPackage); + mProviderPackages.addAll(permittedPackages); + } + } + } } diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java index fe91c63e712b..6accad8a93a0 100644 --- a/services/core/java/com/android/server/location/MockProvider.java +++ b/services/core/java/com/android/server/location/MockProvider.java @@ -17,6 +17,7 @@ package com.android.server.location; import android.annotation.Nullable; +import android.content.Context; import android.location.Location; import android.location.LocationProvider; import android.os.Bundle; @@ -41,9 +42,9 @@ public class MockProvider extends AbstractLocationProvider { private long mStatusUpdateTime; private Bundle mExtras; - public MockProvider( + public MockProvider(Context context, LocationProviderManager locationProviderManager, ProviderProperties properties) { - super(locationProviderManager); + super(context, locationProviderManager); mEnabled = true; mLocation = null; diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java index 30260b2fe14c..3a841c91399e 100644 --- a/services/core/java/com/android/server/location/PassiveProvider.java +++ b/services/core/java/com/android/server/location/PassiveProvider.java @@ -16,6 +16,7 @@ package com.android.server.location; +import android.content.Context; import android.location.Criteria; import android.location.Location; import android.os.Bundle; @@ -42,8 +43,8 @@ public class PassiveProvider extends AbstractLocationProvider { private boolean mReportLocation; - public PassiveProvider(LocationProviderManager locationProviderManager) { - super(locationProviderManager); + public PassiveProvider(Context context, LocationProviderManager locationProviderManager) { + super(context, locationProviderManager); mReportLocation = false; diff --git a/services/core/java/com/android/server/media/MediaUpdateService.java b/services/core/java/com/android/server/media/MediaUpdateService.java deleted file mode 100644 index 7304f0788a1d..000000000000 --- a/services/core/java/com/android/server/media/MediaUpdateService.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 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.media; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.media.IMediaUpdateService; -import android.os.Build; -import android.os.IBinder; -import android.os.Handler; -import android.os.PatternMatcher; -import android.os.ServiceManager; -import android.os.SystemProperties; -import android.os.UserHandle; -import android.text.TextUtils; -import android.util.Log; -import android.util.Slog; -import com.android.server.SystemService; -import java.util.HashMap; - -/** This class provides a system service that manages media framework updates. */ -public class MediaUpdateService extends SystemService { - private static final String TAG = "MediaUpdateService"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private static final String MEDIA_UPDATE_PACKAGE_NAME = - SystemProperties.get("ro.mediacomponents.package"); - private static final String EXTRACTOR_UPDATE_SERVICE_NAME = "media.extractor.update"; - private static final String CODEC_UPDATE_SERVICE_NAME = "media.codec.update"; - private static final String[] UPDATE_SERVICE_NAME_ARRAY = { - EXTRACTOR_UPDATE_SERVICE_NAME, CODEC_UPDATE_SERVICE_NAME, - }; - private final HashMap<String, IMediaUpdateService> mUpdateServiceMap = new HashMap<>(); - private final Handler mHandler = new Handler(); - - public MediaUpdateService(Context context) { - super(context); - } - - @Override - public void onStart() { - if (("userdebug".equals(android.os.Build.TYPE) || "eng".equals(android.os.Build.TYPE)) - && !TextUtils.isEmpty(MEDIA_UPDATE_PACKAGE_NAME)) { - for (String serviceName : UPDATE_SERVICE_NAME_ARRAY) { - connect(serviceName); - } - registerBroadcastReceiver(); - } - } - - private void connect(final String serviceName) { - IBinder binder = ServiceManager.getService(serviceName); - if (binder != null) { - try { - binder.linkToDeath(new IBinder.DeathRecipient() { - @Override - public void binderDied() { - Slog.w(TAG, "service " + serviceName + " died; reconnecting"); - synchronized (mUpdateServiceMap) { - mUpdateServiceMap.remove(serviceName); - } - connect(serviceName); - } - }, 0); - } catch (Exception e) { - binder = null; - } - } - if (binder != null) { - synchronized (mUpdateServiceMap) { - mUpdateServiceMap.put(serviceName, - IMediaUpdateService.Stub.asInterface(binder)); - } - mHandler.post(new Runnable() { - @Override - public void run() { - packageStateChanged(serviceName); - } - }); - } else { - Slog.w(TAG, serviceName + " not found."); - } - } - - private void registerBroadcastReceiver() { - BroadcastReceiver updateReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_SYSTEM) - != UserHandle.USER_SYSTEM) { - // Ignore broadcast for non system users. We don't want to update system - // service multiple times. - return; - } - switch (intent.getAction()) { - case Intent.ACTION_PACKAGE_REMOVED: - if (intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) { - // The existing package is updated. Will be handled with the - // following ACTION_PACKAGE_ADDED case. - return; - } - // fall-thru - case Intent.ACTION_PACKAGE_CHANGED: - case Intent.ACTION_PACKAGE_ADDED: - for (String serviceName : UPDATE_SERVICE_NAME_ARRAY) { - packageStateChanged(serviceName); - } - break; - } - } - }; - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_PACKAGE_ADDED); - filter.addAction(Intent.ACTION_PACKAGE_REMOVED); - filter.addAction(Intent.ACTION_PACKAGE_CHANGED); - filter.addDataScheme("package"); - filter.addDataSchemeSpecificPart(MEDIA_UPDATE_PACKAGE_NAME, PatternMatcher.PATTERN_LITERAL); - - getContext().registerReceiverAsUser(updateReceiver, UserHandle.ALL, filter, - null /* broadcast permission */, null /* handler */); - } - - private void packageStateChanged(String serviceName) { - ApplicationInfo packageInfo = null; - boolean pluginsAvailable = false; - try { - packageInfo = getContext().getPackageManager().getApplicationInfo( - MEDIA_UPDATE_PACKAGE_NAME, PackageManager.MATCH_SYSTEM_ONLY); - pluginsAvailable = packageInfo.enabled; - } catch (Exception e) { - Slog.v(TAG, "package '" + MEDIA_UPDATE_PACKAGE_NAME + "' not installed"); - } - if (packageInfo != null && Build.VERSION.SDK_INT != packageInfo.targetSdkVersion) { - Slog.w(TAG, "This update package is not for this platform version. Ignoring. " - + "platform:" + Build.VERSION.SDK_INT - + " targetSdk:" + packageInfo.targetSdkVersion); - pluginsAvailable = false; - } - loadPlugins(serviceName, - (packageInfo != null && pluginsAvailable) ? packageInfo.sourceDir : ""); - } - - private void loadPlugins(String serviceName, String apkPath) { - try { - IMediaUpdateService service = null; - synchronized (serviceName) { - service = mUpdateServiceMap.get(serviceName); - } - if (service != null) { - service.loadPlugins(apkPath); - } else { - Slog.w(TAG, "service " + serviceName + " passed away"); - } - } catch (Exception e) { - Slog.w(TAG, "Error in loadPlugins for " + serviceName, e); - } - } -} diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java index be15fdaf5abe..b85abd98b00f 100644 --- a/services/core/java/com/android/server/notification/NotificationDelegate.java +++ b/services/core/java/com/android/server/notification/NotificationDelegate.java @@ -51,7 +51,7 @@ public interface NotificationDelegate { * Notifies that smart replies and actions have been added to the UI. */ void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount, int smartActionCount, - boolean generatedByAssistant); + boolean generatedByAssistant, boolean editBeforeSending); /** * Notifies a smart reply is sent. @@ -59,9 +59,9 @@ public interface NotificationDelegate { * @param key the notification key * @param clickedIndex the index of clicked reply * @param reply the reply that is sent - * @param generatedByAssistant specifies is the reply generated by NAS * @param notificationLocation the location of the notification containing the smart reply + * @param modifiedBeforeSending whether the user changed the smart reply before sending */ void onNotificationSmartReplySent(String key, int clickedIndex, CharSequence reply, - boolean generatedByAssistant, int notificationLocation); + int notificationLocation, boolean modifiedBeforeSending); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index ba187c0884b9..34a6663c4352 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -917,20 +917,21 @@ public class NotificationManagerService extends SystemService { @Override public void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount, - int smartActionCount, boolean generatedByAssistant) { + int smartActionCount, boolean generatedByAssistant, boolean editBeforeSending) { synchronized (mNotificationLock) { NotificationRecord r = mNotificationsByKey.get(key); if (r != null) { r.setNumSmartRepliesAdded(smartReplyCount); r.setNumSmartActionsAdded(smartActionCount); r.setSuggestionsGeneratedByAssistant(generatedByAssistant); + r.setEditChoicesBeforeSending(editBeforeSending); } } } @Override public void onNotificationSmartReplySent(String key, int replyIndex, CharSequence reply, - boolean generatedByAssistant, int notificationLocation) { + int notificationLocation, boolean modifiedBeforeSending) { synchronized (mNotificationLock) { NotificationRecord r = mNotificationsByKey.get(key); @@ -940,14 +941,20 @@ public class NotificationManagerService extends SystemService { .setSubtype(replyIndex) .addTaggedData( MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED, - generatedByAssistant ? 1 : 0) + r.getSuggestionsGeneratedByAssistant() ? 1 : 0) .addTaggedData(MetricsEvent.NOTIFICATION_LOCATION, - notificationLocation); + notificationLocation) + .addTaggedData( + MetricsEvent.NOTIFICATION_SMART_REPLY_EDIT_BEFORE_SENDING, + r.getEditChoicesBeforeSending() ? 1 : 0) + .addTaggedData( + MetricsEvent.NOTIFICATION_SMART_REPLY_MODIFIED_BEFORE_SENDING, + modifiedBeforeSending ? 1 : 0); mMetricsLogger.write(logMaker); // Treat clicking on a smart reply as a user interaction. reportUserInteraction(r); mAssistants.notifyAssistantSuggestedReplySent( - r.sbn, reply, generatedByAssistant); + r.sbn, reply, r.getSuggestionsGeneratedByAssistant()); } } } @@ -981,7 +988,10 @@ public class NotificationManagerService extends SystemService { r.getSuggestionsGeneratedByAssistant() ? 1 : 0) // The fields in the NotificationVisibility.NotificationLocation enum map // directly to the fields in the MetricsEvent.NotificationLocation enum. - .addTaggedData(MetricsEvent.NOTIFICATION_LOCATION, notificationLocation); + .addTaggedData(MetricsEvent.NOTIFICATION_LOCATION, notificationLocation) + .addTaggedData( + MetricsEvent.NOTIFICATION_SMART_REPLY_EDIT_BEFORE_SENDING, + r.getEditChoicesBeforeSending() ? 1 : 0); mMetricsLogger.write(logMaker); } } diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index ab49ebb66fbf..b3394b4c599f 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -178,6 +178,7 @@ public final class NotificationRecord { private int mNumberOfSmartRepliesAdded; private int mNumberOfSmartActionsAdded; private boolean mSuggestionsGeneratedByAssistant; + private boolean mEditChoicesBeforeSending; private boolean mHasSeenSmartReplies; /** * Whether this notification (and its channels) should be considered user locked. Used in @@ -1136,6 +1137,14 @@ public final class NotificationRecord { return mSuggestionsGeneratedByAssistant; } + public boolean getEditChoicesBeforeSending() { + return mEditChoicesBeforeSending; + } + + public void setEditChoicesBeforeSending(boolean editChoicesBeforeSending) { + mEditChoicesBeforeSending = editChoicesBeforeSending; + } + public boolean hasSeenSmartReplies() { return mHasSeenSmartReplies; } diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java index f736056c5c7f..1dada92ab118 100644 --- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java +++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java @@ -17,8 +17,10 @@ package com.android.server.os; import android.annotation.RequiresPermission; +import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.Context; +import android.content.pm.UserInfo; import android.os.Binder; import android.os.BugreportParams; import android.os.IDumpstate; @@ -28,26 +30,29 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; +import android.os.UserManager; import android.util.Slog; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; + import java.io.FileDescriptor; // TODO(b/111441001): -// 1. Handle the case where another bugreport is in progress -// 2. Make everything threadsafe -// 3. Pass validation & other errors on listener +// Intercept onFinished() & implement death recipient here and shutdown +// bugreportd service. /** * Implementation of the service that provides a privileged API to capture and consume bugreports. * - * <p>Delegates the actualy generation to a native implementation of {@code Dumpstate}. + * <p>Delegates the actualy generation to a native implementation of {@code IDumpstate}. */ class BugreportManagerServiceImpl extends IDumpstate.Stub { private static final String TAG = "BugreportManagerService"; private static final String BUGREPORT_SERVICE = "bugreportd"; private static final long DEFAULT_BUGREPORT_SERVICE_TIMEOUT_MILLIS = 30 * 1000; - private IDumpstate mDs = null; + private final Object mLock = new Object(); private final Context mContext; private final AppOpsManager mAppOps; @@ -59,43 +64,44 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { @Override @RequiresPermission(android.Manifest.permission.DUMP) public IDumpstateToken setListener(String name, IDumpstateListener listener, - boolean getSectionDetails) throws RemoteException { - // TODO(b/111441001): Figure out if lazy setting of listener should be allowed - // and if so how to handle it. + boolean getSectionDetails) { throw new UnsupportedOperationException("setListener is not allowed on this service"); } - // TODO(b/111441001): Intercept onFinished here in system server and shutdown - // the bugreportd service. @Override @RequiresPermission(android.Manifest.permission.DUMP) public void startBugreport(int callingUidUnused, String callingPackage, FileDescriptor bugreportFd, FileDescriptor screenshotFd, - int bugreportMode, IDumpstateListener listener) throws RemoteException { - int callingUid = Binder.getCallingUid(); - // TODO(b/111441001): validate all arguments & ensure primary user - validate(bugreportMode); + int bugreportMode, IDumpstateListener listener) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "startBugreport"); + Preconditions.checkNotNull(callingPackage); + Preconditions.checkNotNull(bugreportFd); + Preconditions.checkNotNull(listener); + validateBugreportMode(bugreportMode); + ensureIsPrimaryUser(); + int callingUid = Binder.getCallingUid(); mAppOps.checkPackage(callingUid, callingPackage); - mDs = getDumpstateService(); - if (mDs == null) { - Slog.w(TAG, "Unable to get bugreport service"); - // TODO(b/111441001): pass error on listener - return; + + synchronized (mLock) { + startBugreportLocked(callingUid, callingPackage, bugreportFd, screenshotFd, + bugreportMode, listener); } - mDs.startBugreport(callingUid, callingPackage, - bugreportFd, screenshotFd, bugreportMode, listener); } @Override @RequiresPermission(android.Manifest.permission.DUMP) - public void cancelBugreport() throws RemoteException { - // This tells init to cancel bugreportd service. - SystemProperties.set("ctl.stop", BUGREPORT_SERVICE); - mDs = null; + public void cancelBugreport() { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "startBugreport"); + // This tells init to cancel bugreportd service. Note that this is achieved through setting + // a system property which is not thread-safe. So the lock here offers thread-safety only + // among callers of the API. + synchronized (mLock) { + SystemProperties.set("ctl.stop", BUGREPORT_SERVICE); + } } - private boolean validate(@BugreportParams.BugreportMode int mode) { + private void validateBugreportMode(@BugreportParams.BugreportMode int mode) { if (mode != BugreportParams.BUGREPORT_MODE_FULL && mode != BugreportParams.BUGREPORT_MODE_INTERACTIVE && mode != BugreportParams.BUGREPORT_MODE_REMOTE @@ -103,9 +109,66 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { && mode != BugreportParams.BUGREPORT_MODE_TELEPHONY && mode != BugreportParams.BUGREPORT_MODE_WIFI) { Slog.w(TAG, "Unknown bugreport mode: " + mode); - return false; + throw new IllegalArgumentException("Unknown bugreport mode: " + mode); + } + } + + /** + * Validates that the current user is the primary user. + * + * @throws IllegalArgumentException if the current user is not the primary user + */ + private void ensureIsPrimaryUser() { + UserInfo currentUser = null; + try { + currentUser = ActivityManager.getService().getCurrentUser(); + } catch (RemoteException e) { + // Impossible to get RemoteException for an in-process call. + } + + UserInfo primaryUser = UserManager.get(mContext).getPrimaryUser(); + if (currentUser == null) { + logAndThrow("No current user. Only primary user is allowed to take bugreports."); + } + if (primaryUser == null) { + logAndThrow("No primary user. Only primary user is allowed to take bugreports."); + } + if (primaryUser.id != currentUser.id) { + logAndThrow("Current user not primary user. Only primary user" + + " is allowed to take bugreports."); + } + } + + @GuardedBy("mLock") + private void startBugreportLocked(int callingUid, String callingPackage, + FileDescriptor bugreportFd, FileDescriptor screenshotFd, + int bugreportMode, IDumpstateListener listener) { + if (isDumpstateBinderServiceRunningLocked()) { + Slog.w(TAG, "'dumpstate' is already running. Cannot start a new bugreport" + + " while another one is currently in progress."); + // TODO(b/111441001): Use a new error code; add this to the documentation of the API. + reportError(listener, IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR); + return; + } + + IDumpstate ds = startAndGetDumpstateBinderServiceLocked(); + if (ds == null) { + Slog.w(TAG, "Unable to get bugreport service"); + reportError(listener, IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR); + return; + } + try { + ds.startBugreport(callingUid, callingPackage, + bugreportFd, screenshotFd, bugreportMode, listener); + } catch (RemoteException e) { + reportError(listener, IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR); } - return true; + } + + @GuardedBy("mLock") + private boolean isDumpstateBinderServiceRunningLocked() { + IDumpstate ds = IDumpstate.Stub.asInterface(ServiceManager.getService("dumpstate")); + return ds != null; } /* @@ -115,8 +178,12 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { * <p>Generating bugreports requires root privileges. To limit the footprint * of the root access, the actual generation in Dumpstate binary is accessed as a * oneshot service 'bugreport'. + * + * <p>Note that starting the service is achieved through setting a system property, which is + * not thread-safe. So the lock here offers thread-safety only among callers of the API. */ - private IDumpstate getDumpstateService() { + @GuardedBy("mLock") + private IDumpstate startAndGetDumpstateBinderServiceLocked() { // Start bugreport service. SystemProperties.set("ctl.start", BUGREPORT_SERVICE); @@ -145,4 +212,18 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { } return ds; } + + private void reportError(IDumpstateListener listener, int errorCode) { + try { + listener.onError(errorCode); + } catch (RemoteException e) { + // Something went wrong in binder or app process. There's nothing to do here. + Slog.w(TAG, "onError() transaction threw RemoteException: " + e.getMessage()); + } + } + + private void logAndThrow(String message) { + Slog.w(TAG, message); + throw new IllegalArgumentException(message); + } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 65008057f784..e18da7f7b319 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -131,8 +131,6 @@ import android.app.ResourcesManager; import android.app.admin.IDevicePolicyManager; import android.app.admin.SecurityLog; import android.app.backup.IBackupManager; -import android.app.role.RoleManager; -import android.app.role.RoleManagerCallback; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -290,7 +288,6 @@ import com.android.internal.os.SomeArgs; import com.android.internal.os.Zygote; import com.android.internal.telephony.CarrierAppUtils; import com.android.internal.util.ArrayUtils; -import com.android.internal.util.CollectionUtils; import com.android.internal.util.ConcurrentUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; @@ -372,10 +369,8 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @@ -989,6 +984,9 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mPackages") private CheckPermissionDelegate mCheckPermissionDelegate; + @GuardedBy("mPackages") + private PackageManagerInternal.DefaultBrowserProvider mDefaultBrowserProvider; + private class IntentVerifierProxy implements IntentFilterVerifier<ActivityIntentInfo> { private Context mContext; private ComponentName mIntentFilterVerifierComponent; @@ -3943,13 +3941,13 @@ public class PackageManagerService extends IPackageManager.Stub if (apex != null) { try { final ApexInfo activePkg = apex.getActivePackage(packageName); - if (activePkg != null) { + if (activePkg != null && !TextUtils.isEmpty(activePkg.packagePath)) { try { return PackageParser.generatePackageInfoFromApex( new File(activePkg.packagePath), true /* collect certs */); } catch (PackageParserException pe) { - throw new IllegalStateException("Unable to parse: " + activePkg, - pe); + Log.e(TAG, "Unable to parse package at " + + activePkg.packagePath, pe); } } } catch (RemoteException e) { @@ -13428,6 +13426,10 @@ public class PackageManagerService extends IPackageManager.Stub return false; } + if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) { + return false; + } + boolean ensureVerifyAppsEnabled = isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS); // Check if installing from ADB @@ -13581,31 +13583,25 @@ public class PackageManagerService extends IPackageManager.Stub mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); } - if (userId == UserHandle.USER_ALL) { return false; } - RoleManager roleManager = mContext.getSystemService(RoleManager.class); - UserHandle user = UserHandle.of(userId); - RoleManagerCallback.Future future = new RoleManagerCallback.Future(); - if (packageName != null) { - Binder.withCleanCallingIdentity(() -> roleManager.addRoleHolderAsUser( - RoleManager.ROLE_BROWSER, packageName, user, AsyncTask.THREAD_POOL_EXECUTOR, - future)); - } else { - Binder.withCleanCallingIdentity(() -> roleManager.clearRoleHoldersAsUser( - RoleManager.ROLE_BROWSER, user, AsyncTask.THREAD_POOL_EXECUTOR, future)); + PackageManagerInternal.DefaultBrowserProvider provider; + synchronized (mPackages) { + provider = mDefaultBrowserProvider; } - try { - future.get(5, TimeUnit.SECONDS); - } catch (InterruptedException | ExecutionException | TimeoutException e) { - Slog.e(TAG, "Exception while setting default browser package name: " + packageName, e); + if (provider == null) { + Slog.e(TAG, "mDefaultBrowserProvider is null"); return false; } - synchronized (mPackages) { - if (packageName != null) { - mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultBrowser( - packageName, userId); + boolean successful = provider.setDefaultBrowser(packageName, userId); + if (!successful) { + return false; + } + if (packageName != null) { + synchronized (mPackages) { + mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultBrowser(packageName, + userId); } } return true; @@ -13620,10 +13616,15 @@ public class PackageManagerService extends IPackageManager.Stub if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return null; } - RoleManager roleManager = mContext.getSystemService(RoleManager.class); - List<String> packageNames = Binder.withCleanCallingIdentity(() -> - roleManager.getRoleHoldersAsUser(RoleManager.ROLE_BROWSER, UserHandle.of(userId))); - return CollectionUtils.firstOrNull(packageNames); + PackageManagerInternal.DefaultBrowserProvider provider; + synchronized (mPackages) { + provider = mDefaultBrowserProvider; + } + if (provider == null) { + Slog.e(TAG, "mDefaultBrowserProvider is null"); + return null; + } + return provider.getDefaultBrowser(userId); } /** @@ -23922,6 +23923,13 @@ public class PackageManagerService extends IPackageManager.Stub return mSettings.removeDefaultBrowserPackageNameLPw(userId); } } + + @Override + public void setDefaultBrowserProvider(@NonNull DefaultBrowserProvider provider) { + synchronized (mPackages) { + mDefaultBrowserProvider = provider; + } + } } @GuardedBy("mPackages") diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 03716638bbeb..55eb7ea7da4d 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -21,6 +21,7 @@ import android.apex.ApexInfo; import android.apex.ApexInfoList; import android.apex.ApexSessionInfo; import android.apex.IApexService; +import android.content.Context; import android.content.IIntentReceiver; import android.content.IIntentSender; import android.content.Intent; @@ -33,6 +34,7 @@ import android.content.pm.PackageParser.SigningDetails; import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion; import android.content.pm.ParceledListSlice; import android.content.pm.Signature; +import android.content.rollback.IRollbackManager; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -186,6 +188,7 @@ public class StagingManager { private void preRebootVerification(@NonNull PackageInstallerSession session) { boolean success = true; + // STOPSHIP: TODO(b/123753157): Verify APKs through Package Verifier. if (!sessionContainsApex(session)) { // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs, // right away. @@ -238,6 +241,23 @@ public class StagingManager { } } + if ((session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) { + // If rollback is enabled for this session, we call through to the RollbackManager + // with the list of sessions it must enable rollback for. Note that notifyStagedSession + // is a synchronous operation. + final IRollbackManager rm = IRollbackManager.Stub.asInterface( + ServiceManager.getService(Context.ROLLBACK_SERVICE)); + try { + // NOTE: To stay consistent with the non-staged install flow, we don't fail the + // entire install if rollbacks can't be enabled. + if (!rm.notifyStagedSession(session.sessionId)) { + Slog.e(TAG, "Unable to enable rollback for session: " + session.sessionId); + } + } catch (RemoteException re) { + // Cannot happen, the rollback manager is in the same process. + } + } + session.setStagedSessionReady(); if (!sendMarkStagedSessionReadyRequest(session.sessionId)) { session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, @@ -336,6 +356,7 @@ public class StagingManager { PackageInstaller.SessionParams params = originalSession.params.copy(); params.isStaged = false; + params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION; int apkSessionId = mPi.createSession( params, originalSession.getInstallerPackageName(), originalSession.userId); PackageInstallerSession apkSession = mPi.getSession(apkSessionId); diff --git a/services/core/java/com/android/server/pm/permission/TEST_MAPPING b/services/core/java/com/android/server/pm/permission/TEST_MAPPING index 09bacd6ed332..0892b32a6b91 100644 --- a/services/core/java/com/android/server/pm/permission/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/permission/TEST_MAPPING @@ -18,6 +18,14 @@ "include-filter": "android.permission.cts.SplitPermissionTest" } ] + }, + { + "name": "CtsStatsdHostTestCases", + "options": [ + { + "include-filter": "android.cts.statsd.atom.UidAtomTests#testDangerousPermissionState" + } + ] } ] }
\ No newline at end of file diff --git a/services/core/java/com/android/server/role/RoleManagerServiceInternal.java b/services/core/java/com/android/server/role/RoleManagerInternal.java index 3afc3f731eb9..7598bf70d77a 100644 --- a/services/core/java/com/android/server/role/RoleManagerServiceInternal.java +++ b/services/core/java/com/android/server/role/RoleManagerInternal.java @@ -17,22 +17,23 @@ package com.android.server.role; import android.annotation.NonNull; -import android.os.UserHandle; +import android.annotation.UserIdInt; import android.util.ArrayMap; import android.util.ArraySet; /** - * Internal calls into {@link RoleManagerService} + * Internal calls into {@link RoleManagerService}. */ -public abstract class RoleManagerServiceInternal { +public abstract class RoleManagerInternal { + /** - * Get all roles and packages hold them. + * Get all roles and their holders. * - * @param user The user to query to roles for + * @param userId The user to query to roles for * * @return The roles and their holders */ @NonNull - public abstract ArrayMap<String, ArraySet<String>> getRoleHoldersAsUser( - @NonNull UserHandle user); + public abstract ArrayMap<String, ArraySet<String>> getRolesAndHolders( + @UserIdInt int userId); } diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index d72270e3f36d..c464dad93822 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -146,8 +146,11 @@ public class RoleManagerService extends SystemService implements RoleUserState.C mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); mAppOpsManager = context.getSystemService(AppOpsManager.class); - LocalServices.addService(RoleManagerServiceInternal.class, - new RoleManagerServiceInternalImpl()); + LocalServices.addService(RoleManagerInternal.class, new Internal()); + + PackageManagerInternal packageManagerInternal = LocalServices.getService( + PackageManagerInternal.class); + packageManagerInternal.setDefaultBrowserProvider(new DefaultBrowserProvider()); registerUserRemovedReceiver(); } @@ -360,10 +363,10 @@ public class RoleManagerService extends SystemService implements RoleUserState.C notifyRoleHoldersChangedForListeners(listeners, roleName, userId); } - RemoteCallbackList<IOnRoleHoldersChangedListener> allUserListeners = getListeners( + RemoteCallbackList<IOnRoleHoldersChangedListener> allUsersListeners = getListeners( UserHandle.USER_ALL); - if (allUserListeners != null) { - notifyRoleHoldersChangedForListeners(allUserListeners, roleName, userId); + if (allUsersListeners != null) { + notifyRoleHoldersChangedForListeners(allUsersListeners, roleName, userId); } } @@ -386,19 +389,6 @@ public class RoleManagerService extends SystemService implements RoleUserState.C } } - /** - * Get all roles and packages hold them. - * - * @param user The user to query to roles for - * - * @return The roles and their holders - */ - @NonNull - private ArrayMap<String, ArraySet<String>> getRoleHoldersAsUser(@NonNull UserHandle user) { - RoleUserState userState = getOrCreateUserState(user.getIdentifier()); - return userState.getRoleHolders(); - } - private class Stub extends IRoleManager.Stub { @Override @@ -406,8 +396,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); int userId = UserHandle.getUserId(getCallingUid()); - RoleUserState userState = getOrCreateUserState(userId); - return userState.isRoleAvailable(roleName); + return getOrCreateUserState(userId).isRoleAvailable(roleName); } @Override @@ -418,7 +407,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C mAppOpsManager.checkPackage(callingUid, packageName); int userId = UserHandle.getUserId(callingUid); - ArraySet<String> roleHolders = getRoleHoldersInternal(roleName, userId); + ArraySet<String> roleHolders = getOrCreateUserState(userId).getRoleHolders(roleName); if (roleHolders == null) { return false; } @@ -433,24 +422,17 @@ public class RoleManagerService extends SystemService implements RoleUserState.C Slog.e(LOG_TAG, "user " + userId + " does not exist"); return Collections.emptyList(); } - userId = handleIncomingUser(userId, "getRoleHoldersAsUser", false); + userId = handleIncomingUser(userId, false, "getRoleHoldersAsUser"); getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, "getRoleHoldersAsUser"); - ArraySet<String> roleHolders = getRoleHoldersInternal(roleName, userId); + ArraySet<String> roleHolders = getOrCreateUserState(userId).getRoleHolders(roleName); if (roleHolders == null) { return Collections.emptyList(); } return new ArrayList<>(roleHolders); } - @Nullable - private ArraySet<String> getRoleHoldersInternal(@NonNull String roleName, - @UserIdInt int userId) { - RoleUserState userState = getOrCreateUserState(userId); - return userState.getRoleHolders(roleName); - } - @Override public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName, @UserIdInt int userId, @NonNull IRoleManagerCallback callback) { @@ -461,7 +443,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C Slog.e(LOG_TAG, "user " + userId + " does not exist"); return; } - userId = handleIncomingUser(userId, "addRoleHolderAsUser", false); + userId = handleIncomingUser(userId, false, "addRoleHolderAsUser"); getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, "addRoleHolderAsUser"); @@ -478,7 +460,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C Slog.e(LOG_TAG, "user " + userId + " does not exist"); return; } - userId = handleIncomingUser(userId, "removeRoleHolderAsUser", false); + userId = handleIncomingUser(userId, false, "removeRoleHolderAsUser"); getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, "removeRoleHolderAsUser"); @@ -495,7 +477,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C Slog.e(LOG_TAG, "user " + userId + " does not exist"); return; } - userId = handleIncomingUser(userId, "clearRoleHoldersAsUser", false); + userId = handleIncomingUser(userId, false, "clearRoleHoldersAsUser"); getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, "clearRoleHoldersAsUser"); @@ -510,8 +492,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C Slog.e(LOG_TAG, "user " + userId + " does not exist"); return; } - userId = handleIncomingUser(userId, "addOnRoleHoldersChangedListenerAsUser", - true); + userId = handleIncomingUser(userId, true, "addOnRoleHoldersChangedListenerAsUser"); getContext().enforceCallingOrSelfPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS, "addOnRoleHoldersChangedListenerAsUser"); @@ -528,8 +509,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C Slog.e(LOG_TAG, "user " + userId + " does not exist"); return; } - userId = handleIncomingUser(userId, "removeOnRoleHoldersChangedListenerAsUser", - true); + userId = handleIncomingUser(userId, true, "removeOnRoleHoldersChangedListenerAsUser"); getContext().enforceCallingOrSelfPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS, "removeOnRoleHoldersChangedListenerAsUser"); @@ -548,8 +528,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C "setRoleNamesFromController"); int userId = UserHandle.getCallingUserId(); - RoleUserState userState = getOrCreateUserState(userId); - userState.setRoleNames(roleNames); + getOrCreateUserState(userId).setRoleNames(roleNames); } @Override @@ -562,8 +541,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C "addRoleHolderFromController"); int userId = UserHandle.getCallingUserId(); - RoleUserState userState = getOrCreateUserState(userId); - return userState.addRoleHolder(roleName, packageName); + return getOrCreateUserState(userId).addRoleHolder(roleName, packageName); } @Override @@ -576,8 +554,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C "removeRoleHolderFromController"); int userId = UserHandle.getCallingUserId(); - RoleUserState userState = getOrCreateUserState(userId); - return userState.removeRoleHolder(roleName, packageName); + return getOrCreateUserState(userId).removeRoleHolder(roleName, packageName); } @Override @@ -588,13 +565,12 @@ public class RoleManagerService extends SystemService implements RoleUserState.C "getRolesHeldFromController"); int userId = UserHandle.getCallingUserId(); - RoleUserState userState = getOrCreateUserState(userId); - return userState.getHeldRoles(packageName); + return getOrCreateUserState(userId).getHeldRoles(packageName); } @CheckResult - private int handleIncomingUser(@UserIdInt int userId, @NonNull String name, - boolean allowAll) { + private int handleIncomingUser(@UserIdInt int userId, boolean allowAll, + @NonNull String name) { return ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId, allowAll, true, name, null); } @@ -694,15 +670,51 @@ public class RoleManagerService extends SystemService implements RoleUserState.C } } - /** - * Entry point for internal calls into role manager - */ - private final class RoleManagerServiceInternalImpl extends RoleManagerServiceInternal { + private class Internal extends RoleManagerInternal { @NonNull @Override - public ArrayMap<String, ArraySet<String>> getRoleHoldersAsUser(@NonNull UserHandle user) { - return RoleManagerService.this.getRoleHoldersAsUser(user); + public ArrayMap<String, ArraySet<String>> getRolesAndHolders(@UserIdInt int userId) { + return getOrCreateUserState(userId).getRolesAndHolders(); + } + } + + private class DefaultBrowserProvider implements PackageManagerInternal.DefaultBrowserProvider { + + @Nullable + @Override + public String getDefaultBrowser(@UserIdInt int userId) { + return CollectionUtils.firstOrNull(getOrCreateUserState(userId).getRoleHolders( + RoleManager.ROLE_BROWSER)); + } + + @Override + public boolean setDefaultBrowser(@Nullable String packageName, @UserIdInt int userId) { + CompletableFuture<Void> future = new CompletableFuture<>(); + IRoleManagerCallback callback = new IRoleManagerCallback.Stub() { + @Override + public void onSuccess() { + future.complete(null); + } + @Override + public void onFailure() { + future.completeExceptionally(new RuntimeException()); + } + }; + if (packageName != null) { + getOrCreateControllerService(userId).onAddRoleHolder(RoleManager.ROLE_BROWSER, + packageName, callback); + } else { + getOrCreateControllerService(userId).onClearRoleHolders(RoleManager.ROLE_BROWSER, + callback); + } + try { + future.get(5, TimeUnit.SECONDS); + return true; + } catch (InterruptedException | ExecutionException | TimeoutException e) { + Slog.e(LOG_TAG, "Exception while setting default browser: " + packageName, e); + return false; + } } } } diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java index 5030e349a485..c7e3fa4f2074 100644 --- a/services/core/java/com/android/server/role/RoleUserState.java +++ b/services/core/java/com/android/server/role/RoleUserState.java @@ -576,13 +576,14 @@ public class RoleUserState { * @return A copy of the roles and their holders */ @NonNull - public ArrayMap<String, ArraySet<String>> getRoleHolders() { + public ArrayMap<String, ArraySet<String>> getRolesAndHolders() { synchronized (mLock) { return snapshotRolesLocked(); } } @GuardedBy("mLock") + @NonNull private ArrayMap<String, ArraySet<String>> snapshotRolesLocked() { ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>(); for (int i = 0, size = CollectionUtils.size(mRoles); i < size; ++i) { @@ -615,7 +616,8 @@ public class RoleUserState { } } - private static @NonNull File getFile(@UserIdInt int userId) { + @NonNull + private static File getFile(@UserIdInt int userId) { return new File(Environment.getUserSystemDirectory(userId), ROLES_FILE_NAME); } diff --git a/services/core/java/com/android/server/role/TEST_MAPPING b/services/core/java/com/android/server/role/TEST_MAPPING new file mode 100644 index 000000000000..9efd292738be --- /dev/null +++ b/services/core/java/com/android/server/role/TEST_MAPPING @@ -0,0 +1,15 @@ +{ + "presubmit": [ + { + "name": "CtsStatsdHostTestCases", + "options": [ + { + "include-filter": "android.cts.statsd.atom.UidAtomTests#testRoleHolder" + } + ] + }, + { + "name": "CtsRoleTestCases" + } + ] +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/rollback/RollbackData.java b/services/core/java/com/android/server/rollback/RollbackData.java index a4f306489c60..467bb28f5744 100644 --- a/services/core/java/com/android/server/rollback/RollbackData.java +++ b/services/core/java/com/android/server/rollback/RollbackData.java @@ -50,6 +50,12 @@ class RollbackData { public Instant timestamp; /** + * The session ID for the staged session if this rollback data represents a staged session, + * {@code -1} otherwise. + */ + public int stagedSessionId; + + /** * Whether this Rollback is currently in progress. This field is true from the point * we commit a {@code PackageInstaller} session containing these packages to the point the * {@code PackageInstaller} calls into the {@code onFinished} callback. @@ -57,8 +63,9 @@ class RollbackData { // NOTE: All accesses to this field are from the RollbackManager handler thread. public boolean inProgress = false; - RollbackData(int rollbackId, File backupDir) { + RollbackData(int rollbackId, File backupDir, int stagedSessionId) { this.rollbackId = rollbackId; this.backupDir = backupDir; + this.stagedSessionId = stagedSessionId; } } diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index f5b37b4bc4b5..7885abe17c5b 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -59,6 +59,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.concurrent.LinkedBlockingQueue; /** * Implementation of service that manages APK level rollbacks. @@ -156,6 +157,17 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } }, filter, null, getHandler()); + // NOTE: A new intent filter is being created here because this broadcast + // doesn't use a data scheme ("package") like above. + IntentFilter sessionUpdatedFilter = new IntentFilter(); + sessionUpdatedFilter.addAction(PackageInstaller.ACTION_SESSION_UPDATED); + mContext.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + onStagedSessionUpdated(intent); + } + }, sessionUpdatedFilter, null, getHandler()); + IntentFilter enableRollbackFilter = new IntentFilter(); enableRollbackFilter.addAction(Intent.ACTION_PACKAGE_ENABLE_ROLLBACK); try { @@ -718,7 +730,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { return false; } - return enableRollbackForSession(session, installedUsers); + return enableRollbackForSession(session, installedUsers, true); } /** @@ -727,7 +739,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { * the child sessions, not the parent session. */ private boolean enableRollbackForSession(PackageInstaller.SessionInfo session, - int[] installedUsers) { + int[] installedUsers, boolean snapshotUserData) { // TODO: Don't attempt to enable rollback for split installs. final int installFlags = session.installFlags; if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) == 0) { @@ -749,15 +761,17 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } String packageName = newPackage.packageName; - Log.i(TAG, "Enabling rollback for install of " + packageName); + Log.i(TAG, "Enabling rollback for install of " + packageName + + ", session:" + session.sessionId); VersionedPackage newVersion = new VersionedPackage(packageName, newPackage.versionCode); + final boolean isApex = ((installFlags & PackageManager.INSTALL_APEX) != 0); // Get information about the currently installed package. PackageManager pm = mContext.getPackageManager(); PackageInfo pkgInfo = null; try { - pkgInfo = pm.getPackageInfo(packageName, 0); + pkgInfo = pm.getPackageInfo(packageName, isApex ? PackageManager.MATCH_APEX : 0); } catch (PackageManager.NameNotFoundException e) { // TODO: Support rolling back fresh package installs rather than // fail here. Test this case. @@ -768,17 +782,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { VersionedPackage installedVersion = new VersionedPackage(packageName, pkgInfo.getLongVersionCode()); - final boolean isApex = ((installFlags & PackageManager.INSTALL_APEX) != 0); - final IntArray pendingBackups; - if (isApex) { - pendingBackups = IntArray.wrap(new int[0]); - } else { + IntArray pendingBackups = IntArray.wrap(new int[0]); + if (snapshotUserData && !isApex) { pendingBackups = mUserdataHelper.snapshotAppData(packageName, installedUsers); } - // TODO: Record if this is an apex or not. PackageRollbackInfo info = new PackageRollbackInfo(newVersion, installedVersion, - pendingBackups, new ArrayList<>()); + pendingBackups, new ArrayList<>(), isApex); RollbackData data; try { int childSessionId = session.getSessionId(); @@ -786,6 +796,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { if (parentSessionId == PackageInstaller.SessionInfo.INVALID_ID) { parentSessionId = childSessionId; } + synchronized (mLock) { // TODO: no need to add to mChildSessions if childSessionId is // the same as parentSessionId. @@ -793,7 +804,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { data = mPendingRollbacks.get(parentSessionId); if (data == null) { int rollbackId = allocateRollbackIdLocked(); - data = mRollbackStore.createAvailableRollback(rollbackId); + if (session.isStaged()) { + data = mRollbackStore.createPendingStagedRollback(rollbackId, + parentSessionId); + } else { + data = mRollbackStore.createAvailableRollback(rollbackId); + } mPendingRollbacks.put(parentSessionId, data); } data.packages.add(info); @@ -844,6 +860,56 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { }); } + @Override + public boolean notifyStagedSession(int sessionId) { + final LinkedBlockingQueue<Boolean> result = new LinkedBlockingQueue<>(); + + // NOTE: We post this runnable on the RollbackManager's binder thread because we'd prefer + // to preserve the invariant that all operations that modify state happen there. + getHandler().post(() -> { + PackageInstaller installer = mContext.getPackageManager().getPackageInstaller(); + + final PackageInstaller.SessionInfo session = installer.getSessionInfo(sessionId); + if (session == null) { + Log.e(TAG, "No matching install session for: " + sessionId); + result.offer(false); + return; + } + + if (!session.isMultiPackage()) { + if (!enableRollbackForSession(session, null, false)) { + Log.e(TAG, "Unable to enable rollback for session: " + sessionId); + result.offer(false); + return; + } + } else { + for (int childSessionId : session.getChildSessionIds()) { + final PackageInstaller.SessionInfo childSession = + installer.getSessionInfo(childSessionId); + if (childSession == null) { + Log.e(TAG, "No matching child install session for: " + childSessionId); + result.offer(false); + return; + } + if (!enableRollbackForSession(childSession, null, false)) { + Log.e(TAG, "Unable to enable rollback for session: " + sessionId); + result.offer(false); + return; + } + } + } + + result.offer(true); + }); + + try { + return result.take(); + } catch (InterruptedException ie) { + Log.e(TAG, "Interrupted while waiting for notifyStagedSession response"); + return false; + } + } + /** * Gets the version of the package currently installed. * Returns null if the package is not currently installed. @@ -881,57 +947,97 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { @Override public void onFinished(int sessionId, boolean success) { - RollbackData data = null; - synchronized (mLock) { - Integer parentSessionId = mChildSessions.remove(sessionId); - if (parentSessionId != null) { - sessionId = parentSessionId; - } - data = mPendingRollbacks.remove(sessionId); + // If sessionId refers to a staged session, we can't deal with it here since the + // session might take an unbounded amount of time to become "ready" after the package + // installer session is committed. In those cases, we respond to it in response to + // a session ready broadcast. + PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller(); + PackageInstaller.SessionInfo si = packageInstaller.getSessionInfo(sessionId); + if (si != null && si.isStaged()) { + return; } - if (data != null) { - if (success) { - try { - data.timestamp = Instant.now(); - - mRollbackStore.saveAvailableRollback(data); - synchronized (mLock) { - // Note: There is a small window of time between when - // the session has been committed by the package - // manager and when we make the rollback available - // here. Presumably the window is small enough that - // nobody will want to roll back the newly installed - // package before we make the rollback available. - // TODO: We'll lose the rollback data if the - // device reboots between when the session is - // committed and this point. Revisit this after - // adding support for rollback of staged installs. - ensureRollbackDataLoadedLocked(); - mAvailableRollbacks.add(data); - } - // TODO(zezeozue): Provide API to explicitly start observing instead - // of doing this for all rollbacks. If we do this for all rollbacks, - // should document in PackageInstaller.SessionParams#setEnableRollback - // After enabling and commiting any rollback, observe packages and - // prepare to rollback if packages crashes too frequently. - List<String> packages = new ArrayList<>(); - for (int i = 0; i < data.packages.size(); i++) { - packages.add(data.packages.get(i).getPackageName()); - } - mPackageHealthObserver.startObservingHealth(packages, - ROLLBACK_LIFETIME_DURATION_MILLIS); - scheduleExpiration(ROLLBACK_LIFETIME_DURATION_MILLIS); - } catch (IOException e) { - Log.e(TAG, "Unable to enable rollback", e); - mRollbackStore.deleteAvailableRollback(data); + completeEnableRollback(sessionId, success); + } + } + + private void completeEnableRollback(int sessionId, boolean success) { + RollbackData data = null; + synchronized (mLock) { + Integer parentSessionId = mChildSessions.remove(sessionId); + if (parentSessionId != null) { + sessionId = parentSessionId; + } + + data = mPendingRollbacks.remove(sessionId); + } + + if (data != null) { + if (success) { + try { + data.timestamp = Instant.now(); + + mRollbackStore.saveAvailableRollback(data); + synchronized (mLock) { + // Note: There is a small window of time between when + // the session has been committed by the package + // manager and when we make the rollback available + // here. Presumably the window is small enough that + // nobody will want to roll back the newly installed + // package before we make the rollback available. + // TODO: We'll lose the rollback data if the + // device reboots between when the session is + // committed and this point. Revisit this after + // adding support for rollback of staged installs. + ensureRollbackDataLoadedLocked(); + mAvailableRollbacks.add(data); + } + // TODO(zezeozue): Provide API to explicitly start observing instead + // of doing this for all rollbacks. If we do this for all rollbacks, + // should document in PackageInstaller.SessionParams#setEnableRollback + // After enabling and commiting any rollback, observe packages and + // prepare to rollback if packages crashes too frequently. + List<String> packages = new ArrayList<>(); + for (int i = 0; i < data.packages.size(); i++) { + packages.add(data.packages.get(i).getPackageName()); } - } else { - // The install session was aborted, clean up the pending - // install. + mPackageHealthObserver.startObservingHealth(packages, + ROLLBACK_LIFETIME_DURATION_MILLIS); + scheduleExpiration(ROLLBACK_LIFETIME_DURATION_MILLIS); + } catch (IOException e) { + Log.e(TAG, "Unable to enable rollback", e); mRollbackStore.deleteAvailableRollback(data); } + } else { + // The install session was aborted, clean up the pending + // install. + mRollbackStore.deleteAvailableRollback(data); + } + } + } + + private void onStagedSessionUpdated(Intent intent) { + PackageInstaller.SessionInfo pi = intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION); + if (pi == null) { + Log.e(TAG, "Missing intent extra: " + PackageInstaller.EXTRA_SESSION); + return; + } + + if (pi.isStaged()) { + if (!pi.isSessionFailed()) { + // TODO: The session really isn't "enabled" at this point, since more work might + // be required post reboot. + // TODO: We need to make this case consistent with the call from onFinished. + // Ideally, we'd call completeEnableRollback excatly once per multi-package session + // with the parentSessionId only. + completeEnableRollback(pi.sessionId, pi.isSessionReady()); + } else { + // TODO: Clean up the saved rollback when the session fails. This may need to be + // unified with the case where things fail post reboot. } + } else { + Log.e(TAG, "Received onStagedSessionUpdated for: " + pi.sessionId + + ", which isn't staged"); } } diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java index 8e041602c75f..3c6a54af4bd7 100644 --- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -26,6 +26,7 @@ import android.content.rollback.RollbackManager; import android.os.Handler; import android.os.HandlerThread; import android.text.TextUtils; +import android.util.Pair; import android.util.Slog; import android.util.StatsLog; @@ -64,10 +65,8 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve return PackageHealthObserverImpact.USER_IMPACT_NONE; } - RollbackInfo rollback = - getAvailableMainlineRollback(mContext.getSystemService(RollbackManager.class), - failedPackage, moduleMetadataPackage); - if (rollback == null) { + if (getAvailableRollback(mContext.getSystemService(RollbackManager.class), + failedPackage, moduleMetadataPackage) == null) { // Don't handle the notification, no rollbacks available for the package return PackageHealthObserverImpact.USER_IMPACT_NONE; } @@ -84,38 +83,46 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); - RollbackInfo rollback = getAvailableMainlineRollback(rollbackManager, + Pair<RollbackInfo, Boolean> rollbackPair = getAvailableRollback(rollbackManager, failedPackage, moduleMetadataPackage); - if (rollback == null) { - Slog.w(TAG, "Expected rollback but no mainline rollback found for package: [ " + if (rollbackPair == null) { + Slog.w(TAG, "Expected rollback but no valid rollback found for package: [ " + failedPackage.getPackageName() + "] with versionCode: [" + failedPackage.getVersionCode() + "]"); return false; } - - StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED, - StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE, - moduleMetadataPackage.getPackageName(), - moduleMetadataPackage.getVersionCode()); + RollbackInfo rollback = rollbackPair.first; + // We only log mainline package rollbacks, so check if rollback contains the + // module metadata provider, if it does, the rollback is a mainline rollback + boolean hasModuleMetadataPackage = rollbackPair.second; + + if (hasModuleMetadataPackage) { + StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED, + StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE, + moduleMetadataPackage.getPackageName(), + moduleMetadataPackage.getVersionCode()); + } LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> { - int status = result.getIntExtra(RollbackManager.EXTRA_STATUS, - RollbackManager.STATUS_FAILURE); - if (status == RollbackManager.STATUS_SUCCESS) { - StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED, - StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS, - moduleMetadataPackage.getPackageName(), - moduleMetadataPackage.getVersionCode()); - } else { - StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED, - StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, - moduleMetadataPackage.getPackageName(), - moduleMetadataPackage.getVersionCode()); + if (hasModuleMetadataPackage) { + int status = result.getIntExtra(RollbackManager.EXTRA_STATUS, + RollbackManager.STATUS_FAILURE); + if (status == RollbackManager.STATUS_SUCCESS) { + StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED, + StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS, + moduleMetadataPackage.getPackageName(), + moduleMetadataPackage.getVersionCode()); + } else { + StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED, + StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, + moduleMetadataPackage.getPackageName(), + moduleMetadataPackage.getVersionCode()); + } } }); mHandler.post(() -> rollbackManager.commitRollback(rollback.getRollbackId(), - Collections.singletonList(moduleMetadataPackage), + Collections.singletonList(failedPackage), rollbackReceiver.getIntentSender())); // Assume rollback executed successfully return true; @@ -134,7 +141,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs); } - private RollbackInfo getAvailableMainlineRollback(RollbackManager rollbackManager, + private Pair<RollbackInfo, Boolean> getAvailableRollback(RollbackManager rollbackManager, VersionedPackage failedPackage, VersionedPackage moduleMetadataPackage) { for (RollbackInfo rollback : rollbackManager.getAvailableRollbacks()) { // We only rollback mainline packages, so check if rollback contains the @@ -149,8 +156,8 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve && packageRollback.getVersionRolledBackFrom().getVersionCode() == failedPackage.getVersionCode(); } - if (hasModuleMetadataPackage && hasFailedPackage) { - return rollback; + if (hasFailedPackage) { + return new Pair<RollbackInfo, Boolean>(rollback, hasModuleMetadataPackage); } } return null; @@ -159,7 +166,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve private VersionedPackage getModuleMetadataPackage() { String packageName = mContext.getResources().getString( R.string.config_defaultModuleMetadataProvider); - if (!TextUtils.isEmpty(packageName)) { + if (TextUtils.isEmpty(packageName)) { return null; } diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java index 1069530660ff..ff516902a5b3 100644 --- a/services/core/java/com/android/server/rollback/RollbackStore.java +++ b/services/core/java/com/android/server/rollback/RollbackStore.java @@ -201,7 +201,13 @@ class RollbackStore { */ RollbackData createAvailableRollback(int rollbackId) throws IOException { File backupDir = new File(mAvailableRollbacksDir, Integer.toString(rollbackId)); - return new RollbackData(rollbackId, backupDir); + return new RollbackData(rollbackId, backupDir, -1); + } + + RollbackData createPendingStagedRollback(int rollbackId, int stagedSessionId) + throws IOException { + File backupDir = new File(mAvailableRollbacksDir, Integer.toString(rollbackId)); + return new RollbackData(rollbackId, backupDir, stagedSessionId); } /** @@ -240,6 +246,7 @@ class RollbackStore { dataJson.put("rollbackId", data.rollbackId); dataJson.put("packages", toJson(data.packages)); dataJson.put("timestamp", data.timestamp.toString()); + dataJson.put("stagedSessionId", data.stagedSessionId); PrintWriter pw = new PrintWriter(new File(data.backupDir, "rollback.json")); pw.println(dataJson.toString()); @@ -299,7 +306,9 @@ class RollbackStore { IoUtils.readFileAsString(rollbackJsonFile.getAbsolutePath())); int rollbackId = dataJson.getInt("rollbackId"); - RollbackData data = new RollbackData(rollbackId, backupDir); + int stagedSessionId = dataJson.getInt("stagedSessionId"); + RollbackData data = new RollbackData(rollbackId, backupDir, + stagedSessionId); data.packages.addAll(packageRollbackInfosFromJson(dataJson.getJSONArray("packages"))); data.timestamp = Instant.parse(dataJson.getString("timestamp")); return data; @@ -331,6 +340,8 @@ class RollbackStore { json.put("pendingBackups", convertToJsonArray(pendingBackups)); json.put("pendingRestores", convertToJsonArray(pendingRestores)); + json.put("isApex", info.isApex()); + return json; } @@ -345,8 +356,10 @@ class RollbackStore { final ArrayList<RestoreInfo> pendingRestores = convertToRestoreInfoArray( json.getJSONArray("pendingRestores")); + final boolean isApex = json.getBoolean("isApex"); + return new PackageRollbackInfo(versionRolledBackFrom, versionRolledBackTo, - pendingBackups, pendingRestores); + pendingBackups, pendingRestores, isApex); } private JSONArray versionedPackagesToJson(List<VersionedPackage> packages) diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 48e64338262f..35867728f2c9 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -118,7 +118,7 @@ import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.SystemServiceManager; import com.android.server.am.MemoryStatUtil.MemoryStat; -import com.android.server.role.RoleManagerServiceInternal; +import com.android.server.role.RoleManagerInternal; import com.android.server.storage.DiskStatsFileLogger; import com.android.server.storage.DiskStatsLoggingService; @@ -1780,8 +1780,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) { StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); final long elapsedMillis = SystemClock.elapsedRealtime(); - // Fails every 10 buckets. - if (mDebugFailingElapsedClockPullCount++ % 10 == 0) { + // Fails every 5 buckets. + if (mDebugFailingElapsedClockPullCount++ % 5 == 0) { mDebugFailingElapsedClockPreviousValue = elapsedMillis; throw new RuntimeException("Failing debug elapsed clock"); } @@ -1867,16 +1867,16 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { long callingToken = Binder.clearCallingIdentity(); try { PackageManager pm = mContext.getPackageManager(); - RoleManagerServiceInternal rm = - LocalServices.getService(RoleManagerServiceInternal.class); + RoleManagerInternal rmi = LocalServices.getService(RoleManagerInternal.class); List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers(); int numUsers = users.size(); for (int userNum = 0; userNum < numUsers; userNum++) { - UserHandle user = users.get(userNum).getUserHandle(); + int userId = users.get(userNum).getUserHandle().getIdentifier(); - ArrayMap<String, ArraySet<String>> roles = rm.getRoleHoldersAsUser(user); + ArrayMap<String, ArraySet<String>> roles = rmi.getRolesAndHolders( + userId); int numRoles = roles.size(); for (int roleNum = 0; roleNum < numRoles; roleNum++) { @@ -1889,7 +1889,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { PackageInfo pkg; try { - pkg = pm.getPackageInfoAsUser(holderName, 0, user.getIdentifier()); + pkg = pm.getPackageInfoAsUser(holderName, 0, userId); } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, "Role holder " + holderName + " not found"); return; diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index d932a4040cb0..0493ae908f12 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -1280,12 +1280,12 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D @Override public void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount, - int smartActionCount, boolean generatedByAssistant) { + int smartActionCount, boolean generatedByAssistant, boolean editBeforeSending) { enforceStatusBarService(); long identity = Binder.clearCallingIdentity(); try { mNotificationDelegate.onNotificationSmartSuggestionsAdded(key, smartReplyCount, - smartActionCount, generatedByAssistant); + smartActionCount, generatedByAssistant, editBeforeSending); } finally { Binder.restoreCallingIdentity(identity); } @@ -1293,13 +1293,13 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D @Override public void onNotificationSmartReplySent( - String key, int replyIndex, CharSequence reply, boolean generatedByAssistant, - int notificationLocation) throws RemoteException { + String key, int replyIndex, CharSequence reply, int notificationLocation, + boolean modifiedBeforeSending) throws RemoteException { enforceStatusBarService(); long identity = Binder.clearCallingIdentity(); try { mNotificationDelegate.onNotificationSmartReplySent(key, replyIndex, reply, - generatedByAssistant, notificationLocation); + notificationLocation, modifiedBeforeSending); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 111808b54802..18df88b37c61 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -20,8 +20,10 @@ import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LE import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -3250,6 +3252,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mInputMethodTarget = target; mInputMethodTargetWaitingAnim = targetWaitingAnim; assignWindowLayers(false /* setLayoutNeeded */); + mInsetsStateController.onImeTargetChanged(target); } boolean getNeedsMenu(WindowState top, WindowManagerPolicy.WindowState bottom) { diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 66666e681e7a..f67b11b26b12 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -26,6 +26,7 @@ import static android.view.ViewRootImpl.sNewInsetsMode; import android.annotation.NonNull; import android.annotation.Nullable; +import android.graphics.Point; import android.graphics.Rect; import android.util.proto.ProtoOutputStream; import android.view.InsetsState; @@ -135,6 +136,12 @@ class InsetsSourceProvider { mTmpRect.inset(mWin.mGivenContentInsets); } mSource.setFrame(mTmpRect); + if (mControl != null) { + final Rect frame = mWin.getWindowFrames().mFrame; + if (mControl.setSurfacePosition(frame.left, frame.top)) { + mStateController.notifyControlChanged(mControllingWin); + } + } setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.mPolicyVisibility && !mWin.mGivenInsetsPending); } @@ -157,7 +164,8 @@ class InsetsSourceProvider { mWin.startAnimation(mDisplayContent.getPendingTransaction(), mAdapter, !mClientVisible /* hidden */); mControllingWin = target; - mControl = new InsetsSourceControl(mSource.getType(), mAdapter.mCapturedLeash); + mControl = new InsetsSourceControl(mSource.getType(), mAdapter.mCapturedLeash, + new Point(mWin.getWindowFrames().mFrame.left, mWin.getWindowFrames().mFrame.top)); } boolean onInsetsModified(WindowState caller, InsetsSource modifiedSource) { @@ -213,7 +221,8 @@ class InsetsSourceProvider { public void startAnimation(SurfaceControl animationLeash, Transaction t, OnAnimationFinishedCallback finishCallback) { mCapturedLeash = animationLeash; - t.setPosition(mCapturedLeash, mSource.getFrame().left, mSource.getFrame().top); + final Rect frame = mWin.getWindowFrames().mFrame; + t.setPosition(mCapturedLeash, frame.left, frame.top); } @Override diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index bb0cbb1de470..afae9c4ac228 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -204,6 +204,11 @@ class InsetsStateController { mTypeWinControlMap.put(type, win); } + void notifyControlChanged(WindowState target) { + mPendingControlChanged.add(target); + notifyPendingInsetsControlChanged(); + } + private void notifyPendingInsetsControlChanged() { if (mPendingControlChanged.isEmpty()) { return; diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 39568fe2fe3d..888d74163163 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -316,6 +316,7 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta mRotation = rotation; + updateSurfacePosition(); return boundsChange; } @@ -358,6 +359,7 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta } else { mOverrideDisplayedBounds.setEmpty(); } + updateSurfacePosition(); } /** diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index feac6c6f0daf..ed5f5a84eb8b 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -32,6 +32,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.CallSuper; import android.annotation.IntDef; import android.annotation.Nullable; +import android.annotation.TestApi; import android.app.WindowConfiguration; import android.content.res.Configuration; import android.graphics.Point; @@ -1331,6 +1332,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< mLastSurfacePosition.set(mTmpPos.x, mTmpPos.y); } + @TestApi + Point getLastSurfacePosition() { + return mLastSurfacePosition; + } + /** * Displayed bounds specify where to display this container at. It differs from bounds during * certain operations (like animation or interactive dragging). diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 146a8edf244c..3bb660825843 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -4433,7 +4433,8 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Losing focus: " + lastFocus); lastFocus.reportFocusChangedSerialized(false, mInTouchMode); } - } break; + break; + } case REPORT_LOSING_FOCUS: { final DisplayContent displayContent = (DisplayContent) msg.obj; @@ -4450,7 +4451,8 @@ public class WindowManagerService extends IWindowManager.Stub losers.get(i)); losers.get(i).reportFocusChangedSerialized(false, mInTouchMode); } - } break; + break; + } case WINDOW_FREEZE_TIMEOUT: { final DisplayContent displayContent = (DisplayContent) msg.obj; @@ -4614,12 +4616,13 @@ public class WindowManagerService extends IWindowManager.Stub break; } - case NOTIFY_ACTIVITY_DRAWN: + case NOTIFY_ACTIVITY_DRAWN: { try { mActivityTaskManager.notifyActivityDrawn((IBinder) msg.obj); } catch (RemoteException e) { } break; + } case ALL_WINDOWS_DRAWN: { Runnable callback; synchronized (mGlobalLock) { @@ -4656,8 +4659,8 @@ public class WindowManagerService extends IWindowManager.Stub } } } + break; } - break; case CHECK_IF_BOOT_ANIMATION_FINISHED: { final boolean bootAnimationComplete; synchronized (mGlobalLock) { @@ -4667,15 +4670,15 @@ public class WindowManagerService extends IWindowManager.Stub if (bootAnimationComplete) { performEnableScreen(); } + break; } - break; case RESET_ANR_MESSAGE: { synchronized (mGlobalLock) { mLastANRState = null; } mAtmInternal.clearSavedANRState(); + break; } - break; case WALLPAPER_DRAW_PENDING_TIMEOUT: { synchronized (mGlobalLock) { final WallpaperController wallpaperController = @@ -4685,16 +4688,16 @@ public class WindowManagerService extends IWindowManager.Stub mWindowPlacerLocked.performSurfacePlacement(); } } + break; } - break; case UPDATE_DOCKED_STACK_DIVIDER: { synchronized (mGlobalLock) { final DisplayContent displayContent = getDefaultDisplayContentLocked(); displayContent.getDockedDividerController().reevaluateVisibility(false); displayContent.adjustForImeIfNeeded(); } + break; } - break; case WINDOW_REPLACEMENT_TIMEOUT: { synchronized (mGlobalLock) { for (int i = mWindowReplacementTimeouts.size() - 1; i >= 0; i--) { @@ -4703,8 +4706,8 @@ public class WindowManagerService extends IWindowManager.Stub } mWindowReplacementTimeouts.clear(); } + break; } - break; case WINDOW_HIDE_TIMEOUT: { final WindowState window = (WindowState) msg.obj; synchronized (mGlobalLock) { @@ -4724,44 +4727,44 @@ public class WindowManagerService extends IWindowManager.Stub window.setDisplayLayoutNeeded(); mWindowPlacerLocked.performSurfacePlacement(); } + break; } - break; case RESTORE_POINTER_ICON: { synchronized (mGlobalLock) { restorePointerIconLocked((DisplayContent)msg.obj, msg.arg1, msg.arg2); } + break; } - break; case SEAMLESS_ROTATION_TIMEOUT: { final DisplayContent displayContent = (DisplayContent) msg.obj; synchronized (mGlobalLock) { displayContent.onSeamlessRotationTimeout(); } + break; } - break; case SET_HAS_OVERLAY_UI: { mAmInternal.setHasOverlayUi(msg.arg1, msg.arg2 == 1); + break; } - break; case SET_RUNNING_REMOTE_ANIMATION: { mAmInternal.setRunningRemoteAnimation(msg.arg1, msg.arg2 == 1); + break; } - break; case ANIMATION_FAILSAFE: { synchronized (mGlobalLock) { if (mRecentsAnimationController != null) { mRecentsAnimationController.scheduleFailsafe(); } } + break; } - break; case RECOMPUTE_FOCUS: { synchronized (mGlobalLock) { updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /* updateInputWindows */); } + break; } - break; } if (DEBUG_WINDOW_TRACE) { Slog.v(TAG_WM, "handleMessage: exit"); diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 3729eaf63ddb..5c7b287354da 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -127,6 +127,7 @@ cc_defaults { "android.frameworks.schedulerservice@1.0", "android.frameworks.sensorservice@1.0", "android.system.suspend@1.0", + "suspend_control_aidl_interface-cpp", ], static_libs: [ diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp index 024760d46760..5c19ad33617c 100644 --- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp +++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp @@ -32,8 +32,8 @@ #include <android/hardware/power/1.0/IPower.h> #include <android/hardware/power/1.1/IPower.h> #include <android/hardware/power/stats/1.0/IPowerStats.h> -#include <android/system/suspend/1.0/ISystemSuspend.h> -#include <android/system/suspend/1.0/ISystemSuspendCallback.h> +#include <android/system/suspend/BnSuspendCallback.h> +#include <android/system/suspend/ISuspendControlService.h> #include <android_runtime/AndroidRuntime.h> #include <jni.h> @@ -46,14 +46,14 @@ using android::hardware::Return; using android::hardware::Void; +using android::system::suspend::BnSuspendCallback; using android::hardware::power::V1_0::PowerStatePlatformSleepState; using android::hardware::power::V1_0::PowerStateVoter; using android::hardware::power::V1_0::Status; using android::hardware::power::V1_1::PowerStateSubsystem; using android::hardware::power::V1_1::PowerStateSubsystemSleepState; using android::hardware::hidl_vec; -using android::system::suspend::V1_0::ISystemSuspend; -using android::system::suspend::V1_0::ISystemSuspendCallback; +using android::system::suspend::ISuspendControlService; using IPowerV1_1 = android::hardware::power::V1_1::IPower; using IPowerV1_0 = android::hardware::power::V1_0::IPower; @@ -68,7 +68,7 @@ static sem_t wakeup_sem; extern sp<IPowerV1_0> getPowerHalV1_0(); extern sp<IPowerV1_1> getPowerHalV1_1(); extern bool processPowerHalReturn(const Return<void> &ret, const char* functionName); -extern sp<ISystemSuspend> getSuspendHal(); +extern sp<ISuspendControlService> getSuspendControl(); // Java methods used in getLowPowerStats static jmethodID jgetAndUpdatePlatformState = NULL; @@ -103,17 +103,17 @@ struct PowerHalDeathRecipient : virtual public hardware::hidl_death_recipient { sp<PowerHalDeathRecipient> gDeathRecipient = new PowerHalDeathRecipient(); -class WakeupCallback : public ISystemSuspendCallback { -public: - Return<void> notifyWakeup(bool success) override { - ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted"); +class WakeupCallback : public BnSuspendCallback { + public: + binder::Status notifyWakeup(bool success) override { + ALOGI("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted"); int ret = sem_post(&wakeup_sem); if (ret < 0) { char buf[80]; strerror_r(errno, buf, sizeof(buf)); ALOGE("Error posting wakeup sem: %s\n", buf); } - return Void(); + return binder::Status::ok(); } }; @@ -136,9 +136,12 @@ static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf) jniThrowException(env, "java/lang/IllegalStateException", buf); return -1; } - ALOGV("Registering callback..."); - sp<ISystemSuspend> suspendHal = getSuspendHal(); - suspendHal->registerCallback(new WakeupCallback()); + sp<ISuspendControlService> suspendControl = getSuspendControl(); + bool isRegistered = false; + suspendControl->registerCallback(new WakeupCallback(), &isRegistered); + if (!isRegistered) { + ALOGE("Failed to register wakeup callback"); + } } // Wait for wakeup. diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp index 0c9b5f4999a0..9be728bac532 100644 --- a/services/core/jni/com_android_server_power_PowerManagerService.cpp +++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp @@ -20,6 +20,7 @@ #include <android/hardware/power/1.1/IPower.h> #include <android/system/suspend/1.0/ISystemSuspend.h> +#include <android/system/suspend/ISuspendControlService.h> #include <nativehelper/JNIHelp.h> #include "jni.h" @@ -30,13 +31,14 @@ #include <android-base/chrono_utils.h> #include <android_runtime/AndroidRuntime.h> #include <android_runtime/Log.h> +#include <binder/IServiceManager.h> +#include <hardware/power.h> +#include <hardware_legacy/power.h> +#include <hidl/ServiceManagement.h> #include <utils/Timers.h> #include <utils/misc.h> #include <utils/String8.h> #include <utils/Log.h> -#include <hardware/power.h> -#include <hardware_legacy/power.h> -#include <hidl/ServiceManagement.h> #include "com_android_server_power_PowerManagerService.h" @@ -48,6 +50,7 @@ using android::String8; using android::system::suspend::V1_0::ISystemSuspend; using android::system::suspend::V1_0::IWakeLock; using android::system::suspend::V1_0::WakeLockType; +using android::system::suspend::ISuspendControlService; using IPowerV1_1 = android::hardware::power::V1_1::IPower; using IPowerV1_0 = android::hardware::power::V1_0::IPower; @@ -176,6 +179,7 @@ void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t } static sp<ISystemSuspend> gSuspendHal = nullptr; +static sp<ISuspendControlService> gSuspendControl = nullptr; static sp<IWakeLock> gSuspendBlocker = nullptr; static std::mutex gSuspendMutex; @@ -191,18 +195,33 @@ sp<ISystemSuspend> getSuspendHal() { return gSuspendHal; } +sp<ISuspendControlService> getSuspendControl() { + static std::once_flag suspendControlFlag; + std::call_once(suspendControlFlag, [](){ + while(gSuspendControl == nullptr) { + sp<IBinder> control = + defaultServiceManager()->getService(String16("suspend_control")); + if (control != nullptr) { + gSuspendControl = interface_cast<ISuspendControlService>(control); + } + } + }); + return gSuspendControl; +} + void enableAutoSuspend() { static bool enabled = false; - - std::lock_guard<std::mutex> lock(gSuspendMutex); if (!enabled) { - sp<ISystemSuspend> suspendHal = getSuspendHal(); - suspendHal->enableAutosuspend(); - enabled = true; + sp<ISuspendControlService> suspendControl = getSuspendControl(); + suspendControl->enableAutosuspend(&enabled); } - if (gSuspendBlocker) { - gSuspendBlocker->release(); - gSuspendBlocker.clear(); + + { + std::lock_guard<std::mutex> lock(gSuspendMutex); + if (gSuspendBlocker) { + gSuspendBlocker->release(); + gSuspendBlocker.clear(); + } } } diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp index 988d75cfb984..bf96f9a3b71b 100644 --- a/services/core/jni/com_android_server_security_VerityUtils.cpp +++ b/services/core/jni/com_android_server_security_VerityUtils.cpp @@ -27,6 +27,8 @@ #include <sys/stat.h> #include <sys/types.h> +#include <type_traits> + #include <android-base/unique_fd.h> // TODO(112037636): Always include once fsverity.h is upstreamed. @@ -99,8 +101,14 @@ namespace { class JavaByteArrayHolder { public: - static JavaByteArrayHolder* newArray(JNIEnv* env, jsize size) { - return new JavaByteArrayHolder(env, size); + JavaByteArrayHolder(const JavaByteArrayHolder &other) = delete; + JavaByteArrayHolder(JavaByteArrayHolder &&other) + : mEnv(other.mEnv), mBytes(other.mBytes), mElements(other.mElements) { + other.mElements = nullptr; + } + + static JavaByteArrayHolder newArray(JNIEnv* env, jsize size) { + return JavaByteArrayHolder(env, size); } jbyte* getRaw() { @@ -113,6 +121,10 @@ class JavaByteArrayHolder { return mBytes; } + ~JavaByteArrayHolder() { + LOG_ALWAYS_FATAL_IF(mElements == nullptr, "Elements are not released"); + } + private: JavaByteArrayHolder(JNIEnv* env, jsize size) { mEnv = env; @@ -121,10 +133,6 @@ class JavaByteArrayHolder { memset(mElements, 0, size); } - virtual ~JavaByteArrayHolder() { - LOG_ALWAYS_FATAL_IF(mElements == nullptr, "Elements are not released"); - } - JNIEnv* mEnv; jbyteArray mBytes; jbyte* mElements; @@ -143,8 +151,10 @@ int enableFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) { } int measureFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) { - auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_digest) + kSha256Bytes); - fsverity_digest* data = reinterpret_cast<fsverity_digest*>(raii->getRaw()); + using Storage = std::aligned_storage_t<sizeof(fsverity_digest) + kSha256Bytes>; + + Storage bytes; + fsverity_digest *data = reinterpret_cast<fsverity_digest *>(&bytes); data->digest_size = kSha256Bytes; // the only input/output parameter const char* path = env->GetStringUTFChars(filePath, nullptr); @@ -160,7 +170,7 @@ int measureFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) { jbyteArray constructFsveritySignedData(JNIEnv* env, jobject /* clazz */, jbyteArray digest) { auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_digest_disk) + kSha256Bytes); - fsverity_digest_disk* data = reinterpret_cast<fsverity_digest_disk*>(raii->getRaw()); + fsverity_digest_disk* data = reinterpret_cast<fsverity_digest_disk*>(raii.getRaw()); data->digest_algorithm = FS_VERITY_ALG_SHA256; data->digest_size = kSha256Bytes; @@ -172,13 +182,13 @@ jbyteArray constructFsveritySignedData(JNIEnv* env, jobject /* clazz */, jbyteAr const jbyte* src = env->GetByteArrayElements(digest, nullptr); memcpy(data->digest, src, kSha256Bytes); - return raii->release(); + return raii.release(); } jbyteArray constructFsverityDescriptor(JNIEnv* env, jobject /* clazz */, jlong fileSize) { auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_descriptor)); - fsverity_descriptor* desc = reinterpret_cast<fsverity_descriptor*>(raii->getRaw()); + fsverity_descriptor* desc = reinterpret_cast<fsverity_descriptor*>(raii.getRaw()); memcpy(desc->magic, FS_VERITY_MAGIC, sizeof(desc->magic)); desc->major_version = 1; @@ -191,29 +201,29 @@ jbyteArray constructFsverityDescriptor(JNIEnv* env, jobject /* clazz */, jlong f desc->orig_file_size = fileSize; desc->auth_ext_count = 1; - return raii->release(); + return raii.release(); } jbyteArray constructFsverityExtension(JNIEnv* env, jobject /* clazz */, jshort extensionId, jint extensionDataSize) { auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_extension)); - fsverity_extension* ext = reinterpret_cast<fsverity_extension*>(raii->getRaw()); + fsverity_extension* ext = reinterpret_cast<fsverity_extension*>(raii.getRaw()); ext->length = sizeof(fsverity_extension) + extensionDataSize; ext->type = extensionId; - return raii->release(); + return raii.release(); } jbyteArray constructFsverityFooter(JNIEnv* env, jobject /* clazz */, jint offsetToDescriptorHead) { auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_footer)); - fsverity_footer* footer = reinterpret_cast<fsverity_footer*>(raii->getRaw()); + fsverity_footer* footer = reinterpret_cast<fsverity_footer*>(raii.getRaw()); footer->desc_reverse_offset = offsetToDescriptorHead + sizeof(fsverity_footer); memcpy(footer->magic, FS_VERITY_MAGIC, sizeof(footer->magic)); - return raii->release(); + return raii.release(); } const JNINativeMethod sMethods[] = { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 71ed5ae685bb..ab30cda271f0 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -102,7 +102,6 @@ import com.android.server.lights.LightsService; import com.android.server.media.MediaResourceMonitorService; import com.android.server.media.MediaRouterService; import com.android.server.media.MediaSessionService; -import com.android.server.media.MediaUpdateService; import com.android.server.media.projection.MediaProjectionManagerService; import com.android.server.net.NetworkPolicyManagerService; import com.android.server.net.NetworkStatsService; @@ -1286,47 +1285,45 @@ public final class SystemServer { } traceEnd(); - if (!mOnlyCore) { - if (context.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_WIFI)) { - // Wifi Service must be started first for wifi-related services. - traceBeginAndSlog("StartWifi"); - mSystemServiceManager.startService(WIFI_SERVICE_CLASS); - traceEnd(); - traceBeginAndSlog("StartWifiScanning"); - mSystemServiceManager.startService( - "com.android.server.wifi.scanner.WifiScanningService"); - traceEnd(); - } + if (context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WIFI)) { + // Wifi Service must be started first for wifi-related services. + traceBeginAndSlog("StartWifi"); + mSystemServiceManager.startService(WIFI_SERVICE_CLASS); + traceEnd(); + traceBeginAndSlog("StartWifiScanning"); + mSystemServiceManager.startService( + "com.android.server.wifi.scanner.WifiScanningService"); + traceEnd(); + } - if (context.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_WIFI_RTT)) { - traceBeginAndSlog("StartRttService"); - mSystemServiceManager.startService( - "com.android.server.wifi.rtt.RttService"); - traceEnd(); - } + if (context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WIFI_RTT)) { + traceBeginAndSlog("StartRttService"); + mSystemServiceManager.startService( + "com.android.server.wifi.rtt.RttService"); + traceEnd(); + } - if (context.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_WIFI_AWARE)) { - traceBeginAndSlog("StartWifiAware"); - mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS); - traceEnd(); - } + if (context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WIFI_AWARE)) { + traceBeginAndSlog("StartWifiAware"); + mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS); + traceEnd(); + } - if (context.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_WIFI_DIRECT)) { - traceBeginAndSlog("StartWifiP2P"); - mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS); - traceEnd(); - } + if (context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WIFI_DIRECT)) { + traceBeginAndSlog("StartWifiP2P"); + mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS); + traceEnd(); + } - if (context.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_LOWPAN)) { - traceBeginAndSlog("StartLowpan"); - mSystemServiceManager.startService(LOWPAN_SERVICE_CLASS); - traceEnd(); - } + if (context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_LOWPAN)) { + traceBeginAndSlog("StartLowpan"); + mSystemServiceManager.startService(LOWPAN_SERVICE_CLASS); + traceEnd(); } if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET) || @@ -1688,10 +1685,6 @@ public final class SystemServer { mSystemServiceManager.startService(MediaSessionService.class); traceEnd(); - traceBeginAndSlog("StartMediaUpdateService"); - mSystemServiceManager.startService(MediaUpdateService.class); - traceEnd(); - if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_HDMI_CEC)) { traceBeginAndSlog("StartHdmiControlService"); mSystemServiceManager.startService(HdmiControlService.class); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java index 48c89025e865..11bd29d8a163 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java @@ -23,8 +23,10 @@ import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.tv.cec.V1_0.SendMessageResult; import android.os.Looper; import android.os.test.TestLooper; + import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -42,9 +44,8 @@ public class ArcTerminationActionFromAvrTest { private TestLooper mTestLooper = new TestLooper(); private boolean mSendCecCommandSuccess; private boolean mShouldDispatchReportArcTerminated; - private boolean mArcEnabled; - private boolean mSetArcStatusCalled; private Instrumentation mInstrumentation; + @Nullable private Boolean mArcEnabled = null; @Before public void setUp() { @@ -102,7 +103,6 @@ public class ArcTerminationActionFromAvrTest { @Override void setArcStatus(boolean enabled) { - mSetArcStatusCalled = true; mArcEnabled = enabled; } }; @@ -110,45 +110,38 @@ public class ArcTerminationActionFromAvrTest { Looper looper = mTestLooper.getLooper(); hdmiControlService.setIoLooper(looper); - mArcEnabled = true; mAction = new ArcTerminationActionFromAvr(mHdmiCecLocalDeviceAudioSystem); } @Test - public void testSendMessage_NotSuccess() { + public void testSendMessage_notSuccess() { mSendCecCommandSuccess = false; mShouldDispatchReportArcTerminated = false; - mSetArcStatusCalled = false; mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction); mTestLooper.dispatchAll(); - assertThat(mSetArcStatusCalled).isFalse(); - assertThat(mArcEnabled).isTrue(); + assertThat(mArcEnabled).isNull(); } @Test - public void testReportArcTerminated_NotReceived() { + public void testReportArcTerminated_notReceived() { mSendCecCommandSuccess = true; mShouldDispatchReportArcTerminated = false; - mSetArcStatusCalled = false; mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction); mTestLooper.moveTimeForward(1000); mTestLooper.dispatchAll(); - assertThat(mSetArcStatusCalled).isFalse(); - assertThat(mArcEnabled).isTrue(); + assertThat(mArcEnabled).isNull(); } @Test - public void testReportArcTerminated_Received() { + public void testReportArcTerminated_received() { mSendCecCommandSuccess = true; mShouldDispatchReportArcTerminated = true; - mSetArcStatusCalled = false; mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction); mTestLooper.moveTimeForward(1000); mTestLooper.dispatchAll(); - assertThat(mSetArcStatusCalled).isTrue(); assertThat(mArcEnabled).isFalse(); } } diff --git a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java index 33cbf7ae8ba6..f1e6bc25dabf 100644 --- a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java @@ -80,10 +80,10 @@ public class AppDataRollbackHelperTest { } private static RollbackData createInProgressRollbackData(String packageName) { - RollbackData data = new RollbackData(1, new File("/does/not/exist")); + RollbackData data = new RollbackData(1, new File("/does/not/exist"), -1); data.packages.add(new PackageRollbackInfo( new VersionedPackage(packageName, 1), new VersionedPackage(packageName, 1), - new IntArray(), new ArrayList<>())); + new IntArray(), new ArrayList<>(), false)); data.inProgress = true; return data; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 4cae3b3f9ae7..72aa0f2ba09e 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -3925,13 +3925,16 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testOnNotificationSmartReplySent() { final int replyIndex = 2; final String reply = "Hello"; + final boolean modifiedBeforeSending = true; final boolean generatedByAssistant = true; NotificationRecord r = generateNotificationRecord(mTestNotificationChannel); + r.setSuggestionsGeneratedByAssistant(generatedByAssistant); mService.addNotification(r); mService.mNotificationDelegate.onNotificationSmartReplySent( - r.getKey(), replyIndex, reply, generatedByAssistant, NOTIFICATION_LOCATION_UNKNOWN); + r.getKey(), replyIndex, reply, NOTIFICATION_LOCATION_UNKNOWN, + modifiedBeforeSending); verify(mAssistants).notifyAssistantSuggestedReplySent( eq(r.sbn), eq(reply), eq(generatedByAssistant)); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java index ae211d3f1e38..41842010d3a3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -22,6 +22,8 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import android.graphics.Point; +import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; @@ -123,4 +125,19 @@ public class TaskTests extends WindowTestsBase { assertEquals(1, task2.positionInParent()); assertTrue(task.mOnDisplayChangedCalled); } + + @Test + public void testBounds() { + final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent); + final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack1); + + // Check that setting bounds also updates surface position + Rect bounds = new Rect(10, 10, 100, 200); + task.setBounds(bounds); + assertEquals(new Point(bounds.left, bounds.top), task.getLastSurfacePosition()); + + Rect dispBounds = new Rect(20, 30, 110, 220); + task.setOverrideDisplayedBounds(dispBounds); + assertEquals(new Point(dispBounds.left, dispBounds.top), task.getLastSurfacePosition()); + } } diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 85939d498755..a6d7ee6d85e2 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -432,6 +432,7 @@ public class UsageStatsService extends SystemService implements synchronized (mLock) { mHandler.removeMessages(MSG_REPORT_EVENT); Event event = new Event(DEVICE_SHUTDOWN, SystemClock.elapsedRealtime()); + event.mPackage = Event.DEVICE_EVENT_PACKAGE_NAME; // orderly shutdown, the last event is DEVICE_SHUTDOWN. reportEventToAllUserId(event); flushToDiskLocked(); @@ -449,6 +450,7 @@ public class UsageStatsService extends SystemService implements */ void prepareForPossibleShutdown() { Event event = new Event(DEVICE_SHUTDOWN, SystemClock.elapsedRealtime()); + event.mPackage = Event.DEVICE_EVENT_PACKAGE_NAME; mHandler.obtainMessage(MSG_REPORT_EVENT_TO_ALL_USERID, event).sendToTarget(); mHandler.sendEmptyMessage(MSG_FLUSH_TO_DISK); } diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index d52d32faa7d1..3cb22162e36c 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -143,6 +143,7 @@ class UserUsageStatsService { if (size == 0 || currentDailyStats.events.get(size - 1).mEventType != DEVICE_SHUTDOWN) { // The last event in event list is not DEVICE_SHUTDOWN, then we insert one. final Event event = new Event(DEVICE_SHUTDOWN, currentDailyStats.lastTimeSaved); + event.mPackage = Event.DEVICE_EVENT_PACKAGE_NAME; currentDailyStats.addEvent(event); } } diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp index f5b4308a1b50..37caeb2044ff 100644 --- a/startop/view_compiler/Android.bp +++ b/startop/view_compiler/Android.bp @@ -58,6 +58,8 @@ cc_library_static { "util.cc", "layout_validation.cc", ], + // b/123880763, clang-tidy analyzer has segmentation fault with dex_builder.cc + tidy_checks: ["-clang-analyzer-*"], host_supported: true, } diff --git a/telephony/java/android/telephony/AvailableNetworkInfo.java b/telephony/java/android/telephony/AvailableNetworkInfo.java index 4da79b34a55e..b407b2a03bc4 100644 --- a/telephony/java/android/telephony/AvailableNetworkInfo.java +++ b/telephony/java/android/telephony/AvailableNetworkInfo.java @@ -114,7 +114,7 @@ public final class AvailableNetworkInfo implements Parcelable { in.readStringList(mMccMncs); } - public AvailableNetworkInfo(int subId, int priority, ArrayList<String> mccMncs) { + public AvailableNetworkInfo(int subId, int priority, List<String> mccMncs) { mSubId = subId; mPriority = priority; mMccMncs = new ArrayList<String>(mccMncs); diff --git a/telephony/java/android/telephony/CallAttributes.java b/telephony/java/android/telephony/CallAttributes.java index a4cce9c9a7b9..0d4f09f98b43 100644 --- a/telephony/java/android/telephony/CallAttributes.java +++ b/telephony/java/android/telephony/CallAttributes.java @@ -117,9 +117,9 @@ public class CallAttributes implements Parcelable { CallAttributes s = (CallAttributes) o; - return (mPreciseCallState == s.mPreciseCallState + return (Objects.equals(mPreciseCallState, s.mPreciseCallState) && mNetworkType == s.mNetworkType - && mCallQuality == s.mCallQuality); + && Objects.equals(mCallQuality, s.mCallQuality)); } /** diff --git a/telephony/java/android/telephony/PreciseCallState.java b/telephony/java/android/telephony/PreciseCallState.java index 59f3e1f0e7f7..19e1931a30df 100644 --- a/telephony/java/android/telephony/PreciseCallState.java +++ b/telephony/java/android/telephony/PreciseCallState.java @@ -287,11 +287,11 @@ public final class PreciseCallState implements Parcelable { return false; } PreciseCallState other = (PreciseCallState) obj; - return (mRingingCallState != other.mRingingCallState && - mForegroundCallState != other.mForegroundCallState && - mBackgroundCallState != other.mBackgroundCallState && - mDisconnectCause != other.mDisconnectCause && - mPreciseDisconnectCause != other.mPreciseDisconnectCause); + return (mRingingCallState == other.mRingingCallState + && mForegroundCallState == other.mForegroundCallState + && mBackgroundCallState == other.mBackgroundCallState + && mDisconnectCause == other.mDisconnectCause + && mPreciseDisconnectCause == other.mPreciseDisconnectCause); } @Override diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 7c3bde4293c7..f2a93400fd13 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -7974,9 +7974,7 @@ public class TelephonyManager { * support for the feature and device firmware support. * * @return {@code true} if the device and carrier both support RTT, {@code false} otherwise. - * @hide */ - @TestApi public boolean isRttSupported() { try { ITelephony telephony = getITelephony(); @@ -9702,10 +9700,10 @@ public class TelephonyManager { * * <p> * Requires Permission: - * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} * @hide */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled() { String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>"; boolean isEnabled = false; @@ -10093,12 +10091,17 @@ public class TelephonyManager { * Get preferred opportunistic data subscription Id * * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}), - * or has permission {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}. + * or has either READ_PRIVILEGED_PHONE_STATE + * or {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} permission. * @return subId preferred opportunistic subscription id or * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} if there are no preferred * subscription id * */ + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PHONE_STATE + }) public int getPreferredOpportunisticDataSubscription() { String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>"; int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java index 4d95e552c1da..d8d2d9e5951a 100644 --- a/telephony/java/android/telephony/ims/ImsReasonInfo.java +++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java @@ -465,7 +465,7 @@ public final class ImsReasonInfo implements Parcelable { public static final int CODE_USER_REJECTED_SESSION_MODIFICATION = 511; /** - * Upgrade Downgrade request cacncelled by the user who initiated it + * Upgrade Downgrade request cancelled by the user who initiated it */ public static final int CODE_USER_CANCELLED_SESSION_MODIFICATION = 512; @@ -887,6 +887,185 @@ public final class ImsReasonInfo implements Parcelable { public static final int CODE_OEM_CAUSE_15 = 0xf00f; /** + * @hide + */ + @IntDef(value = { + CODE_UNSPECIFIED, + CODE_LOCAL_ILLEGAL_ARGUMENT, + CODE_LOCAL_ILLEGAL_STATE, + CODE_LOCAL_INTERNAL_ERROR, + CODE_LOCAL_IMS_SERVICE_DOWN, + CODE_LOCAL_NO_PENDING_CALL, + CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE, + CODE_LOCAL_POWER_OFF, + CODE_LOCAL_LOW_BATTERY, + CODE_LOCAL_NETWORK_NO_SERVICE, + CODE_LOCAL_NETWORK_NO_LTE_COVERAGE, + CODE_LOCAL_NETWORK_ROAMING, + CODE_LOCAL_NETWORK_IP_CHANGED, + CODE_LOCAL_SERVICE_UNAVAILABLE, + CODE_LOCAL_NOT_REGISTERED, + CODE_LOCAL_CALL_EXCEEDED, + CODE_LOCAL_CALL_BUSY, + CODE_LOCAL_CALL_DECLINE, + CODE_LOCAL_CALL_VCC_ON_PROGRESSING, + CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED, + CODE_LOCAL_CALL_CS_RETRY_REQUIRED, + CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED, + CODE_LOCAL_CALL_TERMINATED, + CODE_LOCAL_HO_NOT_FEASIBLE, + CODE_TIMEOUT_1XX_WAITING, + CODE_TIMEOUT_NO_ANSWER, + CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE, + CODE_CALL_BARRED, + CODE_FDN_BLOCKED, + CODE_IMEI_NOT_ACCEPTED, + CODE_DIAL_MODIFIED_TO_USSD, + CODE_DIAL_MODIFIED_TO_SS, + CODE_DIAL_MODIFIED_TO_DIAL, + CODE_DIAL_MODIFIED_TO_DIAL_VIDEO, + CODE_DIAL_VIDEO_MODIFIED_TO_DIAL, + CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO, + CODE_DIAL_VIDEO_MODIFIED_TO_SS, + CODE_DIAL_VIDEO_MODIFIED_TO_USSD, + CODE_SIP_REDIRECTED, + CODE_SIP_BAD_REQUEST, + CODE_SIP_FORBIDDEN, + CODE_SIP_NOT_FOUND, + CODE_SIP_NOT_SUPPORTED, + CODE_SIP_REQUEST_TIMEOUT, + CODE_SIP_TEMPRARILY_UNAVAILABLE, + CODE_SIP_BAD_ADDRESS, + CODE_SIP_BUSY, + CODE_SIP_REQUEST_CANCELLED, + CODE_SIP_NOT_ACCEPTABLE, + CODE_SIP_NOT_REACHABLE, + CODE_SIP_CLIENT_ERROR, + CODE_SIP_TRANSACTION_DOES_NOT_EXIST, + CODE_SIP_SERVER_INTERNAL_ERROR, + CODE_SIP_SERVICE_UNAVAILABLE, + CODE_SIP_SERVER_TIMEOUT, + CODE_SIP_SERVER_ERROR, + CODE_SIP_USER_REJECTED, + CODE_SIP_GLOBAL_ERROR, + CODE_EMERGENCY_TEMP_FAILURE, + CODE_EMERGENCY_PERM_FAILURE, + CODE_SIP_USER_MARKED_UNWANTED, + CODE_SIP_METHOD_NOT_ALLOWED, + CODE_SIP_PROXY_AUTHENTICATION_REQUIRED, + CODE_SIP_REQUEST_ENTITY_TOO_LARGE, + CODE_SIP_REQUEST_URI_TOO_LARGE, + CODE_SIP_EXTENSION_REQUIRED, + CODE_SIP_INTERVAL_TOO_BRIEF, + CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST, + CODE_SIP_LOOP_DETECTED, + CODE_SIP_TOO_MANY_HOPS, + CODE_SIP_AMBIGUOUS, + CODE_SIP_REQUEST_PENDING, + CODE_SIP_UNDECIPHERABLE, + CODE_MEDIA_INIT_FAILED, + CODE_MEDIA_NO_DATA, + CODE_MEDIA_NOT_ACCEPTABLE, + CODE_MEDIA_UNSPECIFIED, + CODE_USER_TERMINATED, + CODE_USER_NOANSWER, + CODE_USER_IGNORE, + CODE_USER_DECLINE, + CODE_LOW_BATTERY, + CODE_BLACKLISTED_CALL_ID, + CODE_USER_TERMINATED_BY_REMOTE, + CODE_USER_REJECTED_SESSION_MODIFICATION, + CODE_USER_CANCELLED_SESSION_MODIFICATION, + CODE_SESSION_MODIFICATION_FAILED, + CODE_UT_NOT_SUPPORTED, + CODE_UT_SERVICE_UNAVAILABLE, + CODE_UT_OPERATION_NOT_ALLOWED, + CODE_UT_NETWORK_ERROR, + CODE_UT_CB_PASSWORD_MISMATCH, + CODE_UT_SS_MODIFIED_TO_DIAL, + CODE_UT_SS_MODIFIED_TO_USSD, + CODE_UT_SS_MODIFIED_TO_SS, + CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO, + CODE_ECBM_NOT_SUPPORTED, + CODE_MULTIENDPOINT_NOT_SUPPORTED, + CODE_REGISTRATION_ERROR, + CODE_ANSWERED_ELSEWHERE, + CODE_CALL_PULL_OUT_OF_SYNC, + CODE_CALL_END_CAUSE_CALL_PULL, + CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE, + CODE_REJECTED_ELSEWHERE, + CODE_SUPP_SVC_FAILED, + CODE_SUPP_SVC_CANCELLED, + CODE_SUPP_SVC_REINVITE_COLLISION, + CODE_IWLAN_DPD_FAILURE, + CODE_EPDG_TUNNEL_ESTABLISH_FAILURE, + CODE_EPDG_TUNNEL_REKEY_FAILURE, + CODE_EPDG_TUNNEL_LOST_CONNECTION, + CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED, + CODE_REMOTE_CALL_DECLINE, + CODE_DATA_LIMIT_REACHED, + CODE_DATA_DISABLED, + CODE_WIFI_LOST, + CODE_IKEV2_AUTH_FAILURE, + CODE_RADIO_OFF, + CODE_NO_VALID_SIM, + CODE_RADIO_INTERNAL_ERROR, + CODE_NETWORK_RESP_TIMEOUT, + CODE_NETWORK_REJECT, + CODE_RADIO_ACCESS_FAILURE, + CODE_RADIO_LINK_FAILURE, + CODE_RADIO_LINK_LOST, + CODE_RADIO_UPLINK_FAILURE, + CODE_RADIO_SETUP_FAILURE, + CODE_RADIO_RELEASE_NORMAL, + CODE_RADIO_RELEASE_ABNORMAL, + CODE_ACCESS_CLASS_BLOCKED, + CODE_NETWORK_DETACH, + CODE_SIP_ALTERNATE_EMERGENCY_CALL, + CODE_UNOBTAINABLE_NUMBER, + CODE_NO_CSFB_IN_CS_ROAM, + CODE_REJECT_UNKNOWN, + CODE_REJECT_ONGOING_CALL_WAITING_DISABLED, + CODE_REJECT_CALL_ON_OTHER_SUB, + CODE_REJECT_1X_COLLISION, + CODE_REJECT_SERVICE_NOT_REGISTERED, + CODE_REJECT_CALL_TYPE_NOT_ALLOWED, + CODE_REJECT_ONGOING_E911_CALL, + CODE_REJECT_ONGOING_CALL_SETUP, + CODE_REJECT_MAX_CALL_LIMIT_REACHED, + CODE_REJECT_UNSUPPORTED_SIP_HEADERS, + CODE_REJECT_UNSUPPORTED_SDP_HEADERS, + CODE_REJECT_ONGOING_CALL_TRANSFER, + CODE_REJECT_INTERNAL_ERROR, + CODE_REJECT_QOS_FAILURE, + CODE_REJECT_ONGOING_HANDOVER, + CODE_REJECT_VT_TTY_NOT_ALLOWED, + CODE_REJECT_ONGOING_CALL_UPGRADE, + CODE_REJECT_CONFERENCE_TTY_NOT_ALLOWED, + CODE_REJECT_ONGOING_CONFERENCE_CALL, + CODE_REJECT_VT_AVPF_NOT_ALLOWED, + CODE_REJECT_ONGOING_ENCRYPTED_CALL, + CODE_REJECT_ONGOING_CS_CALL, + CODE_OEM_CAUSE_1, + CODE_OEM_CAUSE_2, + CODE_OEM_CAUSE_3, + CODE_OEM_CAUSE_4, + CODE_OEM_CAUSE_5, + CODE_OEM_CAUSE_6, + CODE_OEM_CAUSE_7, + CODE_OEM_CAUSE_8, + CODE_OEM_CAUSE_9, + CODE_OEM_CAUSE_10, + CODE_OEM_CAUSE_11, + CODE_OEM_CAUSE_12, + CODE_OEM_CAUSE_13, + CODE_OEM_CAUSE_14, + CODE_OEM_CAUSE_15 + }, prefix = "CODE_") + @Retention(RetentionPolicy.SOURCE) + public @interface ImsCode {} + + /** * Network string error messages. * mExtraMessage may have these values. */ @@ -964,7 +1143,7 @@ public final class ImsReasonInfo implements Parcelable { /** * @return an integer representing more information about the completion of an operation. */ - public int getCode() { + public @ImsCode int getCode() { return mCode; } diff --git a/telephony/java/android/telephony/mbms/GroupCallCallback.java b/telephony/java/android/telephony/mbms/GroupCallCallback.java index 77e36bbcf2ae..603f4e6d2030 100644 --- a/telephony/java/android/telephony/mbms/GroupCallCallback.java +++ b/telephony/java/android/telephony/mbms/GroupCallCallback.java @@ -57,7 +57,7 @@ public interface GroupCallCallback { * @param errorCode The error code. * @param message A human-readable message generated by the middleware for debugging purposes. */ - void onError(@GroupCallError int errorCode, @Nullable String message); + default void onError(@GroupCallError int errorCode, @Nullable String message) {} /** * Called to indicate this call has changed state. @@ -65,8 +65,8 @@ public interface GroupCallCallback { * See {@link GroupCall#STATE_STOPPED}, {@link GroupCall#STATE_STARTED} * and {@link GroupCall#STATE_STALLED}. */ - void onGroupCallStateChanged(@GroupCall.GroupCallState int state, - @GroupCall.GroupCallStateChangeReason int reason); + default void onGroupCallStateChanged(@GroupCall.GroupCallState int state, + @GroupCall.GroupCallStateChangeReason int reason) {} /** * Broadcast Signal Strength updated. @@ -78,5 +78,6 @@ public interface GroupCallCallback { * {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available * for this call due to timing, geography or popularity. */ - void onBroadcastSignalStrengthUpdated(@IntRange(from = -1, to = 4) int signalStrength); + default void onBroadcastSignalStrengthUpdated( + @IntRange(from = -1, to = 4) int signalStrength) {} } diff --git a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java index 04e7ba1af372..ac7e17271e34 100644 --- a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java +++ b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java @@ -57,7 +57,7 @@ public interface MbmsGroupCallSessionCallback { * @param errorCode The error code. * @param message A human-readable message generated by the middleware for debugging purposes. */ - void onError(@GroupCallError int errorCode, @Nullable String message); + default void onError(@GroupCallError int errorCode, @Nullable String message) {} /** * Indicates that the list of currently available SAIs has been updated. The app may use this @@ -70,8 +70,8 @@ public interface MbmsGroupCallSessionCallback { * @param availableSais A list of lists of available SAIS in neighboring cells, where each list * contains the available SAIs in an individual cell. */ - void onAvailableSaisUpdated(@NonNull List<Integer> currentSais, - @NonNull List<List<Integer>> availableSais); + default void onAvailableSaisUpdated(@NonNull List<Integer> currentSais, + @NonNull List<List<Integer>> availableSais) {} /** * Called soon after the app calls {@link MbmsGroupCallSession#create}. The information supplied @@ -85,7 +85,7 @@ public interface MbmsGroupCallSessionCallback { * @param interfaceName The interface name for the data link. * @param index The index for the data link. */ - void onServiceInterfaceAvailable(@NonNull String interfaceName, int index); + default void onServiceInterfaceAvailable(@NonNull String interfaceName, int index) {} /** * Called to indicate that the middleware has been initialized and is ready. @@ -95,5 +95,5 @@ public interface MbmsGroupCallSessionCallback { * delivered via {@link #onError(int, String)} with error code * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}. */ - void onMiddlewareReady(); + default void onMiddlewareReady() {} } diff --git a/tests/RollbackTest/Android.mk b/tests/RollbackTest/Android.mk index 780bb24e437b..40d4eff53577 100644 --- a/tests/RollbackTest/Android.mk +++ b/tests/RollbackTest/Android.mk @@ -76,12 +76,14 @@ LOCAL_PACKAGE_NAME := RollbackTest LOCAL_MODULE_TAGS := tests LOCAL_STATIC_JAVA_LIBRARIES := android-support-test LOCAL_COMPATIBILITY_SUITE := general-tests +LOCAL_COMPATIBILITY_SUPPORT_FILES := $(ROLLBACK_TEST_APEX_V1) LOCAL_JAVA_RESOURCE_FILES := \ $(ROLLBACK_TEST_APP_AV1) \ $(ROLLBACK_TEST_APP_AV2) \ $(ROLLBACK_TEST_APP_A_CRASHING_V2) \ $(ROLLBACK_TEST_APP_BV1) \ - $(ROLLBACK_TEST_APP_BV2) + $(ROLLBACK_TEST_APP_BV2) \ + $(ROLLBACK_TEST_APEX_V2) LOCAL_SDK_VERSION := system_current LOCAL_TEST_CONFIG := RollbackTest.xml include $(BUILD_PACKAGE) diff --git a/tests/RollbackTest/TestApex/Android.bp b/tests/RollbackTest/TestApex/Android.bp new file mode 100644 index 000000000000..a2a8e1783f3b --- /dev/null +++ b/tests/RollbackTest/TestApex/Android.bp @@ -0,0 +1,56 @@ +// 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. + +apex { + name: "com.android.tests.rollback.testapex.RollbackTestApexV1", + manifest: "RollbackTestApexV1.json", + file_contexts: "apex.test", + prebuilts: ["RollbackTestApex.prebuilt.txt"], + key: "RollbackTestApex.key", + installable: false, +} + +apex { + name: "com.android.tests.rollback.testapex.RollbackTestApexV2", + manifest: "RollbackTestApexV2.json", + file_contexts: "apex.test", + prebuilts: ["RollbackTestApex.prebuilt.txt"], + key: "RollbackTestApex.key", + installable: false, +} + +apex_key { + name: "RollbackTestApex.key", + public_key: "com.android.tests.rollback.testapex.avbpubkey", + private_key: "com.android.tests.rollback.testapex.pem", + installable: false, +} + +prebuilt_etc { + name: "RollbackTestApex.prebuilt.txt", + src: "RollbackTestApex.prebuilt.txt", +} + +filegroup { + name: "RollbackTestApexV1_filegroup", + srcs: [":com.android.tests.rollback.testapex.RollbackTestApexV1"], + export_to_make_var: "ROLLBACK_TEST_APEX_V1", +} + +filegroup { + name: "RollbackTestApexV2_filegroup", + srcs: [":com.android.tests.rollback.testapex.RollbackTestApexV2"], + export_to_make_var: "ROLLBACK_TEST_APEX_V2", +} + diff --git a/tests/RollbackTest/TestApex/RollbackTestApex.prebuilt.txt b/tests/RollbackTest/TestApex/RollbackTestApex.prebuilt.txt new file mode 100644 index 000000000000..e871146fb824 --- /dev/null +++ b/tests/RollbackTest/TestApex/RollbackTestApex.prebuilt.txt @@ -0,0 +1,3 @@ + +This file contains dummy content to include in the RollbackTestApex. + diff --git a/tests/RollbackTest/TestApex/RollbackTestApexV1.json b/tests/RollbackTest/TestApex/RollbackTestApexV1.json new file mode 100644 index 000000000000..c3239ca0d902 --- /dev/null +++ b/tests/RollbackTest/TestApex/RollbackTestApexV1.json @@ -0,0 +1,4 @@ +{ + "name": "com.android.tests.rollback.testapex", + "version": 1 +} diff --git a/tests/RollbackTest/TestApex/RollbackTestApexV2.json b/tests/RollbackTest/TestApex/RollbackTestApexV2.json new file mode 100644 index 000000000000..9de3f4590877 --- /dev/null +++ b/tests/RollbackTest/TestApex/RollbackTestApexV2.json @@ -0,0 +1,4 @@ +{ + "name": "com.android.tests.rollback.testapex", + "version": 2 +} diff --git a/tests/RollbackTest/TestApex/com.android.tests.rollback.testapex.avbpubkey b/tests/RollbackTest/TestApex/com.android.tests.rollback.testapex.avbpubkey Binary files differnew file mode 100644 index 000000000000..b347331bc539 --- /dev/null +++ b/tests/RollbackTest/TestApex/com.android.tests.rollback.testapex.avbpubkey diff --git a/tests/RollbackTest/TestApex/com.android.tests.rollback.testapex.pem b/tests/RollbackTest/TestApex/com.android.tests.rollback.testapex.pem new file mode 100644 index 000000000000..7181ce5c2a73 --- /dev/null +++ b/tests/RollbackTest/TestApex/com.android.tests.rollback.testapex.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIJKQIBAAKCAgEArBLX+v6RMiK6soQFgbc0RZ+wticTD5sCbu9Q5B5WT7UnV1Wt +cNI/n2bjks3UYNxTneOzMQOVjd4ln0BYZoNvoDtrc1BsYABpt4FywSq1zz/3sp3L +3Vp8vaUyRsx0Da+PcOdYHPFs1WPX3Shw3MSPhUO/72KTm6GhO/cHEPEzSQLaw/zO +8FiPpDdRqILqVJlu1DZ+i1DO+To9mKt59uLlxy3F+HAMnQaNW6+2NCV/vdCxQrEL +m7PpsM6DymsFKcvVra1Il4apKkYNgcgTQ+AlmyPxGo6twfZvRMrU3bcrUmrg3OAx +tMD7MufXlKkoFMlT5clrQ7P2ErKoFxhTVWhBL5ZJv/lKwaTfxCLyNvYQ7JALKtOL +9xx4c77NdyYVjxMDAJ7E+Uj3QjdOv3FxM38Fcvt0zN4SSlmDjEDtm2e2w53Ztgz1 +zHAuNllIW1zGdPqa+ROoa9JLJePlyNXG9Rn1sSkjM9WwteaJ3nrdje6uBsvo7I9g +1MWab2XNXQwJNHACICtyU7QPryGP4U/lyHqWEkdoXrs8O36gmTGU/EsJYSIVILax +0HZC3zkmxHgiN95ZvD+Y5Y9HDXAN04kT8O1V/QD4QUCP8MGXNrkQP/JZAnu5Y2D1 +Wrn/7Tft2ON3OpH5vUgR99bKR549LcRrYgxeTnci3xkOsnTjZRxEUJFQgVUCAwEA +AQKCAgEAlBzIMbDLk+cW4rAG+WeTo9ZXygKKQqV/i7OM4j5GtudMTL1fxDwFLZTn +kCaBhzo+8ynaxPa71ViA87n0HZFHFRnwXFq+XkgctyrCdwjnY9RAxktS/l5z/t1i +EFTOFDWod1t6mbcpoegGF8RGmZDLpL7zR/+G5LBUU4RHXcrwBQli+s3x5imkwoon +TLAbOeSz5BBgDlTpQtdhy7bWDa+ybya0QCtagBLyvBfb8rjQYrduzOQOBODw5xJr +mGFUGWztqUf9swfjNpMD71EjXApk9EwUrXJgmBMiJYmdfpa6wH9kvFpyDo8J6gBr +rPeJm5LMF3+vR6Bw0Bld3DtBB8PsTrockOdeJNHfnY54480e7AahfM5gkeuIYC4P +E3CIbyAgpS8+OLpsXP207AOjFz1TLiOqyDUDsBqSMNEQ7QbIPTQw8UZ/o0vEoqEv +RrJAvIAv0KUcFNUL0t6VX2OXbV+i+T85wPey3XqK9P8Zzc8NUEGJ2paDUkSuemKC +nF9/siGaoBHXsNUkQuV9xYo4co66MHDGAx/OdUM5lPiUcrF/bR+ZUA3p1BLUQXbQ +aJD9uKlvJ4ZRSwhR/fQc5UiVWl3l76k+0Ia/Ddd1ArphVhbvGPOsm3W7wJ6KukSk +rBJh1PPV1HnA+icS2Wvu2kVdz9+39cQUYJIICO1uwWfPIb/R5MUCggEBAOSwkwXs +jqewNc6X4mc19RGKIMUvOrRrQkOOY33pI+BjoqI8cl+NOHNB7F8/gVnHIfhZpOG6 +WD5St1/qlSPT1Nte+38P+woAp5mdclZyRgYb4MjZybZihUPIPVgUbxiZTDHy1Sw0 +kRgmvaV3ccvG9/9bBUKNr9Z+XySsIhq5DzY+yIZ33vhLB/0g05x2HVIsSlsRWzri +lw0iX/v07bdIRUIv7QB8xcLY/23pIbHAXS8DaknN8YxpKtjilOnUWVp/dx4SIcJY +6PtSBfPcwVJ2MA/xXWBvDP82/XxvbxKnjj8lvkbfqMC6jrXJBw3NT/he8b932IBb +PwLGU4hoKvjXfUcCggEBAMCfa4N1KUXW0Mo3BcDk2Hk8zVYuTPDNTWg1Mxb46sn+ +HNVJKU0OrejM2hNNCwzJG7eVQBorJj5XefhaQFTvWoLKbf1YZuWKaQrRwKkHEqDi +edplA2RkpUebeS4KYIr22rzu3ZrZqiJmRU5kAS9hwOzwFvnXUgGy7IoZKXhpo0HK +xvwylb/C5FXh97tecDdH/5evB/DC515TIhPzUQ5tpT0oAl9MU7Pe1E/opzrD/0sR +dqKVJcl9vBRbtcAIkCOVpLoA9T2VvisafZAJaRXc3ACFMceewgpVAtBCsWUtLfvW +fHMHWfti5je19SWg7bUK56uHJ0vdAI+irkfpe1t0aoMCggEAR5cPL3eSYOREs9vQ +QEcf5NG82H2kfv5kzAkzFCN7267VJryNgWQQG+SzPk3/DD/OXpSRjShsn3X9ecVR +0tlpdRMS3//8snDqBqjHNlCnoxnvEHE9OB83YLS6n2wmKykyNSCzoxcBpPHbxITT +1tr+n626w87fEOKWnkBUnND59h1JYO79mfTDF3bDR+Oh4iuDS2bvjEuKxc3RBmry +T8IMDGA8bT6iGhEcRSgKKD7z7NfA2kHiL/ZsN2EXBOw43J+yhnNephx3MtXGj0S4 +MDxXZ2ZDuQCKrQpl6CJqPwi8+v+xxTYW+d5s9nNsBeIT+sieHTZDTEtEOnYjiDwz +15p92QKCAQEAkMwGNQawpOhLgYcFEzC0LcbwEFWzztx10N0U77LkRD16jTZ3Do73 +WmYLlLC4mr7e0A0o58MB96EodfHaJD7dSi5Dqkt25hw6xEBS1H0Vms1EjlCa0S/7 +Mq4D1QFF+5B/c8EX4ty20S8R8FCqt2SDc1kz3FHpOo+20kUB8Jtwdveox1J7UXB+ +1rSL1lSyhEviLbMMhAbvh+90UYz5pJ/1s9hMmDi3PyJFdWBNvZYyZcrV5He7tRCI +fsFGCfol6CoIby5jLA1Rq/M46jq4vQ+ObfGyLv3/nWa0O7u2wHjK9WIRoSKomJmK +t9xXURb9Obfd2Qo7FwMl9dNzsWkpKuGDYwKCAQB+hiWu2C+0foJ4Z8auM1SK8be4 +waplfD7qIvONE/vtl+VAN+eVpz5kkQJfXiafktAHahgEdSx43bofR0Kv0/R7IRs3 +M1WYAr0w+19TXLXkuh2oYIbcoGrIN3HMmlKMv44xh/QUhRe337cCLejP0SESNN+k +5wT91SbJwuCw/QsG3sD6NIMtCNWdcsAQq/ruhz7pQ/JZUFOueV0tkzbK+oNHWbNU +lS99qjPaVCXZFlz/t2/89cljh9mtRjcfXIBfuTijN9sXNcLTXsfLsyHJ86eEbI2U +o2JQ7Sjs10LpiwBbNNHBmulARgRONNMgik6tpNIS0tk9eke0lCX42bDFtAD4 +-----END RSA PRIVATE KEY----- diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java index ace0e6d14c58..4b277ae850c5 100644 --- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java +++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java @@ -37,7 +37,6 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -629,12 +628,9 @@ public class RollbackTest { assertEquals(versionRolledBackTo, info.getVersionRolledBackTo().getLongVersionCode()); } - // TODO: Allow installing test app along atomically with module metadata package so that - // a failed test app will be flagged as a failed mainline app /** * Test bad update automatic rollback. */ - @Ignore @Test public void testBadUpdateRollback() throws Exception { BroadcastReceiver crashCountReceiver = null; diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index c50e6a778cda..c5b9cf137dd8 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -1099,9 +1099,13 @@ public class WifiConfiguration implements Parcelable { */ public static final int DISABLED_BY_WRONG_PASSWORD = 13; /** + * This network is disabled because service is not subscribed + */ + public static final int DISABLED_AUTHENTICATION_NO_SUBSCRIPTION = 14; + /** * This Maximum disable reason value */ - public static final int NETWORK_SELECTION_DISABLED_MAX = 14; + public static final int NETWORK_SELECTION_DISABLED_MAX = 15; /** * Quality network selection disable reason String (for debug purpose) @@ -1120,7 +1124,8 @@ public class WifiConfiguration implements Parcelable { "NETWORK_SELECTION_DISABLED_NO_INTERNET_PERMANENT", "NETWORK_SELECTION_DISABLED_BY_WIFI_MANAGER", "NETWORK_SELECTION_DISABLED_BY_USER_SWITCH", - "NETWORK_SELECTION_DISABLED_BY_WRONG_PASSWORD" + "NETWORK_SELECTION_DISABLED_BY_WRONG_PASSWORD", + "NETWORK_SELECTION_DISABLED_AUTHENTICATION_NO_SUBSCRIPTION" }; /** |