Merge "Add TestApi annotations to LocationManager"
diff --git a/Android.bp b/Android.bp
index ecdc082..8d25704 100644
--- a/Android.bp
+++ b/Android.bp
@@ -746,6 +746,7 @@
"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 417ae80..131237a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -16935,6 +16935,7 @@
field public static final int REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING = 4; // 0x4
field public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3; // 0x3
field public static final int REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS = 5; // 0x5
+ field public static final int REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA = 13; // 0xd
field public static final int REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING = 7; // 0x7
field public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; // 0x0
field public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; // 0x1
@@ -24051,7 +24052,8 @@
}
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 +24065,7 @@
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 +24143,10 @@
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 +24991,7 @@
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 +44137,7 @@
}
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 +45099,7 @@
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 +45135,7 @@
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 +45668,9 @@
}
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 +45728,10 @@
}
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 +50670,7 @@
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);
@@ -50771,6 +50778,7 @@
method public void setTouchDelegate(android.view.TouchDelegate);
method public void setTransitionAlpha(float);
method public final void setTransitionName(String);
+ method public void setTransitionVisibility(int);
method public void setTranslationX(float);
method public void setTranslationY(float);
method public void setTranslationZ(float);
@@ -53405,6 +53413,11 @@
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 +53428,10 @@
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);
}
@@ -54845,6 +54862,7 @@
method public int getCheckedItemPosition();
method public android.util.SparseBooleanArray getCheckedItemPositions();
method public int getChoiceMode();
+ method public boolean getDrawSelectorOnTop();
method public int getListPaddingBottom();
method public int getListPaddingLeft();
method public int getListPaddingRight();
@@ -56245,6 +56263,7 @@
method public android.view.Menu getMenu();
method public android.view.MenuInflater getMenuInflater();
method public void inflate(@MenuRes int);
+ method public void setForceShowIcon(boolean);
method public void setGravity(int);
method public void setOnDismissListener(android.widget.PopupMenu.OnDismissListener);
method public void setOnMenuItemClickListener(android.widget.PopupMenu.OnMenuItemClickListener);
@@ -56458,6 +56477,7 @@
ctor public RelativeLayout(android.content.Context, android.util.AttributeSet, int, int);
method public android.widget.RelativeLayout.LayoutParams generateLayoutParams(android.util.AttributeSet);
method public int getGravity();
+ method public int getIgnoreGravity();
method public void setGravity(int);
method public void setHorizontalGravity(int);
method public void setIgnoreGravity(int);
@@ -56674,8 +56694,9 @@
method public CharSequence getQuery();
method @Nullable public CharSequence getQueryHint();
method public android.widget.CursorAdapter getSuggestionsAdapter();
- method public boolean isIconfiedByDefault();
+ method @Deprecated public boolean isIconfiedByDefault();
method public boolean isIconified();
+ method public boolean isIconifiedByDefault();
method public boolean isQueryRefinementEnabled();
method public boolean isSubmitButtonEnabled();
method public void onActionViewCollapsed();
@@ -57398,6 +57419,7 @@
ctor public ToggleButton(android.content.Context, android.util.AttributeSet, int);
ctor public ToggleButton(android.content.Context, android.util.AttributeSet);
ctor public ToggleButton(android.content.Context);
+ method public float getDisabledAlpha();
method public CharSequence getTextOff();
method public CharSequence getTextOn();
method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
@@ -57558,6 +57580,7 @@
@android.widget.RemoteViews.RemoteView public class ViewFlipper extends android.widget.ViewAnimator {
ctor public ViewFlipper(android.content.Context);
ctor public ViewFlipper(android.content.Context, android.util.AttributeSet);
+ method public int getFlipInterval();
method public boolean isAutoStart();
method public boolean isFlipping();
method public void setAutoStart(boolean);
diff --git a/api/system-current.txt b/api/system-current.txt
index f9403ff..c3088c0 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3317,11 +3317,11 @@
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public int getGnssBatchSize();
method public int getGnssCapabilities();
method @Nullable public String getLocationControllerExtraPackage();
- method @Nullable public String getNetworkProviderPackage();
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void injectGnssMeasurementCorrections(@NonNull android.location.GnssMeasurementCorrections);
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);
@@ -5765,10 +5765,19 @@
field public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled";
}
+ public static interface DeviceConfig.Runtime {
+ field public static final String NAMESPACE = "runtime";
+ field public static final String USE_PRECOMPILED_LAYOUT = "view.precompiled_layout_enabled";
+ }
+
public static interface DeviceConfig.RuntimeNative {
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 +5971,6 @@
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";
@@ -6026,6 +6034,7 @@
method public abstract void onClearRoleHolders(@NonNull String, @NonNull android.app.role.RoleManagerCallback);
method public abstract void onGrantDefaultRoles(@NonNull android.app.role.RoleManagerCallback);
method public abstract void onRemoveRoleHolder(@NonNull String, @NonNull String, @NonNull android.app.role.RoleManagerCallback);
+ method public abstract void onSmsKillSwitchToggled(boolean);
field public static final String SERVICE_INTERFACE = "android.rolecontrollerservice.RoleControllerService";
}
@@ -6304,8 +6313,6 @@
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 +6321,7 @@
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";
}
@@ -7849,6 +7854,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean switchSlots(int[]);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void toggleRadioOnOff();
method public void updateServiceLocation();
+ field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final String ACTION_DEBUG_EVENT = "android.telephony.action.DEBUG_EVENT";
field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
@@ -7856,6 +7862,8 @@
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
+ field public static final String EXTRA_DEBUG_EVENT_DESCRIPTION = "android.telephony.extra.DEBUG_EVENT_DESCRIPTION";
+ field public static final String EXTRA_DEBUG_EVENT_ID = "android.telephony.extra.DEBUG_EVENT_ID";
field public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
field public static final String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
field public static final String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
@@ -9264,11 +9272,7 @@
package android.view.autofill {
public final class AutofillManager {
- method @NonNull public java.util.Set<android.content.ComponentName> getAugmentedAutofillDisabledActivities();
- method @NonNull public java.util.Set<java.lang.String> getAugmentedAutofillDisabledPackages();
- method public void setActivityAugmentedAutofillEnabled(@NonNull android.content.ComponentName, boolean);
method public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
- method public void setPackageAugmentedAutofillEnabled(@NonNull String, boolean);
}
}
@@ -9298,6 +9302,8 @@
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 4210d17..49c4e68 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -322,6 +322,14 @@
}
+package android.app.assist {
+
+ public static class AssistStructure.ViewNode {
+ ctor public AssistStructure.ViewNode();
+ }
+
+}
+
package android.app.backup {
public class BackupManager {
@@ -1788,6 +1796,7 @@
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";
}
@@ -2069,6 +2078,40 @@
}
+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 {
@@ -2149,7 +2192,6 @@
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
@@ -2624,17 +2666,56 @@
}
public final class AutofillManager {
- method @NonNull public java.util.Set<android.content.ComponentName> getAugmentedAutofillDisabledActivities();
- method @NonNull public java.util.Set<java.lang.String> getAugmentedAutofillDisabledPackages();
- method public void setActivityAugmentedAutofillEnabled(@NonNull android.content.ComponentName, boolean);
method public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
- method public void setPackageAugmentedAutofillEnabled(@NonNull String, boolean);
field public static final int FLAG_SMART_SUGGESTION_SYSTEM = 1; // 0x1
field public static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 120000; // 0x1d4c0
}
}
+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 {
@@ -2691,7 +2772,7 @@
}
public class DatePicker extends android.widget.FrameLayout {
- method public int getMode();
+ method @android.view.inspector.InspectableProperty(name="datePickerMode", enumMapping={@android.view.inspector.InspectableProperty.EnumMap(value=android.widget.DatePicker.MODE_SPINNER, name="spinner"), @android.view.inspector.InspectableProperty.EnumMap(value=android.widget.DatePicker.MODE_CALENDAR, name="calendar")}) public int getMode();
field public static final int MODE_CALENDAR = 2; // 0x2
field public static final int MODE_SPINNER = 1; // 0x1
}
@@ -2727,7 +2808,7 @@
method public android.view.View getAmView();
method public android.view.View getHourView();
method public android.view.View getMinuteView();
- method public int getMode();
+ method @android.view.inspector.InspectableProperty(name="timePickerMode", enumMapping={@android.view.inspector.InspectableProperty.EnumMap(name="clock", value=android.widget.TimePicker.MODE_CLOCK), @android.view.inspector.InspectableProperty.EnumMap(name="spinner", value=android.widget.TimePicker.MODE_SPINNER)}) public int getMode();
method public android.view.View getPmView();
field public static final int MODE_CLOCK = 2; // 0x2
field public static final int MODE_SPINNER = 1; // 0x1
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 5f47e06..63f9b59 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -2144,30 +2144,24 @@
}
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 eaba9be..40a4070 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -559,7 +559,7 @@
// 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 @@
// 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 @@
}
// 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 3a5be43..eec3c73 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -155,7 +155,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index a748959..11bb38b 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -489,7 +489,6 @@
Landroid/location/ILocationManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/ILocationManager;
Landroid/location/ILocationManager$Stub;->TRANSACTION_getAllProviders:I
Landroid/location/ILocationManager;->getAllProviders()Ljava/util/List;
-Landroid/location/ILocationManager;->getNetworkProviderPackage()Ljava/lang/String;
Landroid/location/INetInitiatedListener$Stub;-><init>()V
Landroid/location/INetInitiatedListener;->sendNiResponse(II)Z
Landroid/location/LocationManager$ListenerTransport;-><init>(Landroid/location/LocationManager;Landroid/location/LocationListener;Landroid/os/Looper;)V
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
index 5b3813d..c753710 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 @@
*
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public void cancel() {
if (currentChangingAnimations.size() > 0) {
LinkedHashMap<View, Animator> currentAnimCopy =
@@ -1107,7 +1108,7 @@
*
* @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 aac8f08..0eadd1d 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -7155,7 +7155,8 @@
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 9ee2f03..ea145f0 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -4485,6 +4485,7 @@
* @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 @@
*/
@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 @@
* @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 @@
* @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 @@
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 @@
// 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 6006ad2..2b1e7cd 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -3,6 +3,7 @@
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 @@
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 8cbe7a3..fafea34 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -52,6 +52,14 @@
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 e2907e2..6e52b33 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -866,6 +866,14 @@
*/
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 4a2dbe7..cfe35b0 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -137,6 +137,32 @@
}
/**
+ * 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.
*/
@@ -843,4 +869,21 @@
* {@code token} can be completed.
*/
public abstract void finishPackageInstall(int token, boolean didLaunch);
+
+ /**
+ * Remove the default browser stored in the legacy package settings.
+ *
+ * @param userId the user id
+ *
+ * @return the package name of the default browser, or {@code null} if none
+ */
+ @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 104661f..d951756 100644
--- a/core/java/android/content/rollback/IRollbackManager.aidl
+++ b/core/java/android/content/rollback/IRollbackManager.aidl
@@ -40,4 +40,11 @@
// 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 d4ed35a..0ec4018 100644
--- a/core/java/android/content/rollback/PackageRollbackInfo.java
+++ b/core/java/android/content/rollback/PackageRollbackInfo.java
@@ -69,6 +69,11 @@
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 @@
}
/** @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 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 b2288fc..87568e8 100644
--- a/core/java/android/ddm/DdmHandleHello.java
+++ b/core/java/android/ddm/DdmHandleHello.java
@@ -16,13 +16,15 @@
package android.ddm;
+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 android.util.Log;
-import android.os.Debug;
-import android.os.UserHandle;
-import dalvik.system.VMRuntime;
import java.nio.ByteBuffer;
@@ -35,6 +37,8 @@
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 @@
+ 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/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 4bc2702..1881a0cd 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1901,6 +1901,7 @@
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA LOGICAL_MULTI_CAMERA}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME MONOCHROME}</li>
+ * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA SECURE_IMAGE_DATA}</li>
* </ul></p>
* <p>This key is available on all devices.</p>
*
@@ -1918,6 +1919,7 @@
* @see #REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING
* @see #REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
* @see #REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME
+ * @see #REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA
*/
@PublicKey
public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES =
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 6302aa5..a3e561c 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -972,6 +972,14 @@
*/
public static final int REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME = 12;
+ /**
+ * <p>The camera device is capable of writing image data into a region of memory
+ * inaccessible to Android userspace or the Android kernel, and only accessible to
+ * trusted execution environments (TEE).</p>
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ */
+ public static final int REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA = 13;
+
//
// Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
//
diff --git a/core/java/android/hardware/hdmi/HdmiClient.java b/core/java/android/hardware/hdmi/HdmiClient.java
index 45a79e1..bff8c39 100644
--- a/core/java/android/hardware/hdmi/HdmiClient.java
+++ b/core/java/android/hardware/hdmi/HdmiClient.java
@@ -56,6 +56,26 @@
}
/**
+ * Sends a volume key event to the primary audio receiver in the system. This method should only
+ * be called when the volume key is not handled by the local device. HDMI framework handles the
+ * logic of finding the address of the receiver.
+ *
+ * @param keyCode key code to send. Defined in {@link android.view.KeyEvent}.
+ * @param isPressed true if this is key press event
+ *
+ * @hide
+ * TODO(b/110094868): unhide for Q
+ */
+ public void sendVolumeKeyEvent(int keyCode, boolean isPressed) {
+ try {
+ mService.sendVolumeKeyEvent(getDeviceType(), keyCode, isPressed);
+ } catch (RemoteException e) {
+ Log.e(TAG, "sendVolumeKeyEvent threw exception ", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Sends vendor-specific command.
*
* @param targetAddress address of the target device
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 1cd9920..95eaf75 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -47,6 +47,7 @@
void deviceSelect(int deviceId, IHdmiControlCallback callback);
void portSelect(int portId, IHdmiControlCallback callback);
void sendKeyEvent(int deviceType, int keyCode, boolean isPressed);
+ void sendVolumeKeyEvent(int deviceType, int keyCode, boolean isPressed);
List<HdmiPortInfo> getPortInfo();
boolean canChangeSystemAudioMode();
boolean getSystemAudioMode();
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index 843db6d..ffae361e 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -51,6 +51,7 @@
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 @@
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 @@
}
@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 333cfbd..ab630fd 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -592,7 +592,6 @@
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 @@
}
}
+ 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 @@
}
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 b4b541d..31c948a 100644
--- a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java
+++ b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java
@@ -290,6 +290,12 @@
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 141d33b..1f33693 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -19,6 +19,7 @@
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 @@
* 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 @@
* </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 @@
* 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 @@
/**
* 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 @@
/**
* <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 @@
/**
* 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 @@
/**
* <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 @@
* 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 3a5b8a8..27f7e22 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -24,7 +24,8 @@
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 @@
@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 @@
}
}
- 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 @@
}
@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 ddec688..68f9288 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -103,7 +103,7 @@
* 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 @@
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 @@
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 @@
// 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 @@
}
}
- 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 @@
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 @@
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 @@
}
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 @@
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 @@
* 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 @@
* - 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 @@
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 @@
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 @@
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 @@
* 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 @@
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 @@
// 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 @@
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 @@
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 @@
* 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 @@
// 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 @@
// 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 @@
}
return;
}
- boolean isOptIn =
+ final boolean isOptIn =
getGlobalSettingsString(coreSettings, Settings.Global.GAME_DRIVER_OPT_IN_APPS)
.contains(ai.packageName);
if (!isOptIn
@@ -563,13 +560,13 @@
// 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 @@
}
}
- 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 @@
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 @@
* 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 @@
}
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/os/Handler.java b/core/java/android/os/Handler.java
index e8704af..e6c12c7 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -469,6 +469,11 @@
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
+ /** @hide */
+ public final boolean postDelayed(Runnable r, int what, long delayMillis) {
+ return sendMessageDelayed(getPostMessage(r).setWhat(what), delayMillis);
+ }
+
/**
* Causes the Runnable r to be added to the message queue, to be run
* after the specified amount of time elapses.
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 0441ba2..22ba74d 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -566,7 +566,7 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef({ServiceType.GPS,
+ @IntDef({ServiceType.LOCATION,
ServiceType.VIBRATION,
ServiceType.ANIMATION,
ServiceType.FULL_BACKUP,
@@ -583,7 +583,7 @@
})
public @interface ServiceType {
int NULL = 0;
- int GPS = 1;
+ int LOCATION = 1;
int VIBRATION = 2;
int ANIMATION = 3;
int FULL_BACKUP = 4;
@@ -1400,11 +1400,11 @@
*/
@LocationPowerSaveMode
public int getLocationPowerSaveMode() {
- final PowerSaveState powerSaveState = getPowerSaveState(ServiceType.GPS);
+ final PowerSaveState powerSaveState = getPowerSaveState(ServiceType.LOCATION);
if (!powerSaveState.globalBatterySaverEnabled) {
return LOCATION_MODE_NO_CHANGE;
}
- return powerSaveState.gpsMode;
+ return powerSaveState.locationMode;
}
/**
diff --git a/core/java/android/os/PowerSaveState.java b/core/java/android/os/PowerSaveState.java
index 4918ad1..4a5e894 100644
--- a/core/java/android/os/PowerSaveState.java
+++ b/core/java/android/os/PowerSaveState.java
@@ -19,7 +19,7 @@
* Data class for battery saver state. It contains the data
* <p>
* 1. Whether battery saver mode is enabled
- * 2. Specific parameters to use in battery saver mode(i.e. screen brightness, gps mode)
+ * 2. Specific parameters to use in battery saver mode (i.e. screen brightness, location mode)
*
* @hide
*/
@@ -35,12 +35,12 @@
* {@link PowerManager#isPowerSaveMode()}
*/
public final boolean globalBatterySaverEnabled;
- public final int gpsMode;
+ public final int locationMode;
public final float brightnessFactor;
public PowerSaveState(Builder builder) {
batterySaverEnabled = builder.mBatterySaverEnabled;
- gpsMode = builder.mGpsMode;
+ locationMode = builder.mLocationMode;
brightnessFactor = builder.mBrightnessFactor;
globalBatterySaverEnabled = builder.mGlobalBatterySaverEnabled;
}
@@ -48,7 +48,7 @@
public PowerSaveState(Parcel in) {
batterySaverEnabled = in.readByte() != 0;
globalBatterySaverEnabled = in.readByte() != 0;
- gpsMode = in.readInt();
+ locationMode = in.readInt();
brightnessFactor = in.readFloat();
}
@@ -61,14 +61,14 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeByte((byte) (batterySaverEnabled ? 1 : 0));
dest.writeByte((byte) (globalBatterySaverEnabled ? 1 : 0));
- dest.writeInt(gpsMode);
+ dest.writeInt(locationMode);
dest.writeFloat(brightnessFactor);
}
public static final class Builder {
private boolean mBatterySaverEnabled = false;
private boolean mGlobalBatterySaverEnabled = false;
- private int mGpsMode = 0;
+ private int mLocationMode = 0;
private float mBrightnessFactor = 0.5f;
public Builder() {}
@@ -83,8 +83,8 @@
return this;
}
- public Builder setGpsMode(int mode) {
- mGpsMode = mode;
+ public Builder setLocationMode(int mode) {
+ mLocationMode = mode;
return this;
}
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 92650e1..1f58723 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -150,6 +150,21 @@
}
/**
+ * Namespace for all runtime related features.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface Runtime {
+ String NAMESPACE = "runtime";
+
+ /**
+ * Whether or not we use the precompiled layout.
+ */
+ String USE_PRECOMPILED_LAYOUT = "view.precompiled_layout_enabled";
+ }
+
+ /**
* Namespace for all runtime native related features.
*
* @hide
@@ -160,6 +175,17 @@
}
/**
+ * 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 0743c23..67c8400 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -110,6 +110,16 @@
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 e14bb66..3a02e85 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5704,7 +5704,6 @@
* 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/rolecontrollerservice/IRoleControllerService.aidl b/core/java/android/rolecontrollerservice/IRoleControllerService.aidl
index ac5be06..4e98201 100644
--- a/core/java/android/rolecontrollerservice/IRoleControllerService.aidl
+++ b/core/java/android/rolecontrollerservice/IRoleControllerService.aidl
@@ -32,4 +32,6 @@
void onClearRoleHolders(in String roleName, in IRoleManagerCallback callback);
void onGrantDefaultRoles(in IRoleManagerCallback callback);
+
+ void onSmsKillSwitchToggled(boolean smsRestrictionEnabled);
}
diff --git a/core/java/android/rolecontrollerservice/RoleControllerService.java b/core/java/android/rolecontrollerservice/RoleControllerService.java
index 6eda504..5403cfa 100644
--- a/core/java/android/rolecontrollerservice/RoleControllerService.java
+++ b/core/java/android/rolecontrollerservice/RoleControllerService.java
@@ -96,6 +96,11 @@
RoleControllerService.this.onGrantDefaultRoles(new RoleManagerCallbackDelegate(
callback));
}
+
+ @Override
+ public void onSmsKillSwitchToggled(boolean smsRestrictionEnabled) {
+ RoleControllerService.this.onSmsKillSwitchToggled(smsRestrictionEnabled);
+ }
};
}
@@ -141,6 +146,14 @@
@NonNull RoleManagerCallback callback);
/**
+ * Cleanup appop/permissions state in response to sms kill switch toggle
+ *
+ * @param smsRestrictionEnabled whether kill switch was turned on
+ */
+ //STOPSHIP: remove this api before shipping a final version
+ public abstract void onSmsKillSwitchToggled(boolean smsRestrictionEnabled);
+
+ /**
* Called by system to grant default permissions and roles.
* <p>
* This is typically when creating a new user or upgrading either system or
diff --git a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
index db242a2..ca6676d 100644
--- a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
+++ b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
@@ -17,6 +17,7 @@
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 @@
* @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 020de7f..c98f09e 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -21,6 +21,7 @@
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 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 @@
* @hide
*/
@SystemApi
+@TestApi
public abstract class ContentCaptureService extends Service {
private static final String TAG = ContentCaptureService.class.getSimpleName();
@@ -166,10 +166,6 @@
/**
* 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 @@
}
/**
- * 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 e84bd6f..2a729b6 100644
--- a/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl
+++ b/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl
@@ -28,8 +28,4 @@
*/
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 bc2116a..c3af1f0 100644
--- a/core/java/android/service/contentcapture/SnapshotData.java
+++ b/core/java/android/service/contentcapture/SnapshotData.java
@@ -19,6 +19,7 @@
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 @@
* @hide
*/
@SystemApi
+@TestApi
public final class SnapshotData implements Parcelable {
private final @NonNull Bundle mAssistData;
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 44353b1..81643e9 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -2182,6 +2182,13 @@
return Character.isWhitespace(codePoint) || codePoint == NBSP_CODE_POINT;
}
+ /** @hide */
+ @Nullable
+ public static String withoutPrefix(@Nullable String prefix, @Nullable String str) {
+ if (prefix == null || str == null) return str;
+ return str.startsWith(prefix) ? str.substring(prefix.length()) : str;
+ }
+
/**
* Remove html, remove bad characters, and truncate string.
*
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 7026d2b..2ba1e01 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -18,10 +18,10 @@
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 @@
return;
}
- if (setVisible) {
- mController.show(Type.IME);
- } else {
- mController.hide(Type.IME);
- }
+ mController.applyImeVisibility(setVisible);
}
@Override
@@ -91,6 +87,30 @@
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 625ddc2..583651d 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -180,9 +180,10 @@
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 8e77379..7ad97a6 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -29,11 +29,16 @@
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 @@
*/
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 @@
private ObjectAnimator mAnimator;
private @AnimationDirection int mAnimationDirection;
+ private int mPendingTypesToShow;
+
public InsetsController(ViewRootImpl viewRoot) {
mViewRoot = viewRoot;
mAnimCallback = () -> {
@@ -193,6 +202,12 @@
@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 @@
// 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 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
// 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 cccfd87..eab83ce 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 @@
*/
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 @@
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 9383e6c..9fb6bdb 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -29,10 +29,13 @@
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 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 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/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 6a290b7..c130250 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -33,6 +33,8 @@
import android.os.Message;
import android.os.SystemProperties;
import android.os.Trace;
+import android.provider.DeviceConfig;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
@@ -42,13 +44,14 @@
import com.android.internal.R;
import dalvik.system.PathClassLoader;
-import java.io.File;
-import java.lang.reflect.Method;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
import java.util.HashMap;
/**
@@ -78,8 +81,6 @@
private static final String TAG = LayoutInflater.class.getSimpleName();
private static final boolean DEBUG = false;
- private static final String USE_PRECOMPILED_LAYOUT_SYSTEM_PROPERTY
- = "view.precompiled_layout_enabled";
private static final String COMPILED_VIEW_DEX_FILE_NAME = "/compiled_view.dex";
/** Empty stack trace used to avoid log spam in re-throw exceptions. */
@@ -400,8 +401,19 @@
}
private void initPrecompiledViews() {
- initPrecompiledViews(
- SystemProperties.getBoolean(USE_PRECOMPILED_LAYOUT_SYSTEM_PROPERTY, false));
+ // Use the device config if enabled, otherwise default to the system property.
+ String usePrecompiledLayout = DeviceConfig.getProperty(
+ DeviceConfig.Runtime.NAMESPACE,
+ DeviceConfig.Runtime.USE_PRECOMPILED_LAYOUT);
+ boolean enabled = false;
+ if (TextUtils.isEmpty(usePrecompiledLayout)) {
+ enabled = SystemProperties.getBoolean(
+ DeviceConfig.Runtime.USE_PRECOMPILED_LAYOUT,
+ false);
+ } else {
+ enabled = Boolean.parseBoolean(usePrecompiledLayout);
+ }
+ initPrecompiledViews(enabled);
}
private void initPrecompiledViews(boolean enablePrecompiledViews) {
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index c189afe..dc11d3d 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -143,7 +143,7 @@
private long mCurrTime;
private long mPrevTime;
private boolean mInProgress;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768938)
private int mSpanSlop;
@UnsupportedAppUsage
private int mMinSpan;
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index ecbec65..cd5207c 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -492,7 +492,7 @@
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 992b996..33d42d7 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -82,6 +82,7 @@
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.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 @@
*/
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 @@
* 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 @@
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,17 +4156,24 @@
* 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;
/**
* The view flags hold various views states.
+ *
+ * Use {@link #setTransitionVisibility(int)} to change the visibility of this view without
+ * triggering updates.
* {@hide}
*/
@ViewDebug.ExportedProperty(formatToHexString = true)
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
int mViewFlags;
static class TransformationInfo {
@@ -5042,12 +5065,17 @@
* 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 +8261,7 @@
* </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 +8972,27 @@
* @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 +9033,6 @@
}
// 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 +9069,10 @@
* </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 +9085,27 @@
}
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 +9116,12 @@
}
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 +9135,7 @@
} 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 +9148,24 @@
}
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 +9189,8 @@
* {@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 +9202,18 @@
*/
@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 +9230,6 @@
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 +9402,62 @@
}
/**
+ * 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}.
@@ -12801,14 +12913,17 @@
}
/**
- * Change the visibility of the View without triggering any other changes. This is
- * important for transitions, where visibility changes should not adjust focus or
- * trigger a new layout. This is only used when the visibility has already been changed
- * and we need a transient value during an animation. When the animation completes,
- * the original visibility value is always restored.
+ * Changes the visibility of this View without triggering any other changes. This should only
+ * be used by animation frameworks, such as {@link android.transition.Transition}, where
+ * visibility changes should not adjust focus or trigger a new layout. Application developers
+ * should use {@link #setVisibility} instead to ensure that the hierarchy is correctly updated.
+ *
+ * <p>Only call this method when a temporary visibility must be applied during an
+ * animation and the original visibility value is guaranteed to be reset after the
+ * animation completes. Use {@link #setVisibility} in all other cases.</p>
*
* @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
- * @hide
+ * @see #setVisibility(int)
*/
public void setTransitionVisibility(@Visibility int visibility) {
mViewFlags = (mViewFlags & ~View.VISIBILITY_MASK) | visibility;
@@ -17457,6 +17572,10 @@
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
@@ -21614,6 +21733,7 @@
* @return The known solid color background for this view, or 0 if the color may vary
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
@ColorInt
public int getSolidColor() {
return 0;
@@ -27963,6 +28083,23 @@
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 +28117,31 @@
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 0986cfa..d2b40f7 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -60,6 +60,8 @@
import android.view.animation.LayoutAnimationController;
import android.view.animation.Transformation;
import android.view.autofill.Helper;
+import android.view.inspector.InspectableProperty;
+import android.view.inspector.InspectableProperty.EnumMap;
import com.android.internal.R;
@@ -773,6 +775,11 @@
@ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"),
@ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS")
})
+ @InspectableProperty(enumMapping = {
+ @EnumMap(value = FOCUS_BEFORE_DESCENDANTS, name = "beforeDescendants"),
+ @EnumMap(value = FOCUS_AFTER_DESCENDANTS, name = "afterDescendants"),
+ @EnumMap(value = FOCUS_BLOCK_DESCENDANTS, name = "blocksDescendants")
+ })
public int getDescendantFocusability() {
return mGroupFlags & FLAG_MASK_FOCUSABILITY;
}
@@ -1391,6 +1398,7 @@
* Check whether this ViewGroup should ignore focus requests for itself and its children.
*/
@ViewDebug.ExportedProperty(category = "focus")
+ @InspectableProperty
public boolean getTouchscreenBlocksFocus() {
return (mGroupFlags & FLAG_TOUCHSCREEN_BLOCKS_FOCUS) != 0;
}
@@ -3116,6 +3124,7 @@
* Returns true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
* @return true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
*/
+ @InspectableProperty(name = "splitMotionEvents")
public boolean isMotionEventSplittingEnabled() {
return (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS;
}
@@ -3132,6 +3141,7 @@
* {@link android.view.ViewOutlineProvider#BACKGROUND} was given to
* {@link #setOutlineProvider(ViewOutlineProvider)} and false otherwise.
*/
+ @InspectableProperty
public boolean isTransitionGroup() {
if ((mGroupFlags & FLAG_IS_TRANSITION_GROUP_SET) != 0) {
return ((mGroupFlags & FLAG_IS_TRANSITION_GROUP) != 0);
@@ -3603,7 +3613,7 @@
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 +3624,31 @@
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 +3675,34 @@
}
}
+ 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;
@@ -4390,6 +4446,7 @@
* false otherwise.
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
public boolean getClipChildren() {
return ((mGroupFlags & FLAG_CLIP_CHILDREN) != 0);
}
@@ -4447,6 +4504,7 @@
* @attr ref android.R.styleable#ViewGroup_clipToPadding
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
public boolean getClipToPadding() {
return hasBooleanFlag(FLAG_CLIP_TO_PADDING);
}
@@ -6294,6 +6352,7 @@
*
* @return the current animation controller
*/
+ @InspectableProperty
public LayoutAnimationController getLayoutAnimation() {
return mLayoutAnimationController;
}
@@ -6313,6 +6372,7 @@
* Caching behavior of children may be controlled through {@link View#setLayerType(int, Paint)}.
*/
@Deprecated
+ @InspectableProperty(name = "animationCache")
public boolean isAnimationCacheEnabled() {
return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
}
@@ -6350,6 +6410,7 @@
* Child views may no longer have their caching behavior disabled by parents.
*/
@Deprecated
+ @InspectableProperty(name = "alwaysDrawnWithCache")
public boolean isAlwaysDrawnWithCacheEnabled() {
return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE;
}
@@ -6493,6 +6554,12 @@
@ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"),
@ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES, to = "ALL")
})
+ @InspectableProperty(enumMapping = {
+ @EnumMap(value = PERSISTENT_NO_CACHE, name = "none"),
+ @EnumMap(value = PERSISTENT_ANIMATION_CACHE, name = "animation"),
+ @EnumMap(value = PERSISTENT_SCROLLING_CACHE, name = "scrolling"),
+ @EnumMap(value = PERSISTENT_ALL_CACHES, name = "all"),
+ })
public int getPersistentDrawingCache() {
return mPersistentDrawingCache;
}
@@ -6570,6 +6637,10 @@
*
* @see #setLayoutMode(int)
*/
+ @InspectableProperty(enumMapping = {
+ @EnumMap(value = LAYOUT_MODE_CLIP_BOUNDS, name = "clipBounds"),
+ @EnumMap(value = LAYOUT_MODE_OPTICAL_BOUNDS, name = "opticalBounds")
+ })
public int getLayoutMode() {
if (mLayoutMode == LAYOUT_MODE_UNDEFINED) {
int inheritedLayoutMode = (mParent instanceof ViewGroup) ?
@@ -7261,6 +7332,7 @@
* make a group appear to be focused when its child EditText or button
* is focused.
*/
+ @InspectableProperty
public boolean addStatesFromChildren() {
return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0;
}
@@ -8544,16 +8616,16 @@
/**
* 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 9a317db..47528a0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -103,7 +103,10 @@
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 @@
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 @@
* @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 @@
boolean mApplyInsetsRequested;
boolean mLayoutRequested;
boolean mFirst;
+ boolean mPerformContentCapture;
boolean mReportNextDraw;
boolean mFullRedrawNeeded;
boolean mNewSurfaceNeeded;
@@ -608,6 +613,7 @@
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 @@
}
}
+ 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 @@
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/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 64c34f61..1c96b87 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -79,7 +79,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
-import java.util.Set;
//TODO: use java.lang.ref.Cleaner once Android supports Java 9
import sun.misc.Cleaner;
@@ -1769,45 +1768,6 @@
}
/**
- * Defines whether augmented autofill should be triggered for activities with such
- * {@link android.content.ComponentName}.
- *
- * <p>Useful to blacklist a particular activity.
- *
- * <p><b>Note:</b> This method should only be called by the app providing the augmented autofill
- * service, and it's ignored if the caller isn't it.
- *
- * @hide
- */
- @SystemApi
- @TestApi
- //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
- //in the same package as the test, and that module is compiled with SDK=test_current
- public void setActivityAugmentedAutofillEnabled(@NonNull ComponentName activity,
- boolean enabled) {
- // TODO(b/123100824): implement
- }
-
- /**
- * Defines whether augmented autofill should be triggered for activities of the app with such
- * {@code packageName}.
- *
- * <p>Useful to blacklist any activity from a particular app.
- *
- * <p><b>Note:</b> This method should only be called by the app providing the augmented autofill
- * service, and it's ignored if the caller isn't it.
- *
- * @hide
- */
- @SystemApi
- @TestApi
- //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
- //in the same package as the test, and that module is compiled with SDK=test_current
- public void setPackageAugmentedAutofillEnabled(@NonNull String packageName, boolean enabled) {
- // TODO(b/123100824): implement
- }
-
- /**
* Explicitly limits augmented autofill to the given packages and activities.
*
* <p>When the whitelist is set, it overrides the values passed to
@@ -1838,38 +1798,6 @@
// TODO(b/123100824): implement
}
- /**
- * Gets the activities where augmented autofill was disabled by
- * {@link #setActivityAugmentedAutofillEnabled(ComponentName, boolean)}.
- *
- * <p><b>Note:</b> This method should only be called by the app providing the augmented autofill
- * service, and it's ignored if the caller isn't it.
- *
- * @hide
- */
- @SystemApi
- @TestApi
- @NonNull
- public Set<ComponentName> getAugmentedAutofillDisabledActivities() {
- return null; // TODO(b/123100824): implement
- }
-
- /**
- * Gets the apps where content capture was disabled by
- * {@link #setPackageAugmentedAutofillEnabled(String, boolean)}.
- *
- * <p><b>Note:</b> This method should only be called by the app providing the augmented autofill
- * service, and it's ignored if the caller isn't it.
- *
- * @hide
- */
- @SystemApi
- @TestApi
- @NonNull
- public Set<String> getAugmentedAutofillDisabledPackages() {
- return null; // TODO(b/123100824): implement
- }
-
private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
Rect anchorBounds, IAutofillWindowPresenter presenter) {
final View anchor = findView(id);
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
index 63c21f3..acb81e0 100644
--- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -93,6 +93,11 @@
}
@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 5814759..6a9759d 100644
--- a/core/java/android/view/contentcapture/ContentCaptureContext.java
+++ b/core/java/android/view/contentcapture/ContentCaptureContext.java
@@ -19,6 +19,7 @@
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 @@
* @hide
*/
@SystemApi
+ @TestApi
public static final int FLAG_DISABLED_BY_APP = 0x1;
/**
@@ -67,6 +69,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public static final int FLAG_DISABLED_BY_FLAG_SECURE = 0x2;
/** @hide */
@@ -136,6 +139,7 @@
* @hide
*/
@SystemApi
+ @TestApi
@Nullable
public Bundle getExtras() {
return mExtras;
@@ -147,6 +151,7 @@
* @hide
*/
@SystemApi
+ @TestApi
@Nullable
public Uri getUri() {
return mUri;
@@ -158,6 +163,7 @@
* @hide
*/
@SystemApi
+ @TestApi
@Nullable
public String getAction() {
return mAction;
@@ -169,6 +175,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public int getTaskId() {
return mTaskId;
}
@@ -179,6 +186,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public @Nullable ComponentName getActivityComponent() {
return mComponentName;
}
@@ -191,6 +199,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public @Nullable ContentCaptureSessionId getParentSessionId() {
return mParentSessionId == null ? null : new ContentCaptureSessionId(mParentSessionId);
}
@@ -207,6 +216,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public int getDisplayId() {
return mDisplayId;
}
@@ -220,7 +230,8 @@
* @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 a6d4472..dfac35d 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -21,6 +21,7 @@
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 @@
/** @hide */
@SystemApi
+@TestApi
public final class ContentCaptureEvent implements Parcelable {
private static final String TAG = ContentCaptureEvent.class.getSimpleName();
@@ -69,13 +71,33 @@
*/
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 @@
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 @@
* 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 @@
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 f31856c..2512b95 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -22,6 +22,7 @@
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 @@
* 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 {
@@ -120,6 +121,7 @@
}
/** @hide */
+ @UiThread
public void onActivityStarted(@NonNull IBinder applicationToken,
@NonNull ComponentName activityComponent, int flags) {
synchronized (mLock) {
@@ -129,6 +131,7 @@
}
/** @hide */
+ @UiThread
public void onActivityStopped() {
getMainContentCaptureSession().destroy();
}
@@ -140,6 +143,7 @@
*
* @hide
*/
+ @UiThread
public void flush(@FlushReason int reason) {
getMainContentCaptureSession().flush(reason);
}
@@ -217,6 +221,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public boolean isContentCaptureFeatureEnabled() {
if (mService == null) return false;
@@ -249,6 +254,7 @@
* @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 68a3e8a..e028961 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -204,6 +204,12 @@
return mId.hashCode();
}
+ /** @hide */
+ @NonNull
+ public String getId() {
+ return mId;
+ }
+
/**
* Creates a new {@link ContentCaptureSession}.
*
@@ -362,6 +368,9 @@
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 034c8fa..eb945b5 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;
@@ -24,10 +26,9 @@
import static android.view.contentcapture.ContentCaptureHelper.VERBOSE;
import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UiThread;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ParceledListSlice;
@@ -56,16 +57,14 @@
* <p>This session is created when the activity starts and finished when it stops; clients can use
* it to create children activities.
*
- * <p><b>NOTE: all methods in this class should return right away, or do the real work in a handler
- * thread. Hence, the only field that must be thread-safe is {@code mEnabled}, which is called at
- * the beginning of every method.
- *
* @hide
*/
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.
*/
@@ -164,46 +163,28 @@
*
* @hide
*/
- void start(@NonNull IBinder applicationToken, @NonNull ComponentName activityComponent,
+ @UiThread
+ void start(@NonNull IBinder token, @NonNull ComponentName component,
int flags) {
if (!isContentCaptureEnabled()) return;
if (VERBOSE) {
- Log.v(TAG, "start(): token=" + applicationToken + ", comp="
- + ComponentName.flattenToShortString(activityComponent));
+ Log.v(TAG, "start(): token=" + token + ", comp="
+ + ComponentName.flattenToShortString(component));
}
- mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleStartSession, this,
- applicationToken, activityComponent, flags));
- }
-
- @Override
- void flush(@FlushReason int reason) {
- mHandler.sendMessage(
- obtainMessage(MainContentCaptureSession::handleForceFlush, this, reason));
- }
-
- @Override
- void onDestroy() {
- mHandler.removeMessages(MSG_FLUSH);
- mHandler.sendMessage(
- obtainMessage(MainContentCaptureSession::handleDestroySession, this));
- }
-
- private void handleStartSession(@NonNull IBinder token, @NonNull ComponentName componentName,
- int flags) {
- if (handleHasStarted()) {
+ if (hasStarted()) {
// TODO(b/122959591): make sure this is expected (and when), or use Log.w
if (DEBUG) {
Log.d(TAG, "ignoring handleStartSession(" + token + "/"
- + ComponentName.flattenToShortString(componentName) + " while on state "
+ + ComponentName.flattenToShortString(component) + " while on state "
+ getStateAsString(mState));
}
return;
}
mState = STATE_WAITING_FOR_SERVER;
mApplicationToken = token;
- mComponentName = componentName;
+ mComponentName = component;
if (VERBOSE) {
Log.v(TAG, "handleStartSession(): token=" + token + ", act="
@@ -213,28 +194,36 @@
try {
if (mSystemServerInterface == null) return;
- mSystemServerInterface.startSession(mApplicationToken, componentName, mId, flags,
+ mSystemServerInterface.startSession(mApplicationToken, component, mId, flags,
new IResultReceiver.Stub() {
@Override
public void send(int resultCode, Bundle resultData) {
- IBinder binder = null;
+ final IBinder binder;
if (resultData != null) {
binder = resultData.getBinder(EXTRA_BINDER);
if (binder == null) {
Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result");
- handleResetSession(STATE_DISABLED | STATE_INTERNAL_ERROR);
+ mHandler.post(() -> resetSession(
+ STATE_DISABLED | STATE_INTERNAL_ERROR));
return;
}
+ } else {
+ binder = null;
}
- handleSessionStarted(resultCode, binder);
+ mHandler.post(() -> onSessionStarted(resultCode, binder));
}
});
} catch (RemoteException e) {
- Log.w(TAG, "Error starting session for " + componentName.flattenToShortString() + ": "
- + e);
+ Log.w(TAG, "Error starting session for " + component.flattenToShortString() + ": " + e);
}
}
+ @Override
+ void onDestroy() {
+ mHandler.removeMessages(MSG_FLUSH);
+ mHandler.post(() -> destroySession());
+ }
+
/**
* Callback from {@code system_server} after call to
* {@link IContentCaptureManager#startSession(IBinder, ComponentName, String, int,
@@ -243,7 +232,8 @@
* @param resultCode session state
* @param binder handle to {@code IContentCaptureDirectManager}
*/
- private void handleSessionStarted(int resultCode, @Nullable IBinder binder) {
+ @UiThread
+ private void onSessionStarted(int resultCode, @Nullable IBinder binder) {
if (binder != null) {
mDirectServiceInterface = IContentCaptureDirectManager.Stub.asInterface(binder);
mDirectServiceVulture = () -> {
@@ -258,7 +248,7 @@
}
if ((resultCode & STATE_DISABLED) != 0) {
- handleResetSession(resultCode);
+ resetSession(resultCode);
} else {
mState = resultCode;
mDisabled.set(false);
@@ -270,10 +260,16 @@
}
}
- private void handleSendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
+ @UiThread
+ private void sendEvent(@NonNull ContentCaptureEvent event) {
+ sendEvent(event, /* forceFlush= */ false);
+ }
+
+ @UiThread
+ private void sendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
final int eventType = event.getType();
if (VERBOSE) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event);
- if (!handleHasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED) {
+ if (!hasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED) {
// TODO(b/120494182): comment when this could happen (dialogs?)
Log.v(TAG, "handleSendEvent(" + getDebugState() + ", "
+ ContentCaptureEvent.getTypeAsString(eventType)
@@ -334,7 +330,7 @@
final boolean bufferEvent = numberEvents < MAX_BUFFER_SIZE;
if (bufferEvent && !forceFlush) {
- handleScheduleFlush(FLUSH_REASON_IDLE_TIMEOUT, /* checkExisting= */ true);
+ scheduleFlush(FLUSH_REASON_IDLE_TIMEOUT, /* checkExisting= */ true);
return;
}
@@ -348,7 +344,7 @@
Log.d(TAG, "Closing session for " + getDebugState()
+ " after " + numberEvents + " delayed events");
}
- handleResetSession(STATE_DISABLED | STATE_NO_RESPONSE);
+ resetSession(STATE_DISABLED | STATE_NO_RESPONSE);
// TODO(b/111276913): blacklist activity / use special flag to indicate that
// when it's launched again
return;
@@ -365,19 +361,21 @@
flushReason = FLUSH_REASON_FULL;
}
- handleForceFlush(flushReason);
+ flush(flushReason);
}
- private boolean handleHasStarted() {
+ @UiThread
+ private boolean hasStarted() {
return mState != UNKNOWN_STATE;
}
- private void handleScheduleFlush(@FlushReason int reason, boolean checkExisting) {
+ @UiThread
+ private void scheduleFlush(@FlushReason int reason, boolean checkExisting) {
if (VERBOSE) {
Log.v(TAG, "handleScheduleFlush(" + getDebugState(reason)
+ ", checkExisting=" + checkExisting);
}
- if (!handleHasStarted()) {
+ if (!hasStarted()) {
if (VERBOSE) Log.v(TAG, "handleScheduleFlush(): session not started yet");
return;
}
@@ -398,20 +396,22 @@
Log.v(TAG, "handleScheduleFlush(): scheduled to flush in "
+ FLUSHING_FREQUENCY_MS + "ms: " + TimeUtils.logTimeOfDay(mNextFlush));
}
- mHandler.sendMessageDelayed(
- obtainMessage(MainContentCaptureSession::handleFlushIfNeeded, this, reason)
- .setWhat(MSG_FLUSH), FLUSHING_FREQUENCY_MS);
+ // Post using a Runnable directly to trim a few μs from PooledLambda.obtainMessage()
+ mHandler.postDelayed(() -> flushIfNeeded(reason), MSG_FLUSH, FLUSHING_FREQUENCY_MS);
}
- private void handleFlushIfNeeded(@FlushReason int reason) {
+ @UiThread
+ private void flushIfNeeded(@FlushReason int reason) {
if (mEvents == null || mEvents.isEmpty()) {
if (VERBOSE) Log.v(TAG, "Nothing to flush");
return;
}
- handleForceFlush(reason);
+ flush(reason);
}
- private void handleForceFlush(@FlushReason int reason) {
+ @Override
+ @UiThread
+ void flush(@FlushReason int reason) {
if (mEvents == null) return;
if (mDisabled.get()) {
@@ -426,7 +426,7 @@
+ "client not ready: " + mEvents);
}
if (!mHandler.hasMessages(MSG_FLUSH)) {
- handleScheduleFlush(reason, /* checkExisting= */ false);
+ scheduleFlush(reason, /* checkExisting= */ false);
}
return;
}
@@ -443,7 +443,7 @@
mFlushHistory.log(logRecord);
mHandler.removeMessages(MSG_FLUSH);
- final ParceledListSlice<ContentCaptureEvent> events = handleClearEvents();
+ final ParceledListSlice<ContentCaptureEvent> events = clearEvents();
mDirectServiceInterface.sendEvents(events);
} catch (RemoteException e) {
Log.w(TAG, "Error sending " + numberEvents + " for " + getDebugState()
@@ -455,7 +455,8 @@
* Resets the buffer and return a {@link ParceledListSlice} with the previous events.
*/
@NonNull
- private ParceledListSlice<ContentCaptureEvent> handleClearEvents() {
+ @UiThread
+ private ParceledListSlice<ContentCaptureEvent> clearEvents() {
// NOTE: we must save a reference to the current mEvents and then set it to to null,
// otherwise clearing it would clear it in the receiving side if the service is also local.
final List<ContentCaptureEvent> events = mEvents == null
@@ -465,7 +466,8 @@
return new ParceledListSlice<>(events);
}
- private void handleDestroySession() {
+ @UiThread
+ private void destroySession() {
if (DEBUG) {
Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
+ (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
@@ -484,7 +486,8 @@
// TODO(b/122454205): once we support multiple sessions, we might need to move some of these
// clearings out.
- private void handleResetSession(int newState) {
+ @UiThread
+ private void resetSession(int newState) {
if (VERBOSE) {
Log.v(TAG, "handleResetSession(" + getActivityName() + "): from "
+ getStateAsString(mState) + " to " + getStateAsString(newState));
@@ -518,6 +521,11 @@
}
@Override
+ public void internalNotifyViewHierarchyEvent(boolean started) {
+ notifyInitialViewHierarchyEvent(mId, started);
+ }
+
+ @Override
boolean isContentCaptureEnabled() {
return super.isContentCaptureEnabled() && mManager.isContentCaptureEnabled();
}
@@ -532,37 +540,52 @@
// change should also get get rid of the "internalNotifyXXXX" methods above
void notifyChildSessionStarted(@NonNull String parentSessionId,
@NonNull String childSessionId, @NonNull ContentCaptureContext clientContext) {
- mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
- new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
- .setParentSessionId(parentSessionId)
- .setClientContext(clientContext),
- /* forceFlush= */ true));
+ sendEvent(new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
+ .setParentSessionId(parentSessionId).setClientContext(clientContext),
+ 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));
+ sendEvent(new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED)
+ .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));
+ sendEvent(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));
+ sendEvent(
+ 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);
+ }
+ sendEvent(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));
+ sendEvent(new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED).setAutofillId(id)
+ .setText(text));
+ }
+
+ void notifyInitialViewHierarchyEvent(@NonNull String sessionId, boolean started) {
+ if (started) {
+ sendEvent(new ContentCaptureEvent(sessionId, TYPE_INITIAL_VIEW_TREE_APPEARING));
+ } else {
+ sendEvent(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 0cabafa..eef841d 100644
--- a/core/java/android/view/contentcapture/ViewNode.java
+++ b/core/java/android/view/contentcapture/ViewNode.java
@@ -18,6 +18,7 @@
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 @@
// 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 7fee3ef..aee4b1f 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.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.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,17 +957,69 @@
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();
+ // We intentionally do not use UserHandle.getCallingUserId() here because for system
+ // services InputMethodManagerInternal.getInputMethodListAsUser() should be used
+ // instead.
+ return mService.getInputMethodList(UserHandle.myUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+ /**
+ * Returns the list of installed 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> getInputMethodListAsUser(@UserIdInt int userId) {
+ try {
+ return mService.getInputMethodList(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * 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 +1027,9 @@
/**
* 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 +1945,36 @@
}
/**
+ * 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 de15f33..eb81628 100644
--- a/core/java/android/view/inputmethod/InputMethodSession.java
+++ b/core/java/android/view/inputmethod/InputMethodSession.java
@@ -184,4 +184,11 @@
* 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 0000000..8faae1f
--- /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 0000000..c08f49c
--- /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/AbsListView.java b/core/java/android/widget/AbsListView.java
index 99895bd..4dd7d3a 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -82,6 +82,8 @@
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InputMethodManager;
+import android.view.inspector.InspectableProperty;
+import android.view.inspector.InspectableProperty.EnumMap;
import android.widget.RemoteViews.OnClickHandler;
import com.android.internal.R;
@@ -1221,6 +1223,12 @@
*
* @return The current choice mode
*/
+ @InspectableProperty(enumMapping = {
+ @EnumMap(value = CHOICE_MODE_NONE, name = "none"),
+ @EnumMap(value = CHOICE_MODE_SINGLE, name = "singleChoice"),
+ @EnumMap(value = CHOICE_MODE_MULTIPLE, name = "multipleChoice"),
+ @EnumMap(value = CHOICE_MODE_MULTIPLE_MODAL, name = "multipleChoiceModal")
+ })
public int getChoiceMode() {
return mChoiceMode;
}
@@ -1421,6 +1429,7 @@
* @return true if fast scroll is enabled, false otherwise
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty
public boolean isFastScrollEnabled() {
if (mFastScroll == null) {
return mFastScrollEnabled;
@@ -1485,6 +1494,7 @@
* @see #setSmoothScrollbarEnabled(boolean)
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty(name = "smoothScrollbar")
public boolean isSmoothScrollbarEnabled() {
return mSmoothScrollbarEnabled;
}
@@ -1620,6 +1630,7 @@
* @see View#setDrawingCacheEnabled(boolean)
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty(name = "scrollingCache")
public boolean isScrollingCacheEnabled() {
return mScrollingCacheEnabled;
}
@@ -1667,6 +1678,7 @@
* @see Filterable
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty
public boolean isTextFilterEnabled() {
return mTextFilterEnabled;
}
@@ -1697,6 +1709,7 @@
* @return true if the content is stacked from the bottom edge, false otherwise
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty
public boolean isStackFromBottom() {
return mStackFromBottom;
}
@@ -2813,6 +2826,18 @@
}
/**
+ * Returns whether the selection highlight drawable should be drawn on top of the item or
+ * behind it.
+ *
+ * @return true if selector is drawn on top, false otherwise
+ * @attr ref android.R.styleable#AbsListView_drawSelectorOnTop
+ */
+ @InspectableProperty
+ public boolean getDrawSelectorOnTop() {
+ return mDrawSelectorOnTop;
+ }
+
+ /**
* Set a Drawable that should be used to highlight the currently selected item.
*
* @param resID A Drawable resource to use as the selection highlight.
@@ -2845,6 +2870,7 @@
*
* @return the drawable used to display the selector
*/
+ @InspectableProperty(name = "listSelector")
public Drawable getSelector() {
return mSelector;
}
@@ -6272,6 +6298,11 @@
* @return {@link #TRANSCRIPT_MODE_DISABLED}, {@link #TRANSCRIPT_MODE_NORMAL} or
* {@link #TRANSCRIPT_MODE_ALWAYS_SCROLL}
*/
+ @InspectableProperty(enumMapping = {
+ @EnumMap(value = TRANSCRIPT_MODE_DISABLED, name = "disabled"),
+ @EnumMap(value = TRANSCRIPT_MODE_NORMAL, name = "normal"),
+ @EnumMap(value = TRANSCRIPT_MODE_ALWAYS_SCROLL, name = "alwaysScroll")
+ })
public int getTranscriptMode() {
return mTranscriptMode;
}
@@ -6309,6 +6340,7 @@
* @return The cache color hint
*/
@ViewDebug.ExportedProperty(category = "drawing")
+ @InspectableProperty
@ColorInt
public int getCacheColorHint() {
return mCacheColorHint;
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index a85c585..c8be1d6 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -34,6 +34,7 @@
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.inspector.InspectableProperty;
import com.android.internal.R;
@@ -252,6 +253,7 @@
* @attr ref android.R.styleable#SeekBar_thumbTint
* @see #setThumbTintList(ColorStateList)
*/
+ @InspectableProperty(name = "thumbTint")
@Nullable
public ColorStateList getThumbTintList() {
return mThumbTintList;
@@ -284,6 +286,7 @@
* @attr ref android.R.styleable#SeekBar_thumbTintMode
* @see #setThumbTintMode(PorterDuff.Mode)
*/
+ @InspectableProperty
@Nullable
public PorterDuff.Mode getThumbTintMode() {
return mThumbTintMode;
@@ -406,6 +409,7 @@
* @attr ref android.R.styleable#SeekBar_tickMarkTint
* @see #setTickMarkTintList(ColorStateList)
*/
+ @InspectableProperty(name = "tickMarkTint")
@Nullable
public ColorStateList getTickMarkTintList() {
return mTickMarkTintList;
@@ -438,6 +442,7 @@
* @attr ref android.R.styleable#SeekBar_tickMarkTintMode
* @see #setTickMarkTintMode(PorterDuff.Mode)
*/
+ @InspectableProperty
@Nullable
public PorterDuff.Mode getTickMarkTintMode() {
return mTickMarkTintMode;
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index 7d6564f..9bc055e 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -35,12 +35,12 @@
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
+import android.view.inspector.InspectableProperty;
import com.android.internal.R;
@@ -362,27 +362,33 @@
*
* @attr ref android.R.styleable#AutoCompleteTextView_completionHint
*/
+ @InspectableProperty
public CharSequence getCompletionHint() {
return mHintText;
}
/**
- * <p>Returns the current width for the auto-complete drop down list. This can
- * be a fixed width, or {@link ViewGroup.LayoutParams#MATCH_PARENT} to fill the screen, or
- * {@link ViewGroup.LayoutParams#WRAP_CONTENT} to fit the width of its anchor view.</p>
+ * Returns the current width for the auto-complete drop down list.
+ *
+ * This can be a fixed width, or {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}
+ * to fill the screen, or {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
+ * to fit the width of its anchor view.
*
* @return the width for the drop down list
*
* @attr ref android.R.styleable#AutoCompleteTextView_dropDownWidth
*/
+ @InspectableProperty
public int getDropDownWidth() {
return mPopup.getWidth();
}
/**
- * <p>Sets the current width for the auto-complete drop down list. This can
- * be a fixed width, or {@link ViewGroup.LayoutParams#MATCH_PARENT} to fill the screen, or
- * {@link ViewGroup.LayoutParams#WRAP_CONTENT} to fit the width of its anchor view.</p>
+ * Sets the current width for the auto-complete drop down list.
+ *
+ * This can be a fixed width, or {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}
+ * to fill the screen, or {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
+ * to fit the width of its anchor view.
*
* @param width the width to use
*
@@ -393,24 +399,27 @@
}
/**
- * <p>Returns the current height for the auto-complete drop down list. This can
- * be a fixed height, or {@link ViewGroup.LayoutParams#MATCH_PARENT} to fill
- * the screen, or {@link ViewGroup.LayoutParams#WRAP_CONTENT} to fit the height
- * of the drop down's content.</p>
+ * <p>Returns the current height for the auto-complete drop down list.
+ *
+ * This can be a fixed width, or {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}
+ * to fill the screen, or {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
+ * to fit the width of its anchor view.
*
* @return the height for the drop down list
*
* @attr ref android.R.styleable#AutoCompleteTextView_dropDownHeight
*/
+ @InspectableProperty
public int getDropDownHeight() {
return mPopup.getHeight();
}
/**
- * <p>Sets the current height for the auto-complete drop down list. This can
- * be a fixed height, or {@link ViewGroup.LayoutParams#MATCH_PARENT} to fill
- * the screen, or {@link ViewGroup.LayoutParams#WRAP_CONTENT} to fit the height
- * of the drop down's content.</p>
+ * Sets the current height for the auto-complete drop down list.
+ *
+ * This can be a fixed width, or {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}
+ * to fill the screen, or {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
+ * to fit the width of its anchor view.
*
* @param height the height to use
*
@@ -452,6 +461,7 @@
*
* @attr ref android.R.styleable#PopupWindow_popupBackground
*/
+ @InspectableProperty(name = "popupBackground")
public Drawable getDropDownBackground() {
return mPopup.getBackground();
}
@@ -496,6 +506,7 @@
*
* @attr ref android.R.styleable#ListPopupWindow_dropDownVerticalOffset
*/
+ @InspectableProperty
public int getDropDownVerticalOffset() {
return mPopup.getVerticalOffset();
}
@@ -518,6 +529,7 @@
*
* @attr ref android.R.styleable#ListPopupWindow_dropDownHorizontalOffset
*/
+ @InspectableProperty
public int getDropDownHorizontalOffset() {
return mPopup.getHorizontalOffset();
}
@@ -610,6 +622,7 @@
*
* @attr ref android.R.styleable#AutoCompleteTextView_completionThreshold
*/
+ @InspectableProperty(name = "completionThreshold")
public int getThreshold() {
return mThreshold;
}
diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java
index 2ff815d..6c74c8c 100644
--- a/core/java/android/widget/CalendarView.java
+++ b/core/java/android/widget/CalendarView.java
@@ -34,6 +34,7 @@
import android.icu.util.TimeZone;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.inspector.InspectableProperty;
import com.android.internal.R;
@@ -149,6 +150,7 @@
* @attr ref android.R.styleable#CalendarView_shownWeekCount
* @deprecated No longer used by Material-style CalendarView.
*/
+ @InspectableProperty
@Deprecated
public int getShownWeekCount() {
return mDelegate.getShownWeekCount();
@@ -175,6 +177,7 @@
* @attr ref android.R.styleable#CalendarView_selectedWeekBackgroundColor
* @deprecated No longer used by Material-style CalendarView.
*/
+ @InspectableProperty
@ColorInt
@Deprecated
public int getSelectedWeekBackgroundColor() {
@@ -202,6 +205,7 @@
* @attr ref android.R.styleable#CalendarView_focusedMonthDateColor
* @deprecated No longer used by Material-style CalendarView.
*/
+ @InspectableProperty
@ColorInt
@Deprecated
public int getFocusedMonthDateColor() {
@@ -229,6 +233,7 @@
* @attr ref android.R.styleable#CalendarView_unfocusedMonthDateColor
* @deprecated No longer used by Material-style CalendarView.
*/
+ @InspectableProperty
@ColorInt
@Deprecated
public int getUnfocusedMonthDateColor() {
@@ -256,6 +261,7 @@
* @attr ref android.R.styleable#CalendarView_weekNumberColor
* @deprecated No longer used by Material-style CalendarView.
*/
+ @InspectableProperty
@ColorInt
@Deprecated
public int getWeekNumberColor() {
@@ -285,6 +291,7 @@
*/
@ColorInt
@Deprecated
+ @InspectableProperty
public int getWeekSeparatorLineColor() {
return mDelegate.getWeekSeparatorLineColor();
}
@@ -324,6 +331,7 @@
* @return The vertical bar drawable.
* @deprecated No longer used by Material-style CalendarView.
*/
+ @InspectableProperty
@Deprecated
public Drawable getSelectedDateVerticalBar() {
return mDelegate.getSelectedDateVerticalBar();
@@ -347,6 +355,7 @@
*
* @attr ref android.R.styleable#CalendarView_weekDayTextAppearance
*/
+ @InspectableProperty
public @StyleRes int getWeekDayTextAppearance() {
return mDelegate.getWeekDayTextAppearance();
}
@@ -369,6 +378,7 @@
*
* @attr ref android.R.styleable#CalendarView_dateTextAppearance
*/
+ @InspectableProperty
public @StyleRes int getDateTextAppearance() {
return mDelegate.getDateTextAppearance();
}
@@ -385,6 +395,7 @@
*
* @attr ref android.R.styleable#CalendarView_minDate
*/
+ @InspectableProperty
public long getMinDate() {
return mDelegate.getMinDate();
}
@@ -414,6 +425,7 @@
*
* @attr ref android.R.styleable#CalendarView_maxDate
*/
+ @InspectableProperty
public long getMaxDate() {
return mDelegate.getMaxDate();
}
@@ -452,6 +464,7 @@
*
* @attr ref android.R.styleable#CalendarView_showWeekNumber
*/
+ @InspectableProperty
@Deprecated
public boolean getShowWeekNumber() {
return mDelegate.getShowWeekNumber();
@@ -472,6 +485,7 @@
*
* @attr ref android.R.styleable#CalendarView_firstDayOfWeek
*/
+ @InspectableProperty
public int getFirstDayOfWeek() {
return mDelegate.getFirstDayOfWeek();
}
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index 1472b01..b7fdcbe 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -35,6 +35,7 @@
import android.view.ViewHierarchyEncoder;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.inspector.InspectableProperty;
import com.android.internal.R;
@@ -121,6 +122,7 @@
}
@ViewDebug.ExportedProperty
+ @InspectableProperty
public boolean isChecked() {
return mChecked;
}
@@ -237,6 +239,7 @@
* @attr ref android.R.styleable#CheckedTextView_checkMarkTint
* @see #setCheckMarkTintList(ColorStateList)
*/
+ @InspectableProperty(name = "checkMarkTint")
@Nullable
public ColorStateList getCheckMarkTintList() {
return mCheckMarkTintList;
@@ -269,6 +272,7 @@
* @attr ref android.R.styleable#CheckedTextView_checkMarkTintMode
* @see #setCheckMarkTintMode(PorterDuff.Mode)
*/
+ @InspectableProperty
@Nullable
public PorterDuff.Mode getCheckMarkTintMode() {
return mCheckMarkTintMode;
@@ -328,6 +332,7 @@
*
* @attr ref android.R.styleable#CheckedTextView_checkMark
*/
+ @InspectableProperty(name = "checkMark")
public Drawable getCheckMarkDrawable() {
return mCheckMarkDrawable;
}
diff --git a/core/java/android/widget/Chronometer.java b/core/java/android/widget/Chronometer.java
index d11c03a..66c35d9 100644
--- a/core/java/android/widget/Chronometer.java
+++ b/core/java/android/widget/Chronometer.java
@@ -29,6 +29,7 @@
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
+import android.view.inspector.InspectableProperty;
import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
@@ -145,6 +146,7 @@
*
* @see #setCountDown(boolean)
*/
+ @InspectableProperty
public boolean isCountDown() {
return mCountDown;
}
@@ -206,6 +208,7 @@
/**
* Returns the current format string as set through {@link #setFormat}.
*/
+ @InspectableProperty
public String getFormat() {
return mFormat;
}
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index d35bec8..a0f93da 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -39,6 +39,7 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
+import android.view.inspector.InspectableProperty;
import com.android.internal.R;
@@ -145,6 +146,7 @@
return handled;
}
+ @InspectableProperty
@ViewDebug.ExportedProperty
@Override
public boolean isChecked() {
@@ -282,6 +284,7 @@
* @see #setButtonDrawable(Drawable)
* @see #setButtonDrawable(int)
*/
+ @InspectableProperty(name = "button")
@Nullable
public Drawable getButtonDrawable() {
return mButtonDrawable;
@@ -314,6 +317,7 @@
* @attr ref android.R.styleable#CompoundButton_buttonTint
* @see #setButtonTintList(ColorStateList)
*/
+ @InspectableProperty(name = "buttonTint")
@Nullable
public ColorStateList getButtonTintList() {
return mButtonTintList;
@@ -342,6 +346,7 @@
* @attr ref android.R.styleable#CompoundButton_buttonTintMode
* @see #setButtonTintMode(PorterDuff.Mode)
*/
+ @InspectableProperty
@Nullable
public PorterDuff.Mode getButtonTintMode() {
return mButtonTintMode;
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index df53795..cca951c 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -37,6 +37,7 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
+import android.view.inspector.InspectableProperty;
import com.android.internal.R;
@@ -212,6 +213,10 @@
* @attr ref android.R.styleable#DatePicker_datePickerMode
* @hide Visible for testing only.
*/
+ @InspectableProperty(name = "datePickerMode", enumMapping = {
+ @InspectableProperty.EnumMap(value = MODE_SPINNER, name = "spinner"),
+ @InspectableProperty.EnumMap(value = MODE_CALENDAR, name = "calendar")
+ })
@DatePickerMode
@TestApi
public int getMode() {
@@ -257,6 +262,7 @@
/**
* @return The selected year.
*/
+ @InspectableProperty(hasAttributeId = false)
public int getYear() {
return mDelegate.getYear();
}
@@ -264,6 +270,7 @@
/**
* @return The selected month.
*/
+ @InspectableProperty(hasAttributeId = false)
public int getMonth() {
return mDelegate.getMonth();
}
@@ -271,6 +278,7 @@
/**
* @return The selected day of month.
*/
+ @InspectableProperty(hasAttributeId = false)
public int getDayOfMonth() {
return mDelegate.getDayOfMonth();
}
@@ -285,6 +293,7 @@
*
* @return The minimal supported date.
*/
+ @InspectableProperty
public long getMinDate() {
return mDelegate.getMinDate().getTimeInMillis();
}
@@ -310,6 +319,7 @@
*
* @return The maximal supported date.
*/
+ @InspectableProperty
public long getMaxDate() {
return mDelegate.getMaxDate().getTimeInMillis();
}
@@ -411,6 +421,7 @@
*
* @attr ref android.R.styleable#DatePicker_firstDayOfWeek
*/
+ @InspectableProperty
public int getFirstDayOfWeek() {
return mDelegate.getFirstDayOfWeek();
}
@@ -426,6 +437,7 @@
* @see #getCalendarView()
* @deprecated Not supported by Material-style {@code calendar} mode
*/
+ @InspectableProperty
@Deprecated
public boolean getCalendarViewShown() {
return mDelegate.getCalendarViewShown();
@@ -476,6 +488,7 @@
* @return {@code true} if the spinners are shown
* @deprecated Not supported by Material-style {@code calendar} mode
*/
+ @InspectableProperty
@Deprecated
public boolean getSpinnersShown() {
return mDelegate.getSpinnersShown();
diff --git a/core/java/android/widget/DateTimeView.java b/core/java/android/widget/DateTimeView.java
index bf2762a..0469dbd 100644
--- a/core/java/android/widget/DateTimeView.java
+++ b/core/java/android/widget/DateTimeView.java
@@ -35,6 +35,7 @@
import android.text.format.Time;
import android.util.AttributeSet;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.inspector.InspectableProperty;
import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
@@ -142,6 +143,16 @@
update();
}
+ /**
+ * Returns whether this view shows relative time
+ *
+ * @return True if it shows relative time, false otherwise
+ */
+ @InspectableProperty(name = "showReleative", hasAttributeId = false)
+ public boolean isShowRelativeTime() {
+ return mShowRelativeTime;
+ }
+
@Override
@android.view.RemotableViewMethod
public void setVisibility(@Visibility int visibility) {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index c821774..97c37b0 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -273,7 +273,11 @@
boolean mDiscardNextActionUp;
boolean mIgnoreActionUpEvent;
- @UnsupportedAppUsage
+ /**
+ * To set a custom cursor, you should use {@link TextView#setTextCursorDrawable(Drawable)}
+ * or {@link TextView#setTextCursorDrawable(int)}.
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private long mShowCursor;
private boolean mRenderCursorRegardlessTiming;
private Blink mBlink;
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index 865f520..5723b94 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -31,6 +31,7 @@
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.ViewHierarchyEncoder;
+import android.view.inspector.InspectableProperty;
import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
@@ -367,6 +368,7 @@
*
* @return Whether all children are considered when measuring.
*/
+ @InspectableProperty
public boolean getMeasureAllChildren() {
return mMeasureAllChildren;
}
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 13c086c..c8abf18 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -46,6 +46,7 @@
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
+import android.view.inspector.InspectableProperty;
import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
@@ -327,6 +328,10 @@
* @attr ref android.R.styleable#GridLayout_orientation
*/
@Orientation
+ @InspectableProperty(enumMapping = {
+ @InspectableProperty.EnumMap(value = HORIZONTAL, name = "horizontal"),
+ @InspectableProperty.EnumMap(value = VERTICAL, name = "vertical")
+ })
public int getOrientation() {
return mOrientation;
}
@@ -387,6 +392,7 @@
*
* @attr ref android.R.styleable#GridLayout_rowCount
*/
+ @InspectableProperty
public int getRowCount() {
return mVerticalAxis.getCount();
}
@@ -420,6 +426,7 @@
*
* @attr ref android.R.styleable#GridLayout_columnCount
*/
+ @InspectableProperty
public int getColumnCount() {
return mHorizontalAxis.getCount();
}
@@ -451,6 +458,7 @@
*
* @attr ref android.R.styleable#GridLayout_useDefaultMargins
*/
+ @InspectableProperty
public boolean getUseDefaultMargins() {
return mUseDefaultMargins;
}
@@ -499,6 +507,10 @@
* @attr ref android.R.styleable#GridLayout_alignmentMode
*/
@AlignmentMode
+ @InspectableProperty(enumMapping = {
+ @InspectableProperty.EnumMap(value = ALIGN_BOUNDS, name = "alignBounds"),
+ @InspectableProperty.EnumMap(value = ALIGN_MARGINS, name = "alignMargins"),
+ })
public int getAlignmentMode() {
return mAlignmentMode;
}
@@ -533,6 +545,7 @@
*
* @attr ref android.R.styleable#GridLayout_rowOrderPreserved
*/
+ @InspectableProperty
public boolean isRowOrderPreserved() {
return mVerticalAxis.isOrderPreserved();
}
@@ -569,6 +582,7 @@
*
* @attr ref android.R.styleable#GridLayout_columnOrderPreserved
*/
+ @InspectableProperty
public boolean isColumnOrderPreserved() {
return mHorizontalAxis.isOrderPreserved();
}
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index f781802..bf65ec0 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -42,6 +42,7 @@
import android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.animation.GridLayoutAnimationController;
+import android.view.inspector.InspectableProperty;
import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
@@ -2061,6 +2062,7 @@
*
* @attr ref android.R.styleable#GridView_gravity
*/
+ @InspectableProperty(valueType = InspectableProperty.ValueType.GRAVITY)
public int getGravity() {
return mGravity;
}
@@ -2096,6 +2098,7 @@
*
* @attr ref android.R.styleable#GridView_horizontalSpacing
*/
+ @InspectableProperty
public int getHorizontalSpacing() {
return mHorizontalSpacing;
}
@@ -2147,6 +2150,7 @@
*
* @attr ref android.R.styleable#GridView_verticalSpacing
*/
+ @InspectableProperty
public int getVerticalSpacing() {
return mVerticalSpacing;
}
@@ -2167,6 +2171,13 @@
}
@StretchMode
+ @InspectableProperty(enumMapping = {
+ @InspectableProperty.EnumMap(value = NO_STRETCH, name = "none"),
+ @InspectableProperty.EnumMap(value = STRETCH_SPACING, name = "spacingWidth"),
+ @InspectableProperty.EnumMap(
+ value = STRETCH_SPACING_UNIFORM, name = "spacingWidthUniform"),
+ @InspectableProperty.EnumMap(value = STRETCH_COLUMN_WIDTH, name = "columnWidth"),
+ })
public int getStretchMode() {
return mStretchMode;
}
@@ -2197,6 +2208,7 @@
*
* @attr ref android.R.styleable#GridView_columnWidth
*/
+ @InspectableProperty
public int getColumnWidth() {
return mColumnWidth;
}
@@ -2241,6 +2253,7 @@
* @see #setNumColumns(int)
*/
@ViewDebug.ExportedProperty
+ @InspectableProperty
public int getNumColumns() {
return mNumColumns;
}
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index bf9dffd..1c5f837 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -43,6 +43,7 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.AnimationUtils;
+import android.view.inspector.InspectableProperty;
import com.android.internal.R;
@@ -293,6 +294,7 @@
*
* @attr ref android.R.styleable#HorizontalScrollView_fillViewport
*/
+ @InspectableProperty
public boolean isFillViewport() {
return mFillViewport;
}
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index c21182c..e9c31db 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -51,6 +51,7 @@
import android.view.ViewDebug;
import android.view.ViewHierarchyEncoder;
import android.view.accessibility.AccessibilityEvent;
+import android.view.inspector.InspectableProperty;
import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
@@ -326,6 +327,7 @@
*
* @attr ref android.R.styleable#ImageView_adjustViewBounds
*/
+ @InspectableProperty
public boolean getAdjustViewBounds() {
return mAdjustViewBounds;
}
@@ -364,6 +366,7 @@
*
* @attr ref android.R.styleable#ImageView_maxWidth
*/
+ @InspectableProperty
public int getMaxWidth() {
return mMaxWidth;
}
@@ -402,6 +405,7 @@
*
* @attr ref android.R.styleable#ImageView_maxHeight
*/
+ @InspectableProperty
public int getMaxHeight() {
return mMaxHeight;
}
@@ -438,6 +442,7 @@
* @return the view's drawable, or null if no drawable has been
* assigned.
*/
+ @InspectableProperty(name = "src")
public Drawable getDrawable() {
if (mDrawable == mRecycleableBitmapDrawable) {
// Consider our cached version dirty since app code now has a reference to it
@@ -650,6 +655,7 @@
* @see #setImageTintList(ColorStateList)
*/
@Nullable
+ @InspectableProperty(name = "tint")
public ColorStateList getImageTintList() {
return mDrawableTintList;
}
@@ -679,6 +685,7 @@
* @see #setImageTintMode(PorterDuff.Mode)
*/
@Nullable
+ @InspectableProperty(name = "tintMode")
public PorterDuff.Mode getImageTintMode() {
return mDrawableTintMode;
}
@@ -844,6 +851,7 @@
* @see ImageView.ScaleType
* @attr ref android.R.styleable#ImageView_scaleType
*/
+ @InspectableProperty
public ScaleType getScaleType() {
return mScaleType;
}
@@ -893,6 +901,7 @@
*
* @attr ref android.R.styleable#ImageView_cropToPadding
*/
+ @InspectableProperty
public boolean getCropToPadding() {
return mCropToPadding;
}
@@ -1404,6 +1413,7 @@
* if baseline alignment is not supported.
*/
@Override
+ @InspectableProperty
@ViewDebug.ExportedProperty(category = "layout")
public int getBaseline() {
if (mBaselineAlignBottom) {
@@ -1451,6 +1461,7 @@
* @return True if the ImageView's baseline is considered the bottom of the view, false if otherwise.
* @see #setBaselineAlignBottom(boolean)
*/
+ @InspectableProperty
public boolean getBaselineAlignBottom() {
return mBaselineAlignBottom;
}
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 15910bb..64769b5 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -31,6 +31,7 @@
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.ViewHierarchyEncoder;
+import android.view.inspector.InspectableProperty;
import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
@@ -342,6 +343,7 @@
*
* @attr ref android.R.styleable#LinearLayout_divider
*/
+ @InspectableProperty(name = "divider")
public Drawable getDividerDrawable() {
return mDivider;
}
@@ -526,6 +528,7 @@
*
* @return true when widgets are baseline-aligned, false otherwise
*/
+ @InspectableProperty
public boolean isBaselineAligned() {
return mBaselineAligned;
}
@@ -554,6 +557,7 @@
*
* @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
*/
+ @InspectableProperty(name = "measureWithLargestChild")
public boolean isMeasureWithLargestChildEnabled() {
return mUseLargestChild;
}
@@ -633,6 +637,7 @@
* part of a larger layout that is baseline aligned, or -1 if none has
* been set.
*/
+ @InspectableProperty
public int getBaselineAlignedChildIndex() {
return mBaselineAlignedChildIndex;
}
@@ -686,6 +691,7 @@
* a number lower than or equals to 0.0f if not weight sum is
* to be used.
*/
+ @InspectableProperty
public float getWeightSum() {
return mWeightSum;
}
@@ -1841,6 +1847,10 @@
* @return either {@link #HORIZONTAL} or {@link #VERTICAL}
*/
@OrientationMode
+ @InspectableProperty(enumMapping = {
+ @InspectableProperty.EnumMap(value = HORIZONTAL, name = "horizontal"),
+ @InspectableProperty.EnumMap(value = VERTICAL, name = "vertical")
+ })
public int getOrientation() {
return mOrientation;
}
@@ -1877,6 +1887,7 @@
* @return the current gravity.
* @see #setGravity
*/
+ @InspectableProperty(valueType = InspectableProperty.ValueType.GRAVITY)
public int getGravity() {
return mGravity;
}
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 9b49786..311f896 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -49,6 +49,7 @@
import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo;
import android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo;
import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.inspector.InspectableProperty;
import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
@@ -3637,6 +3638,7 @@
* @return the current drawable drawn between list elements
* @attr ref R.styleable#ListView_divider
*/
+ @InspectableProperty
@Nullable
public Drawable getDivider() {
return mDivider;
@@ -3666,6 +3668,7 @@
/**
* @return Returns the height of the divider that will be drawn between each item in the list.
*/
+ @InspectableProperty
public int getDividerHeight() {
return mDividerHeight;
}
@@ -3701,6 +3704,7 @@
*
* @see #setHeaderDividersEnabled(boolean)
*/
+ @InspectableProperty(name = "headerDividersEnabled")
public boolean areHeaderDividersEnabled() {
return mHeaderDividersEnabled;
}
@@ -3724,6 +3728,7 @@
*
* @see #setFooterDividersEnabled(boolean)
*/
+ @InspectableProperty(name = "footerDividersEnabled")
public boolean areFooterDividersEnabled() {
return mFooterDividersEnabled;
}
diff --git a/core/java/android/widget/PopupMenu.java b/core/java/android/widget/PopupMenu.java
index d82e56c..b0c0c12 100644
--- a/core/java/android/widget/PopupMenu.java
+++ b/core/java/android/widget/PopupMenu.java
@@ -259,6 +259,19 @@
}
/**
+ * Sets whether the popup menu's adapter is forced to show icons in the
+ * menu item views.
+ * <p>
+ * Changes take effect on the next call to show().
+ *
+ * @param forceShowIcon {@code true} to force icons to be shown, or
+ * {@code false} for icons to be optionally shown
+ */
+ public void setForceShowIcon(boolean forceShowIcon) {
+ mPopup.setForceShowIcon(forceShowIcon);
+ }
+
+ /**
* Interface responsible for receiving menu item click events if the items
* themselves do not have individual item click listeners.
*/
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index ed6f0d6..705a371 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -273,7 +273,7 @@
private int mAnchorXoff;
private int mAnchorYoff;
private int mAnchoredGravity;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private boolean mOverlapAnchor;
private boolean mPopupViewInitialLayoutDirectionInherited;
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 1fc72f5..3519afd 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -58,6 +58,7 @@
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.view.animation.Transformation;
+import android.view.inspector.InspectableProperty;
import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
@@ -556,6 +557,7 @@
*
* @return true if the progress bar is in indeterminate mode
*/
+ @InspectableProperty
@ViewDebug.ExportedProperty(category = "progress")
public synchronized boolean isIndeterminate() {
return mIndeterminate;
@@ -610,6 +612,7 @@
* @see #setIndeterminateDrawable(android.graphics.drawable.Drawable)
* @see #setIndeterminate(boolean)
*/
+ @InspectableProperty
public Drawable getIndeterminateDrawable() {
return mIndeterminateDrawable;
}
@@ -677,6 +680,7 @@
* @attr ref android.R.styleable#ProgressBar_indeterminateTint
* @see #setIndeterminateTintList(ColorStateList)
*/
+ @InspectableProperty(name = "indeterminateTint")
@Nullable
public ColorStateList getIndeterminateTintList() {
return mProgressTintInfo != null ? mProgressTintInfo.mIndeterminateTintList : null;
@@ -712,6 +716,7 @@
* @attr ref android.R.styleable#ProgressBar_indeterminateTintMode
* @see #setIndeterminateTintMode(PorterDuff.Mode)
*/
+ @InspectableProperty
@Nullable
public PorterDuff.Mode getIndeterminateTintMode() {
return mProgressTintInfo != null ? mProgressTintInfo.mIndeterminateTintMode : null;
@@ -768,6 +773,7 @@
* @see #setProgressDrawable(android.graphics.drawable.Drawable)
* @see #setIndeterminate(boolean)
*/
+ @InspectableProperty
public Drawable getProgressDrawable() {
return mProgressDrawable;
}
@@ -821,6 +827,7 @@
/**
* @hide
*/
+ @InspectableProperty
public boolean getMirrorForRtl() {
return mMirrorForRtl;
}
@@ -951,6 +958,7 @@
* @attr ref android.R.styleable#ProgressBar_progressTint
* @see #setProgressTintList(ColorStateList)
*/
+ @InspectableProperty(name = "progressTint")
@Nullable
public ColorStateList getProgressTintList() {
return mProgressTintInfo != null ? mProgressTintInfo.mProgressTintList : null;
@@ -988,6 +996,7 @@
* @attr ref android.R.styleable#ProgressBar_progressTintMode
* @see #setProgressTintMode(PorterDuff.Mode)
*/
+ @InspectableProperty
@Nullable
public PorterDuff.Mode getProgressTintMode() {
return mProgressTintInfo != null ? mProgressTintInfo.mProgressTintMode : null;
@@ -1033,6 +1042,7 @@
* @attr ref android.R.styleable#ProgressBar_progressBackgroundTint
* @see #setProgressBackgroundTintList(ColorStateList)
*/
+ @InspectableProperty(name = "progressBackgroundTint")
@Nullable
public ColorStateList getProgressBackgroundTintList() {
return mProgressTintInfo != null ? mProgressTintInfo.mProgressBackgroundTintList : null;
@@ -1067,6 +1077,7 @@
* @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode
* @see #setProgressBackgroundTintMode(PorterDuff.Mode)
*/
+ @InspectableProperty
@Nullable
public PorterDuff.Mode getProgressBackgroundTintMode() {
return mProgressTintInfo != null ? mProgressTintInfo.mProgressBackgroundTintMode : null;
@@ -1112,6 +1123,7 @@
* @attr ref android.R.styleable#ProgressBar_secondaryProgressTint
* @see #setSecondaryProgressTintList(ColorStateList)
*/
+ @InspectableProperty(name = "secondaryProgressTint")
@Nullable
public ColorStateList getSecondaryProgressTintList() {
return mProgressTintInfo != null ? mProgressTintInfo.mSecondaryProgressTintList : null;
@@ -1150,6 +1162,7 @@
* @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode
* @see #setSecondaryProgressTintMode(PorterDuff.Mode)
*/
+ @InspectableProperty
@Nullable
public PorterDuff.Mode getSecondaryProgressTintMode() {
return mProgressTintInfo != null ? mProgressTintInfo.mSecondaryProgressTintMode : null;
@@ -1477,6 +1490,7 @@
* @see #getMax()
*/
@ViewDebug.ExportedProperty(category = "progress")
+ @InspectableProperty
public synchronized int getProgress() {
return mIndeterminate ? 0 : mProgress;
}
@@ -1494,6 +1508,7 @@
* @see #getMax()
*/
@ViewDebug.ExportedProperty(category = "progress")
+ @InspectableProperty
public synchronized int getSecondaryProgress() {
return mIndeterminate ? 0 : mSecondaryProgress;
}
@@ -1508,6 +1523,7 @@
* @see #getSecondaryProgress()
*/
@ViewDebug.ExportedProperty(category = "progress")
+ @InspectableProperty
public synchronized int getMin() {
return mMin;
}
@@ -1522,6 +1538,7 @@
* @see #getSecondaryProgress()
*/
@ViewDebug.ExportedProperty(category = "progress")
+ @InspectableProperty
public synchronized int getMax() {
return mMax;
}
@@ -1687,6 +1704,7 @@
*
* @return the {@link Interpolator} associated to this animation
*/
+ @InspectableProperty
public Interpolator getInterpolator() {
return mInterpolator;
}
diff --git a/core/java/android/widget/RatingBar.java b/core/java/android/widget/RatingBar.java
index d343d49..9f9fdee 100644
--- a/core/java/android/widget/RatingBar.java
+++ b/core/java/android/widget/RatingBar.java
@@ -23,6 +23,7 @@
import android.graphics.drawable.shapes.Shape;
import android.util.AttributeSet;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.inspector.InspectableProperty;
import com.android.internal.R;
@@ -164,6 +165,7 @@
*
* @attr ref android.R.styleable#RatingBar_isIndicator
*/
+ @InspectableProperty(name = "isIndicator")
public boolean isIndicator() {
return !mIsUserSeekable;
}
@@ -190,6 +192,7 @@
* Returns the number of stars shown.
* @return The number of stars shown.
*/
+ @InspectableProperty
public int getNumStars() {
return mNumStars;
}
@@ -208,6 +211,7 @@
*
* @return The current rating.
*/
+ @InspectableProperty
public float getRating() {
return getProgress() / getProgressPerStar();
}
@@ -234,6 +238,7 @@
*
* @return The step size.
*/
+ @InspectableProperty
public float getStepSize() {
return (float) getNumStars() / getMax();
}
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 506d615..556bfd1 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -34,6 +34,7 @@
import android.view.ViewGroup;
import android.view.ViewHierarchyEncoder;
import android.view.accessibility.AccessibilityEvent;
+import android.view.inspector.InspectableProperty;
import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
@@ -289,6 +290,16 @@
}
/**
+ * Get the id of the View to be ignored by gravity
+ *
+ * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
+ */
+ @InspectableProperty
+ public int getIgnoreGravity() {
+ return mIgnoreGravity;
+ }
+
+ /**
* Describes how the child views are positioned.
*
* @return the gravity.
@@ -298,6 +309,7 @@
*
* @attr ref android.R.styleable#RelativeLayout_gravity
*/
+ @InspectableProperty(valueType = InspectableProperty.ValueType.GRAVITY)
public int getGravity() {
return mGravity;
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 2dec4e8..8514b85 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -206,13 +206,13 @@
*
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public ApplicationInfo mApplication;
/**
* The resource ID of the layout file. (Added to the parcel)
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private final int mLayoutId;
/**
@@ -224,13 +224,13 @@
* An array of actions to perform on the view tree once it has been
* inflated
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private ArrayList<Action> mActions;
/**
* Maps bitmaps to unique indicies to avoid Bitmap duplication.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private BitmapCache mBitmapCache;
/**
@@ -252,7 +252,7 @@
* RemoteViews.
*/
private RemoteViews mLandscape = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private RemoteViews mPortrait = null;
@ApplyFlags
@@ -430,7 +430,7 @@
// Do nothing
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public int mergeBehavior() {
return MERGE_REPLACE;
}
@@ -466,7 +466,7 @@
// Nothing to visit by default
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
int viewId;
}
@@ -499,7 +499,7 @@
*
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public void mergeRemoteViews(RemoteViews newRv) {
if (newRv == null) return;
// We first copy the new RemoteViews, as the process of merging modifies the way the actions
@@ -690,7 +690,7 @@
return SET_PENDING_INTENT_TEMPLATE_TAG;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
PendingIntent pendingIntentTemplate;
}
@@ -1138,7 +1138,7 @@
private static class BitmapCache {
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
ArrayList<Bitmap> mBitmaps;
int mBitmapMemory = -1;
@@ -1190,9 +1190,9 @@
private class BitmapReflectionAction extends Action {
int bitmapId;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Bitmap bitmap;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
String methodName;
BitmapReflectionAction(int viewId, String methodName, Bitmap bitmap) {
@@ -1258,10 +1258,10 @@
static final int COLOR_STATE_LIST = 15;
static final int ICON = 16;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
String methodName;
int type;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Object value;
ReflectionAction(int viewId, String methodName, int type, Object value) {
@@ -1554,7 +1554,7 @@
* ViewGroup methods that are related to adding Views.
*/
private class ViewGroupActionAdd extends Action {
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private RemoteViews mNestedViews;
private int mIndex;
@@ -2469,7 +2469,7 @@
* Returns an estimate of the bitmap heap memory usage for this RemoteViews.
*/
/** @hide */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public int estimateMemoryUsage() {
return mBitmapCache.getBitmapMemory();
}
@@ -2517,7 +2517,7 @@
*
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public void addView(int viewId, RemoteViews nestedView, int index) {
addAction(new ViewGroupActionAdd(viewId, nestedView, index));
}
@@ -2992,8 +2992,9 @@
* See {@link Adapter#getViewTypeCount()}.
*
* @hide
+ * @deprecated this appears to have no users outside of UnsupportedAppUsage?
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public void setRemoteAdapter(int viewId, ArrayList<RemoteViews> list, int viewTypeCount) {
addAction(new SetRemoteViewsAdapterList(viewId, list, viewTypeCount));
}
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 4658d73..fc4e9ec 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -45,6 +45,7 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.AnimationUtils;
+import android.view.inspector.InspectableProperty;
import com.android.internal.R;
@@ -324,6 +325,7 @@
*
* @attr ref android.R.styleable#ScrollView_fillViewport
*/
+ @InspectableProperty
public boolean isFillViewport() {
return mFillViewport;
}
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 10e1dfb..af3b8c0 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -62,6 +62,7 @@
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
+import android.view.inspector.InspectableProperty;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.TextView.OnEditorActionListener;
@@ -563,6 +564,7 @@
*
* @return the query string
*/
+ @InspectableProperty(hasAttributeId = false)
public CharSequence getQuery() {
return mSearchSrcTextView.getText();
}
@@ -620,6 +622,7 @@
* @return the displayed query hint text, or {@code null} if none set
* @attr ref android.R.styleable#SearchView_queryHint
*/
+ @InspectableProperty
@Nullable
public CharSequence getQueryHint() {
final CharSequence hint;
@@ -656,13 +659,25 @@
* Returns the default iconified state of the search field.
* @return
*
+ * @deprecated use {@link #isIconifiedByDefault()}
* @attr ref android.R.styleable#SearchView_iconifiedByDefault
*/
+ @Deprecated
public boolean isIconfiedByDefault() {
return mIconifiedByDefault;
}
/**
+ * Returns the default iconified state of the search field.
+ *
+ * @attr ref android.R.styleable#SearchView_iconifiedByDefault
+ */
+ @InspectableProperty
+ public boolean isIconifiedByDefault() {
+ return mIconifiedByDefault;
+ }
+
+ /**
* Iconifies or expands the SearchView. Any query text is cleared when iconified. This is
* a temporary state and does not override the default iconified state set by
* {@link #setIconifiedByDefault(boolean)}. If the default state is iconified, then
@@ -686,6 +701,7 @@
* @return true if the SearchView is currently iconified, false if the search field is
* fully visible.
*/
+ @InspectableProperty(hasAttributeId = false)
public boolean isIconified() {
return mIconified;
}
@@ -780,6 +796,7 @@
*
* @attr ref android.R.styleable#SearchView_maxWidth
*/
+ @InspectableProperty
public int getMaxWidth() {
return mMaxWidth;
}
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index 1705ba8..fb56d97 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -45,6 +45,7 @@
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.inspector.InspectableProperty;
import android.widget.PopupWindow.OnDismissListener;
import com.android.internal.R;
@@ -360,6 +361,7 @@
*
* @attr ref android.R.styleable#Spinner_popupBackground
*/
+ @InspectableProperty
public Drawable getPopupBackground() {
return mPopup.getBackground();
}
@@ -392,6 +394,7 @@
*
* @attr ref android.R.styleable#ListPopupWindow_dropDownVerticalOffset
*/
+ @InspectableProperty
public int getDropDownVerticalOffset() {
return mPopup.getVerticalOffset();
}
@@ -416,6 +419,7 @@
*
* @attr ref android.R.styleable#ListPopupWindow_dropDownHorizontalOffset
*/
+ @InspectableProperty
public int getDropDownHorizontalOffset() {
return mPopup.getHorizontalOffset();
}
@@ -452,6 +456,7 @@
*
* @attr ref android.R.styleable#Spinner_dropDownWidth
*/
+ @InspectableProperty
public int getDropDownWidth() {
return mDropDownWidth;
}
@@ -491,6 +496,7 @@
*
* @return A {@link android.view.Gravity Gravity} value
*/
+ @InspectableProperty(valueType = InspectableProperty.ValueType.GRAVITY)
public int getGravity() {
return mGravity;
}
@@ -828,6 +834,7 @@
/**
* @return The prompt to display when the dialog is shown
*/
+ @InspectableProperty
public CharSequence getPrompt() {
return mPopup.getHintText();
}
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 79dc670..af4f0202 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -52,6 +52,7 @@
import android.view.ViewStructure;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.inspector.InspectableProperty;
import com.android.internal.R;
@@ -444,6 +445,7 @@
*
* @attr ref android.R.styleable#Switch_switchPadding
*/
+ @InspectableProperty
public int getSwitchPadding() {
return mSwitchPadding;
}
@@ -469,6 +471,7 @@
*
* @attr ref android.R.styleable#Switch_switchMinWidth
*/
+ @InspectableProperty
public int getSwitchMinWidth() {
return mSwitchMinWidth;
}
@@ -492,6 +495,7 @@
*
* @attr ref android.R.styleable#Switch_thumbTextPadding
*/
+ @InspectableProperty
public int getThumbTextPadding() {
return mThumbTextPadding;
}
@@ -532,6 +536,7 @@
*
* @attr ref android.R.styleable#Switch_track
*/
+ @InspectableProperty(name = "track")
public Drawable getTrackDrawable() {
return mTrackDrawable;
}
@@ -562,6 +567,7 @@
* @attr ref android.R.styleable#Switch_trackTint
* @see #setTrackTintList(ColorStateList)
*/
+ @InspectableProperty(name = "trackTint")
@Nullable
public ColorStateList getTrackTintList() {
return mTrackTintList;
@@ -591,6 +597,7 @@
* @attr ref android.R.styleable#Switch_trackTintMode
* @see #setTrackTintMode(PorterDuff.Mode)
*/
+ @InspectableProperty
@Nullable
public PorterDuff.Mode getTrackTintMode() {
return mTrackTintMode;
@@ -655,6 +662,7 @@
*
* @attr ref android.R.styleable#Switch_thumb
*/
+ @InspectableProperty(name = "thumb")
public Drawable getThumbDrawable() {
return mThumbDrawable;
}
@@ -685,6 +693,7 @@
* @attr ref android.R.styleable#Switch_thumbTint
* @see #setThumbTintList(ColorStateList)
*/
+ @InspectableProperty(name = "thumbTint")
@Nullable
public ColorStateList getThumbTintList() {
return mThumbTintList;
@@ -714,6 +723,7 @@
* @attr ref android.R.styleable#Switch_thumbTintMode
* @see #setThumbTintMode(PorterDuff.Mode)
*/
+ @InspectableProperty
@Nullable
public PorterDuff.Mode getThumbTintMode() {
return mThumbTintMode;
@@ -758,6 +768,7 @@
*
* @attr ref android.R.styleable#Switch_splitTrack
*/
+ @InspectableProperty
public boolean getSplitTrack() {
return mSplitTrack;
}
@@ -767,6 +778,7 @@
*
* @attr ref android.R.styleable#Switch_textOn
*/
+ @InspectableProperty
public CharSequence getTextOn() {
return mTextOn;
}
@@ -786,6 +798,7 @@
*
* @attr ref android.R.styleable#Switch_textOff
*/
+ @InspectableProperty
public CharSequence getTextOff() {
return mTextOff;
}
@@ -817,6 +830,7 @@
* @return whether the on/off text should be displayed
* @attr ref android.R.styleable#Switch_showText
*/
+ @InspectableProperty
public boolean getShowText() {
return mShowText;
}
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index 4865808..7f462cb 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -39,6 +39,7 @@
import android.util.AttributeSet;
import android.view.RemotableViewMethod;
import android.view.ViewHierarchyEncoder;
+import android.view.inspector.InspectableProperty;
import com.android.internal.R;
@@ -285,6 +286,7 @@
* @see #setFormat12Hour(CharSequence)
* @see #is24HourModeEnabled()
*/
+ @InspectableProperty
@ExportedProperty
public CharSequence getFormat12Hour() {
return mFormat12;
@@ -344,6 +346,7 @@
* @see #setFormat24Hour(CharSequence)
* @see #is24HourModeEnabled()
*/
+ @InspectableProperty
@ExportedProperty
public CharSequence getFormat24Hour() {
return mFormat24;
@@ -435,6 +438,7 @@
* @see #setFormat24Hour(CharSequence)
* @see #getFormat24Hour()
*/
+ @InspectableProperty(hasAttributeId = false)
public boolean is24HourModeEnabled() {
if (mShowCurrentUserTime) {
return DateFormat.is24HourFormat(getContext(), ActivityManager.getCurrentUser());
@@ -453,6 +457,7 @@
* @see java.util.TimeZone#getAvailableIDs()
* @see #setTimeZone(String)
*/
+ @InspectableProperty
public String getTimeZone() {
return mTimeZone;
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 8029cf0..d790c0c 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -172,6 +172,9 @@
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
+import android.view.inspector.InspectableProperty;
+import android.view.inspector.InspectableProperty.EnumMap;
+import android.view.inspector.InspectableProperty.FlagMap;
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationManager;
@@ -1889,6 +1892,10 @@
* @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
* @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
*/
+ @InspectableProperty(enumMapping = {
+ @EnumMap(name = "none", value = AUTO_SIZE_TEXT_TYPE_NONE),
+ @EnumMap(name = "uniform", value = AUTO_SIZE_TEXT_TYPE_UNIFORM)
+ })
@AutoSizeTextType
public int getAutoSizeTextType() {
return mAutoSizeTextType;
@@ -1901,6 +1908,7 @@
*
* @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
*/
+ @InspectableProperty
public int getAutoSizeStepGranularity() {
return Math.round(mAutoSizeStepGranularityInPx);
}
@@ -1914,6 +1922,7 @@
* @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
* @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
*/
+ @InspectableProperty
public int getAutoSizeMinTextSize() {
return Math.round(mAutoSizeMinTextSizeInPx);
}
@@ -1927,6 +1936,7 @@
* @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
* @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
*/
+ @InspectableProperty
public int getAutoSizeMaxTextSize() {
return Math.round(mAutoSizeMaxTextSizeInPx);
}
@@ -2271,6 +2281,7 @@
* @attr ref android.R.styleable#TextView_text
*/
@ViewDebug.CapturedViewProperty
+ @InspectableProperty
public CharSequence getText() {
return mText;
}
@@ -2308,6 +2319,7 @@
* contain additional first-or last-line padding.
* @return The height of one standard line in pixels.
*/
+ @InspectableProperty
public int getLineHeight() {
return FastMath.round(mTextPaint.getFontMetricsInt(null) * mSpacingMult + mSpacingAdd);
}
@@ -3231,6 +3243,7 @@
*
* @attr ref android.R.styleable#TextView_drawablePadding
*/
+ @InspectableProperty(name = "drawablePadding")
public int getCompoundDrawablePadding() {
final Drawables dr = mDrawables;
return dr != null ? dr.mDrawablePadding : 0;
@@ -3267,6 +3280,7 @@
* @attr ref android.R.styleable#TextView_drawableTint
* @see #setCompoundDrawableTintList(ColorStateList)
*/
+ @InspectableProperty(name = "drawableTint")
public ColorStateList getCompoundDrawableTintList() {
return mDrawables != null ? mDrawables.mTintList : null;
}
@@ -3301,6 +3315,7 @@
* @attr ref android.R.styleable#TextView_drawableTintMode
* @see #setCompoundDrawableTintMode(PorterDuff.Mode)
*/
+ @InspectableProperty(name = "drawableTintMode")
public PorterDuff.Mode getCompoundDrawableTintMode() {
return mDrawables != null ? mDrawables.mTintMode : null;
}
@@ -3481,6 +3496,7 @@
* @see #setFirstBaselineToTopHeight(int)
* @attr ref android.R.styleable#TextView_firstBaselineToTopHeight
*/
+ @InspectableProperty
public int getFirstBaselineToTopHeight() {
return getPaddingTop() - getPaint().getFontMetricsInt().top;
}
@@ -3491,17 +3507,24 @@
* @see #setLastBaselineToBottomHeight(int)
* @attr ref android.R.styleable#TextView_lastBaselineToBottomHeight
*/
+ @InspectableProperty
public int getLastBaselineToBottomHeight() {
return getPaddingBottom() + getPaint().getFontMetricsInt().bottom;
}
/**
- * Gets the autolink mask of the text. See {@link
- * android.text.util.Linkify#ALL Linkify.ALL} and peers for
- * possible values.
+ * Gets the autolink mask of the text.
+ *
+ * See {@link Linkify#ALL} and peers for possible values.
*
* @attr ref android.R.styleable#TextView_autoLink
*/
+ @InspectableProperty(name = "autoLink", flagMapping = {
+ @FlagMap(name = "web", target = Linkify.WEB_URLS),
+ @FlagMap(name = "email", target = Linkify.EMAIL_ADDRESSES),
+ @FlagMap(name = "phone", target = Linkify.PHONE_NUMBERS),
+ @FlagMap(name = "map", target = Linkify.MAP_ADDRESSES)
+ })
public final int getAutoLinkMask() {
return mAutoLinkMask;
}
@@ -4150,6 +4173,7 @@
/**
* @return the size (in pixels) of the default text size in this TextView.
*/
+ @InspectableProperty
@ViewDebug.ExportedProperty(category = "text")
public float getTextSize() {
return mTextPaint.getTextSize();
@@ -4243,6 +4267,7 @@
* This will usually be 1.0.
* @return The horizontal scale factor.
*/
+ @InspectableProperty
public float getTextScaleX() {
return mTextPaint.getTextScaleX();
}
@@ -4303,6 +4328,7 @@
* @attr ref android.R.styleable#TextView_typeface
* @attr ref android.R.styleable#TextView_textStyle
*/
+ @InspectableProperty
public Typeface getTypeface() {
return mTextPaint.getTypeface();
}
@@ -4363,6 +4389,7 @@
*
* @attr ref android.R.styleable#TextView_fallbackLineSpacing
*/
+ @InspectableProperty
public boolean isFallbackLineSpacing() {
return mUseFallbackLineSpacing;
}
@@ -4376,6 +4403,7 @@
* @see #setElegantTextHeight(boolean)
* @see Paint#setElegantTextHeight(boolean)
*/
+ @InspectableProperty
public boolean isElegantTextHeight() {
return mTextPaint.isElegantTextHeight();
}
@@ -4388,6 +4416,7 @@
* @see #setLetterSpacing(float)
* @see Paint#setLetterSpacing
*/
+ @InspectableProperty
public float getLetterSpacing() {
return mTextPaint.getLetterSpacing();
}
@@ -4426,6 +4455,7 @@
* @see #setFontFeatureSettings(String)
* @see Paint#setFontFeatureSettings(String) Paint.setFontFeatureSettings(String)
*/
+ @InspectableProperty
@Nullable
public String getFontFeatureSettings() {
return mTextPaint.getFontFeatureSettings();
@@ -4477,6 +4507,11 @@
* @attr ref android.R.styleable#TextView_breakStrategy
* @see #setBreakStrategy(int)
*/
+ @InspectableProperty(enumMapping = {
+ @EnumMap(name = "simple", value = Layout.BREAK_STRATEGY_SIMPLE),
+ @EnumMap(name = "high_quality", value = Layout.BREAK_STRATEGY_HIGH_QUALITY),
+ @EnumMap(name = "balanced", value = Layout.BREAK_STRATEGY_BALANCED)
+ })
@Layout.BreakStrategy
public int getBreakStrategy() {
return mBreakStrategy;
@@ -4523,6 +4558,11 @@
* @attr ref android.R.styleable#TextView_hyphenationFrequency
* @see #setHyphenationFrequency(int)
*/
+ @InspectableProperty(enumMapping = {
+ @EnumMap(name = "none", value = Layout.HYPHENATION_FREQUENCY_NONE),
+ @EnumMap(name = "normal", value = Layout.HYPHENATION_FREQUENCY_NORMAL),
+ @EnumMap(name = "full", value = Layout.HYPHENATION_FREQUENCY_FULL)
+ })
@Layout.HyphenationFrequency
public int getHyphenationFrequency() {
return mHyphenationFrequency;
@@ -4580,6 +4620,10 @@
*
* @see #setJustificationMode(int)
*/
+ @InspectableProperty(enumMapping = {
+ @EnumMap(name = "none", value = Layout.JUSTIFICATION_MODE_NONE),
+ @EnumMap(name = "inter_word", value = Layout.JUSTIFICATION_MODE_INTER_WORD)
+ })
public @Layout.JustificationMode int getJustificationMode() {
return mJustificationMode;
}
@@ -4720,6 +4764,7 @@
*
* @attr ref android.R.styleable#TextView_textColor
*/
+ @InspectableProperty(name = "textColor")
public final ColorStateList getTextColors() {
return mTextColor;
}
@@ -4754,6 +4799,7 @@
*
* @attr ref android.R.styleable#TextView_textColorHighlight
*/
+ @InspectableProperty(name = "textColorHighlight")
@ColorInt
public int getHighlightColor() {
return mHighlightColor;
@@ -4819,6 +4865,7 @@
*
* @attr ref android.R.styleable#TextView_shadowRadius
*/
+ @InspectableProperty
public float getShadowRadius() {
return mShadowRadius;
}
@@ -4830,6 +4877,7 @@
*
* @attr ref android.R.styleable#TextView_shadowDx
*/
+ @InspectableProperty
public float getShadowDx() {
return mShadowDx;
}
@@ -4842,6 +4890,7 @@
*
* @attr ref android.R.styleable#TextView_shadowDy
*/
+ @InspectableProperty
public float getShadowDy() {
return mShadowDy;
}
@@ -4854,6 +4903,7 @@
*
* @attr ref android.R.styleable#TextView_shadowColor
*/
+ @InspectableProperty
@ColorInt
public int getShadowColor() {
return mShadowColor;
@@ -4901,6 +4951,7 @@
*
* @attr ref android.R.styleable#TextView_linksClickable
*/
+ @InspectableProperty
public final boolean getLinksClickable() {
return mLinksClickable;
}
@@ -4961,6 +5012,7 @@
*
* @attr ref android.R.styleable#TextView_textColorHint
*/
+ @InspectableProperty(name = "textColorHint")
public final ColorStateList getHintTextColors() {
return mHintTextColor;
}
@@ -5013,6 +5065,7 @@
*
* @attr ref android.R.styleable#TextView_textColorLink
*/
+ @InspectableProperty(name = "textColorLink")
public final ColorStateList getLinkTextColors() {
return mLinkTextColor;
}
@@ -5062,6 +5115,7 @@
* @see android.view.Gravity
* @attr ref android.R.styleable#TextView_gravity
*/
+ @InspectableProperty(valueType = InspectableProperty.ValueType.GRAVITY)
public int getGravity() {
return mGravity;
}
@@ -5118,6 +5172,7 @@
* @attr ref android.R.styleable#TextView_scrollHorizontally
* @see #setHorizontallyScrolling(boolean)
*/
+ @InspectableProperty(name = "scrollHorizontally")
public final boolean isHorizontallyScrollable() {
return mHorizontallyScrolling;
}
@@ -5170,6 +5225,7 @@
*
* @attr ref android.R.styleable#TextView_minLines
*/
+ @InspectableProperty
public int getMinLines() {
return mMinMode == LINES ? mMinimum : -1;
}
@@ -5252,6 +5308,7 @@
*
* @attr ref android.R.styleable#TextView_maxLines
*/
+ @InspectableProperty
public int getMaxLines() {
return mMaxMode == LINES ? mMaximum : -1;
}
@@ -5291,6 +5348,7 @@
*
* @attr ref android.R.styleable#TextView_maxHeight
*/
+ @InspectableProperty
public int getMaxHeight() {
return mMaxMode == PIXELS ? mMaximum : -1;
}
@@ -5375,6 +5433,7 @@
*
* @attr ref android.R.styleable#TextView_minEms
*/
+ @InspectableProperty
public int getMinEms() {
return mMinWidthMode == EMS ? mMinWidth : -1;
}
@@ -5418,6 +5477,7 @@
*
* @attr ref android.R.styleable#TextView_minWidth
*/
+ @InspectableProperty
public int getMinWidth() {
return mMinWidthMode == PIXELS ? mMinWidth : -1;
}
@@ -5457,6 +5517,7 @@
*
* @attr ref android.R.styleable#TextView_maxEms
*/
+ @InspectableProperty
public int getMaxEms() {
return mMaxWidthMode == EMS ? mMaxWidth : -1;
}
@@ -5496,6 +5557,7 @@
*
* @attr ref android.R.styleable#TextView_maxWidth
*/
+ @InspectableProperty
public int getMaxWidth() {
return mMaxWidthMode == PIXELS ? mMaxWidth : -1;
}
@@ -5579,6 +5641,7 @@
*
* @attr ref android.R.styleable#TextView_lineSpacingMultiplier
*/
+ @InspectableProperty
public float getLineSpacingMultiplier() {
return mSpacingMult;
}
@@ -5593,6 +5656,7 @@
*
* @attr ref android.R.styleable#TextView_lineSpacingExtra
*/
+ @InspectableProperty
public float getLineSpacingExtra() {
return mSpacingAdd;
}
@@ -5877,6 +5941,7 @@
*
* @see #setFreezesText
*/
+ @InspectableProperty
public boolean getFreezesText() {
return mFreezesText;
}
@@ -6340,6 +6405,7 @@
*
* @attr ref android.R.styleable#TextView_hint
*/
+ @InspectableProperty
@ViewDebug.CapturedViewProperty
public CharSequence getHint() {
return mHint;
@@ -6351,6 +6417,7 @@
*
* @attr ref android.R.styleable#TextView_singleLine
*/
+ @InspectableProperty
public boolean isSingleLine() {
return mSingleLine;
}
@@ -6592,6 +6659,147 @@
* @see #setInputType(int)
* @see android.text.InputType
*/
+ @InspectableProperty(flagMapping = {
+ @FlagMap(name = "none", mask = 0xffffffff, target = InputType.TYPE_NULL),
+ @FlagMap(
+ name = "text",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION,
+ target = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL),
+ @FlagMap(
+ name = "textUri",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION,
+ target = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI),
+ @FlagMap(
+ name = "textEmailAddress",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION,
+ target = InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS),
+ @FlagMap(
+ name = "textEmailSubject",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION,
+ target = InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_VARIATION_EMAIL_SUBJECT),
+ @FlagMap(
+ name = "textShortMessage",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION,
+ target = InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE),
+ @FlagMap(
+ name = "textLongMessage",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION,
+ target = InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE),
+ @FlagMap(
+ name = "textPersonName",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION,
+ target = InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_VARIATION_PERSON_NAME),
+ @FlagMap(
+ name = "textPostalAddress",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION,
+ target = InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS),
+ @FlagMap(
+ name = "textPassword",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION,
+ target = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD),
+ @FlagMap(
+ name = "textVisiblePassword",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION,
+ target = InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD),
+ @FlagMap(
+ name = "textWebEditText",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION,
+ target = InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT),
+ @FlagMap(
+ name = "textFilter",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION,
+ target = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_FILTER),
+ @FlagMap(
+ name = "textPhonetic",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION,
+ target = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PHONETIC),
+ @FlagMap(
+ name = "textWebEmailAddress",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION,
+ target = InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS),
+ @FlagMap(
+ name = "textWebPassword",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION,
+ target = InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD),
+ @FlagMap(
+ name = "number",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION,
+ target = InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_NORMAL),
+ @FlagMap(
+ name = "numberPassword",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION,
+ target = InputType.TYPE_CLASS_NUMBER
+ | InputType.TYPE_NUMBER_VARIATION_PASSWORD),
+ @FlagMap(
+ name = "phone",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION,
+ target = InputType.TYPE_CLASS_PHONE),
+ @FlagMap(
+ name = "datetime",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION,
+ target = InputType.TYPE_CLASS_DATETIME
+ | InputType.TYPE_DATETIME_VARIATION_NORMAL),
+ @FlagMap(
+ name = "date",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION,
+ target = InputType.TYPE_CLASS_DATETIME
+ | InputType.TYPE_DATETIME_VARIATION_DATE),
+ @FlagMap(
+ name = "time",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION,
+ target = InputType.TYPE_CLASS_DATETIME
+ | InputType.TYPE_DATETIME_VARIATION_TIME),
+ @FlagMap(
+ name = "textCapCharacters",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_FLAGS,
+ target = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS),
+ @FlagMap(
+ name = "textCapWords",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_FLAGS,
+ target = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_WORDS),
+ @FlagMap(
+ name = "textCapSentences",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_FLAGS,
+ target = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES),
+ @FlagMap(
+ name = "textAutoCorrect",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_FLAGS,
+ target = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT),
+ @FlagMap(
+ name = "textAutoComplete",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_FLAGS,
+ target = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE),
+ @FlagMap(
+ name = "textMultiLine",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_FLAGS,
+ target = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE),
+ @FlagMap(
+ name = "textImeMultiLine",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_FLAGS,
+ target = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE),
+ @FlagMap(
+ name = "textNoSuggestions",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_FLAGS,
+ target = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS),
+ @FlagMap(
+ name = "numberSigned",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_FLAGS,
+ target = InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED),
+ @FlagMap(
+ name = "numberDecimal",
+ mask = InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_FLAGS,
+ target = InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL),
+ })
public int getInputType() {
return mEditor == null ? EditorInfo.TYPE_NULL : mEditor.mInputType;
}
@@ -6614,8 +6822,55 @@
* Get the type of the Input Method Editor (IME).
* @return the type of the IME
* @see #setImeOptions(int)
- * @see android.view.inputmethod.EditorInfo
+ * @see EditorInfo
*/
+ @InspectableProperty(flagMapping = {
+ @FlagMap(name = "normal", mask = 0xffffffff, target = EditorInfo.IME_NULL),
+ @FlagMap(
+ name = "actionUnspecified",
+ mask = EditorInfo.IME_MASK_ACTION,
+ target = EditorInfo.IME_ACTION_UNSPECIFIED),
+ @FlagMap(
+ name = "actionNone",
+ mask = EditorInfo.IME_MASK_ACTION,
+ target = EditorInfo.IME_ACTION_NONE),
+ @FlagMap(
+ name = "actionGo",
+ mask = EditorInfo.IME_MASK_ACTION,
+ target = EditorInfo.IME_ACTION_GO),
+ @FlagMap(
+ name = "actionSearch",
+ mask = EditorInfo.IME_MASK_ACTION,
+ target = EditorInfo.IME_ACTION_SEARCH),
+ @FlagMap(
+ name = "actionSend",
+ mask = EditorInfo.IME_MASK_ACTION,
+ target = EditorInfo.IME_ACTION_SEND),
+ @FlagMap(
+ name = "actionNext",
+ mask = EditorInfo.IME_MASK_ACTION,
+ target = EditorInfo.IME_ACTION_NEXT),
+ @FlagMap(
+ name = "actionDone",
+ mask = EditorInfo.IME_MASK_ACTION,
+ target = EditorInfo.IME_ACTION_DONE),
+ @FlagMap(
+ name = "actionPrevious",
+ mask = EditorInfo.IME_MASK_ACTION,
+ target = EditorInfo.IME_ACTION_PREVIOUS),
+ @FlagMap(name = "flagForceAscii", target = EditorInfo.IME_FLAG_FORCE_ASCII),
+ @FlagMap(name = "flagNavigateNext", target = EditorInfo.IME_FLAG_NAVIGATE_NEXT),
+ @FlagMap(name = "flagNavigatePrevious", target = EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS),
+ @FlagMap(
+ name = "flagNoAccessoryAction",
+ target = EditorInfo.IME_FLAG_NO_ACCESSORY_ACTION),
+ @FlagMap(name = "flagNoEnterAction", target = EditorInfo.IME_FLAG_NO_ENTER_ACTION),
+ @FlagMap(name = "flagNoExtractUi", target = EditorInfo.IME_FLAG_NO_EXTRACT_UI),
+ @FlagMap(name = "flagNoFullscreen", target = EditorInfo.IME_FLAG_NO_FULLSCREEN),
+ @FlagMap(
+ name = "flagNoPersonalizedLearning",
+ target = EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING),
+ })
public int getImeOptions() {
return mEditor != null && mEditor.mInputContentType != null
? mEditor.mInputContentType.imeOptions : EditorInfo.IME_NULL;
@@ -6644,6 +6899,7 @@
* @see #setImeActionLabel
* @see android.view.inputmethod.EditorInfo
*/
+ @InspectableProperty
public CharSequence getImeActionLabel() {
return mEditor != null && mEditor.mInputContentType != null
? mEditor.mInputContentType.imeActionLabel : null;
@@ -6655,6 +6911,7 @@
* @see #setImeActionLabel
* @see android.view.inputmethod.EditorInfo
*/
+ @InspectableProperty
public int getImeActionId() {
return mEditor != null && mEditor.mInputContentType != null
? mEditor.mInputContentType.imeActionId : 0;
@@ -6777,6 +7034,7 @@
* @see #setPrivateImeOptions(String)
* @see EditorInfo#privateImeOptions
*/
+ @InspectableProperty
public String getPrivateImeOptions() {
return mEditor != null && mEditor.mInputContentType != null
? mEditor.mInputContentType.privateImeOptions : null;
@@ -7385,6 +7643,7 @@
*
* @attr ref android.R.styleable#TextView_textIsSelectable
*/
+ @InspectableProperty(name = "textIsSelectable")
public boolean isTextSelectable() {
return mEditor == null ? false : mEditor.mTextIsSelectable;
}
@@ -8924,6 +9183,7 @@
*
* @attr ref android.R.styleable#TextView_includeFontPadding
*/
+ @InspectableProperty
public boolean getIncludeFontPadding() {
return mIncludePad;
}
@@ -9870,6 +10130,7 @@
* @see #setAllCaps(boolean)
* @see #setTransformationMethod(TransformationMethod)
*/
+ @InspectableProperty(name = "textAllCaps")
public boolean isAllCaps() {
final TransformationMethod method = getTransformationMethod();
return method != null && method instanceof AllCapsTransformationMethod;
@@ -9979,6 +10240,7 @@
*
* @attr ref android.R.styleable#TextView_marqueeRepeatLimit
*/
+ @InspectableProperty
public int getMarqueeRepeatLimit() {
return mMarqueeRepeatLimit;
}
@@ -9987,6 +10249,7 @@
* Returns where, if anywhere, words that are longer than the view
* is wide should be ellipsized.
*/
+ @InspectableProperty
@ViewDebug.ExportedProperty
public TextUtils.TruncateAt getEllipsize() {
return mEllipsize;
@@ -10038,6 +10301,7 @@
*
* @attr ref android.R.styleable#TextView_cursorVisible
*/
+ @InspectableProperty
public boolean isCursorVisible() {
// true is the default value
return mEditor == null ? true : mEditor.mCursorVisible;
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 6a5c8cc..b239ce6 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -35,6 +35,7 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
+import android.view.inspector.InspectableProperty;
import com.android.internal.R;
@@ -165,6 +166,10 @@
*/
@TimePickerMode
@TestApi
+ @InspectableProperty(name = "timePickerMode", enumMapping = {
+ @InspectableProperty.EnumMap(name = "clock", value = MODE_CLOCK),
+ @InspectableProperty.EnumMap(name = "spinner", value = MODE_SPINNER)
+ })
public int getMode() {
return mMode;
}
@@ -185,6 +190,7 @@
* @return the currently selected hour, in the range (0-23)
* @see #setHour(int)
*/
+ @InspectableProperty(hasAttributeId = false)
public int getHour() {
return mDelegate.getHour();
}
@@ -205,6 +211,7 @@
* @return the currently selected minute, in the range (0-59)
* @see #setMinute(int)
*/
+ @InspectableProperty(hasAttributeId = false)
public int getMinute() {
return mDelegate.getMinute();
}
@@ -272,6 +279,7 @@
* {@code false} otherwise}
* @see #setIs24HourView(Boolean)
*/
+ @InspectableProperty(hasAttributeId = false, name = "24Hour")
public boolean is24HourView() {
return mDelegate.is24Hour();
}
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index c256d57..eef40e1 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -341,10 +341,10 @@
// 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/android/widget/ToggleButton.java b/core/java/android/widget/ToggleButton.java
index 6a8449e..bba6da6 100644
--- a/core/java/android/widget/ToggleButton.java
+++ b/core/java/android/widget/ToggleButton.java
@@ -21,6 +21,7 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.util.AttributeSet;
+import android.view.inspector.InspectableProperty;
/**
* Displays checked/unchecked states as a button
@@ -87,6 +88,7 @@
*
* @return The text.
*/
+ @InspectableProperty
public CharSequence getTextOn() {
return mTextOn;
}
@@ -105,6 +107,7 @@
*
* @return The text.
*/
+ @InspectableProperty
public CharSequence getTextOff() {
return mTextOff;
}
@@ -118,6 +121,16 @@
mTextOff = textOff;
}
+ /**
+ * Returns the alpha value of the button when it is disabled
+ *
+ * @return the alpha value, 0.0-1.0
+ */
+ @InspectableProperty
+ public float getDisabledAlpha() {
+ return mDisabledAlpha;
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index adb7f2f..a33c47d8 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -45,6 +45,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
+import android.view.inspector.InspectableProperty;
import com.android.internal.R;
import com.android.internal.view.menu.MenuBuilder;
@@ -383,6 +384,7 @@
* 0 if menus are inflated against the toolbar theme
* @see #setPopupTheme(int)
*/
+ @InspectableProperty
public int getPopupTheme() {
return mPopupTheme;
}
@@ -414,6 +416,7 @@
* @see #setTitleMarginStart(int)
* @attr ref android.R.styleable#Toolbar_titleMarginStart
*/
+ @InspectableProperty
public int getTitleMarginStart() {
return mTitleMarginStart;
}
@@ -436,6 +439,7 @@
* @see #setTitleMarginTop(int)
* @attr ref android.R.styleable#Toolbar_titleMarginTop
*/
+ @InspectableProperty
public int getTitleMarginTop() {
return mTitleMarginTop;
}
@@ -458,6 +462,7 @@
* @see #setTitleMarginEnd(int)
* @attr ref android.R.styleable#Toolbar_titleMarginEnd
*/
+ @InspectableProperty
public int getTitleMarginEnd() {
return mTitleMarginEnd;
}
@@ -480,6 +485,7 @@
* @see #setTitleMarginBottom(int)
* @attr ref android.R.styleable#Toolbar_titleMarginBottom
*/
+ @InspectableProperty
public int getTitleMarginBottom() {
return mTitleMarginBottom;
}
@@ -650,6 +656,7 @@
* @see #setLogo(int)
* @see #setLogo(android.graphics.drawable.Drawable)
*/
+ @InspectableProperty
public Drawable getLogo() {
return mLogoView != null ? mLogoView.getDrawable() : null;
}
@@ -688,6 +695,7 @@
*
* @return A description of the logo
*/
+ @InspectableProperty
public CharSequence getLogoDescription() {
return mLogoView != null ? mLogoView.getContentDescription() : null;
}
@@ -735,6 +743,7 @@
*
* @return The current title.
*/
+ @InspectableProperty
public CharSequence getTitle() {
return mTitleText;
}
@@ -791,6 +800,7 @@
*
* @return The current subtitle
*/
+ @InspectableProperty
public CharSequence getSubtitle() {
return mSubtitleText;
}
@@ -895,6 +905,7 @@
*
* @attr ref android.R.styleable#Toolbar_navigationContentDescription
*/
+ @InspectableProperty
@Nullable
public CharSequence getNavigationContentDescription() {
return mNavButtonView != null ? mNavButtonView.getContentDescription() : null;
@@ -987,6 +998,7 @@
*
* @attr ref android.R.styleable#Toolbar_navigationIcon
*/
+ @InspectableProperty
@Nullable
public Drawable getNavigationIcon() {
return mNavButtonView != null ? mNavButtonView.getDrawable() : null;
@@ -1024,6 +1036,7 @@
*
* @attr ref android.R.styleable#Toolbar_collapseContentDescription
*/
+ @InspectableProperty
@Nullable
public CharSequence getCollapseContentDescription() {
return mCollapseButtonView != null ? mCollapseButtonView.getContentDescription() : null;
@@ -1069,6 +1082,7 @@
*
* @attr ref android.R.styleable#Toolbar_collapseIcon
*/
+ @InspectableProperty
@Nullable
public Drawable getCollapseIcon() {
return mCollapseButtonView != null ? mCollapseButtonView.getDrawable() : null;
@@ -1234,6 +1248,7 @@
* @see #getContentInsetRight()
* @attr ref android.R.styleable#Toolbar_contentInsetStart
*/
+ @InspectableProperty
public int getContentInsetStart() {
return mContentInsets != null ? mContentInsets.getStart() : 0;
}
@@ -1254,6 +1269,7 @@
* @see #getContentInsetRight()
* @attr ref android.R.styleable#Toolbar_contentInsetEnd
*/
+ @InspectableProperty
public int getContentInsetEnd() {
return mContentInsets != null ? mContentInsets.getEnd() : 0;
}
@@ -1297,6 +1313,7 @@
* @see #getContentInsetRight()
* @attr ref android.R.styleable#Toolbar_contentInsetLeft
*/
+ @InspectableProperty
public int getContentInsetLeft() {
return mContentInsets != null ? mContentInsets.getLeft() : 0;
}
@@ -1317,6 +1334,7 @@
* @see #getContentInsetLeft()
* @attr ref android.R.styleable#Toolbar_contentInsetRight
*/
+ @InspectableProperty
public int getContentInsetRight() {
return mContentInsets != null ? mContentInsets.getRight() : 0;
}
@@ -1333,6 +1351,7 @@
* @see #setContentInsetStartWithNavigation(int)
* @attr ref android.R.styleable#Toolbar_contentInsetStartWithNavigation
*/
+ @InspectableProperty
public int getContentInsetStartWithNavigation() {
return mContentInsetStartWithNavigation != RtlSpacingHelper.UNDEFINED
? mContentInsetStartWithNavigation
@@ -1376,6 +1395,7 @@
* @see #setContentInsetEndWithActions(int)
* @attr ref android.R.styleable#Toolbar_contentInsetEndWithActions
*/
+ @InspectableProperty
public int getContentInsetEndWithActions() {
return mContentInsetEndWithActions != RtlSpacingHelper.UNDEFINED
? mContentInsetEndWithActions
diff --git a/core/java/android/widget/ViewAnimator.java b/core/java/android/widget/ViewAnimator.java
index 598a407..80ea363 100644
--- a/core/java/android/widget/ViewAnimator.java
+++ b/core/java/android/widget/ViewAnimator.java
@@ -26,6 +26,7 @@
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
+import android.view.inspector.InspectableProperty;
/**
* Base class for a {@link FrameLayout} container that will perform animations
@@ -265,6 +266,7 @@
* @see #setInAnimation(android.view.animation.Animation)
* @see #setInAnimation(android.content.Context, int)
*/
+ @InspectableProperty
public Animation getInAnimation() {
return mInAnimation;
}
@@ -289,6 +291,7 @@
* @see #setOutAnimation(android.view.animation.Animation)
* @see #setOutAnimation(android.content.Context, int)
*/
+ @InspectableProperty
public Animation getOutAnimation() {
return mOutAnimation;
}
@@ -340,6 +343,7 @@
*
* @see #setAnimateFirstView(boolean)
*/
+ @InspectableProperty
public boolean getAnimateFirstView() {
return mAnimateFirstTime;
}
diff --git a/core/java/android/widget/ViewFlipper.java b/core/java/android/widget/ViewFlipper.java
index 5e72b2e..414b136 100644
--- a/core/java/android/widget/ViewFlipper.java
+++ b/core/java/android/widget/ViewFlipper.java
@@ -25,6 +25,7 @@
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.inspector.InspectableProperty;
import android.widget.RemoteViews.RemoteView;
/**
@@ -134,6 +135,16 @@
}
/**
+ * Get the delay before flipping to the next view.
+ *
+ * @return delay time in milliseconds
+ */
+ @InspectableProperty
+ public int getFlipInterval() {
+ return mFlipInterval;
+ }
+
+ /**
* Start a timer to cycle through child views
*/
public void startFlipping() {
@@ -191,6 +202,7 @@
/**
* Returns true if the child views are flipping.
*/
+ @InspectableProperty(hasAttributeId = false)
public boolean isFlipping() {
return mStarted;
}
@@ -207,6 +219,7 @@
* Returns true if this view automatically calls {@link #startFlipping()}
* when it becomes attached to a window.
*/
+ @InspectableProperty
public boolean isAutoStart() {
return mAutoStart;
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index cfe2939..ee96ae9 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -16,6 +16,9 @@
package com.android.internal.app;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.prediction.AppPredictionContext;
@@ -27,6 +30,7 @@
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -46,6 +50,7 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -93,11 +98,13 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.ImageUtils;
import com.google.android.collect.Lists;
import java.io.File;
import java.io.IOException;
+import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -174,6 +181,15 @@
private static final int SHORTCUT_MANAGER_SHARE_TARGET_RESULT = 3;
private static final int SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED = 4;
+ @Retention(SOURCE)
+ @IntDef({CONTENT_PREVIEW_FILE, CONTENT_PREVIEW_IMAGE, CONTENT_PREVIEW_TEXT})
+ private @interface ContentPreviewType {
+ }
+
+ private static final int CONTENT_PREVIEW_IMAGE = 0;
+ private static final int CONTENT_PREVIEW_FILE = 1;
+ private static final int CONTENT_PREVIEW_TEXT = 2;
+
private final Handler mChooserHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -448,18 +464,35 @@
return;
}
- ViewGroup contentPreviewLayout = findViewById(R.id.content_preview);
String action = targetIntent.getAction();
if (!(Intent.ACTION_SEND.equals(action) || Intent.ACTION_SEND_MULTIPLE.equals(action))) {
- contentPreviewLayout.setVisibility(View.GONE);
return;
}
- showDefaultContentPreview(contentPreviewLayout, targetIntent);
+ int previewType = findPreferredContentPreview(targetIntent, getContentResolver());
+ displayContentPreview(previewType, targetIntent);
}
- private void showDefaultContentPreview(final ViewGroup parentLayout,
- final Intent targetIntent) {
+ private void displayContentPreview(@ContentPreviewType int previewType, Intent targetIntent) {
+ switch (previewType) {
+ case CONTENT_PREVIEW_TEXT:
+ displayTextContentPreview(targetIntent);
+ break;
+ case CONTENT_PREVIEW_IMAGE:
+ displayImageContentPreview(targetIntent);
+ break;
+ case CONTENT_PREVIEW_FILE:
+ displayFileContentPreview(targetIntent);
+ break;
+ default:
+ Log.e(TAG, "Unexpected content preview type: " + previewType);
+ }
+ }
+
+ private void displayTextContentPreview(Intent targetIntent) {
+ ViewGroup contentPreviewLayout = findViewById(R.id.content_preview_text_area);
+ contentPreviewLayout.setVisibility(View.VISIBLE);
+
CharSequence sharingText = targetIntent.getCharSequenceExtra(Intent.EXTRA_TEXT);
if (sharingText == null) {
findViewById(R.id.content_preview_text_layout).setVisibility(View.GONE);
@@ -498,6 +531,105 @@
}
}
+ private void displayImageContentPreview(Intent targetIntent) {
+ ViewGroup contentPreviewLayout = findViewById(R.id.content_preview_image_area);
+ contentPreviewLayout.setVisibility(View.VISIBLE);
+
+ String action = targetIntent.getAction();
+ if (Intent.ACTION_SEND.equals(action)) {
+ Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM);
+ loadUriIntoView(R.id.content_preview_image_1_large, uri);
+ } else {
+ ContentResolver resolver = getContentResolver();
+
+ List<Uri> uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
+ List<Uri> imageUris = new ArrayList<>();
+ for (Uri uri : uris) {
+ if (isImageType(resolver.getType(uri))) {
+ imageUris.add(uri);
+ }
+ }
+
+ if (imageUris.size() == 0) {
+ Log.i(TAG, "Attempted to display image preview area with zero"
+ + " available images detected in EXTRA_STREAM list");
+ return;
+ }
+
+ loadUriIntoView(R.id.content_preview_image_1_large, imageUris.get(0));
+
+ if (imageUris.size() == 2) {
+ loadUriIntoView(R.id.content_preview_image_2_large, imageUris.get(1));
+ } else if (imageUris.size() > 2) {
+ loadUriIntoView(R.id.content_preview_image_2_small, imageUris.get(1));
+ RoundedRectImageView imageView = loadUriIntoView(
+ R.id.content_preview_image_3_small, imageUris.get(2));
+
+ if (imageUris.size() > 3) {
+ imageView.setExtraImageCount(imageUris.size() - 3);
+ }
+ }
+ }
+ }
+
+ private void displayFileContentPreview(Intent targetIntent) {
+ // support coming
+ }
+
+ private RoundedRectImageView loadUriIntoView(int imageResourceId, Uri uri) {
+ RoundedRectImageView imageView = findViewById(imageResourceId);
+ imageView.setVisibility(View.VISIBLE);
+ Bitmap bmp = loadThumbnail(uri, new Size(200, 200));
+ imageView.setImageBitmap(bmp);
+
+ return imageView;
+ }
+
+ @VisibleForTesting
+ protected boolean isImageType(String mimeType) {
+ return mimeType != null && mimeType.startsWith("image/");
+ }
+
+ @ContentPreviewType
+ private int findPreferredContentPreview(Uri uri, ContentResolver resolver) {
+ if (uri == null) {
+ return CONTENT_PREVIEW_TEXT;
+ }
+
+ String mimeType = resolver.getType(uri);
+ return isImageType(mimeType) ? CONTENT_PREVIEW_IMAGE : CONTENT_PREVIEW_FILE;
+ }
+
+ /**
+ * In {@link android.content.Intent#getType}, the app may specify a very general
+ * mime-type that broadly covers all data being shared, such as {@literal *}/*
+ * when sending an image and text. We therefore should inspect each item for the
+ * the preferred type, in order of IMAGE, FILE, TEXT.
+ */
+ @ContentPreviewType
+ private int findPreferredContentPreview(Intent targetIntent, ContentResolver resolver) {
+ String action = targetIntent.getAction();
+ if (Intent.ACTION_SEND.equals(action)) {
+ Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM);
+ return findPreferredContentPreview(uri, resolver);
+ } else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
+ List<Uri> uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
+ if (uris == null || uris.isEmpty()) {
+ return CONTENT_PREVIEW_TEXT;
+ }
+
+ for (Uri uri : uris) {
+ if (findPreferredContentPreview(uri, resolver) == CONTENT_PREVIEW_IMAGE) {
+ return CONTENT_PREVIEW_IMAGE;
+ }
+ }
+
+ return CONTENT_PREVIEW_FILE;
+ }
+
+ return CONTENT_PREVIEW_TEXT;
+ }
+
static SharedPreferences getPinnedSharedPrefs(Context context) {
// The code below is because in the android:ui process, no one can hear you scream.
// The package info in the context isn't initialized in the way it is for normal apps,
@@ -1114,7 +1246,8 @@
}
try {
- return getContentResolver().loadThumbnail(uri, size, null);
+ return ImageUtils.decodeSampledBitmapFromStream(getContentResolver(),
+ uri, size.getWidth(), size.getHeight());
} catch (IOException | NullPointerException ex) {
Log.w(TAG, "Error loading preview thumbnail for uri: " + uri.toString(), ex);
}
@@ -2045,6 +2178,9 @@
public static class RoundedRectImageView extends ImageView {
private int mRadius = 0;
private Path mPath = new Path();
+ private Paint mOverlayPaint = new Paint(0);
+ private Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private String mExtraImageCount = null;
public RoundedRectImageView(Context context) {
super(context);
@@ -2062,6 +2198,14 @@
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mRadius = context.getResources().getDimensionPixelSize(R.dimen.chooser_corner_radius);
+
+ mOverlayPaint.setColor(0x99000000);
+ mOverlayPaint.setStyle(Paint.Style.FILL);
+
+ mTextPaint.setColor(Color.WHITE);
+ mTextPaint.setTextSize(context.getResources()
+ .getDimensionPixelSize(R.dimen.chooser_preview_image_font_size));
+ mTextPaint.setTextAlign(Paint.Align.CENTER);
}
private void updatePath(int width, int height) {
@@ -2083,12 +2227,24 @@
updatePath(getWidth(), getHeight());
}
+ /**
+ * Display an overlay with extra image count on 3rd image
+ */
+ public void setExtraImageCount(int count) {
+ if (count > 0) {
+ this.mExtraImageCount = "+" + count;
+ } else {
+ this.mExtraImageCount = null;
+ }
+ }
+
@Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
super.onSizeChanged(width, height, oldWidth, oldHeight);
updatePath(width, height);
}
+
@Override
protected void onDraw(Canvas canvas) {
if (mRadius != 0) {
@@ -2096,6 +2252,16 @@
}
super.onDraw(canvas);
+
+ if (mExtraImageCount != null) {
+ canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), mOverlayPaint);
+
+ int xPos = canvas.getWidth() / 2;
+ int yPos = (int) ((canvas.getHeight() / 2.0f)
+ - ((mTextPaint.descent() + mTextPaint.ascent()) / 2.0f));
+
+ canvas.drawText(mExtraImageCount, xPos, yPos, mTextPaint);
+ }
}
}
}
diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java
index cd80d53..429c618 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.view.ContextThemeWrapper;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
+import android.view.contentcapture.ContentCaptureManager;
import java.lang.ref.WeakReference;
@@ -36,6 +37,7 @@
private PhoneWindow mPhoneWindow;
private WindowManager mWindowManager;
private Resources mActivityResources;
+ private ContentCaptureManager mContentCaptureManager;
private WeakReference<Context> mActivityContext;
@@ -60,6 +62,16 @@
}
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 @@
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 9df37ad..bfb5084 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -69,8 +69,9 @@
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/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 226b8db..397df56 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -690,4 +690,17 @@
}
return result;
}
+
+ /**
+ * Returns the first element from the array for which
+ * condition {@code predicate} is true, or null if there is no such element
+ */
+ public static @Nullable <T> T find(@Nullable T[] items,
+ @NonNull java.util.function.Predicate<T> predicate) {
+ if (isEmpty(items)) return null;
+ for (final T item : items) {
+ if (predicate.test(item)) return item;
+ }
+ return null;
+ }
}
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index f91b837..78fdfe4 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -335,6 +335,13 @@
}
/**
+ * @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/util/ImageUtils.java b/core/java/com/android/internal/util/ImageUtils.java
index 7d56e9e..195ae52 100644
--- a/core/java/com/android/internal/util/ImageUtils.java
+++ b/core/java/com/android/internal/util/ImageUtils.java
@@ -16,14 +16,20 @@
package com.android.internal.util;
+import android.content.ContentResolver;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
+import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.net.Uri;
+
+import java.io.IOException;
+import java.io.InputStream;
/**
* Utility class for image analysis and processing.
@@ -80,7 +86,7 @@
width = height = COMPACT_BITMAP_SIZE;
}
- final int size = height*width;
+ final int size = height * width;
ensureBufferSize(size);
bitmap.getPixels(mTempBuffer, 0, width, 0, 0, width, height);
for (int i = 0; i < size; i++) {
@@ -156,4 +162,55 @@
return result;
}
+
+ /**
+ * @see https://developer.android.com/topic/performance/graphics/load-bitmap
+ */
+ public static int calculateInSampleSize(BitmapFactory.Options options,
+ int reqWidth, int reqHeight) {
+ // Raw height and width of image
+ final int height = options.outHeight;
+ final int width = options.outWidth;
+ int inSampleSize = 1;
+
+ if (height > reqHeight || width > reqWidth) {
+ final int halfHeight = height / 2;
+ final int halfWidth = width / 2;
+
+ // Calculate the largest inSampleSize value that is a power of 2 and keeps both
+ // height and width larger than the requested height and width.
+ while ((halfHeight / inSampleSize) >= reqHeight
+ && (halfWidth / inSampleSize) >= reqWidth) {
+ inSampleSize *= 2;
+ }
+ }
+
+ return inSampleSize;
+ }
+
+ /**
+ * Load a bitmap, and attempt to downscale to the required size, to save
+ * on memory.
+ *
+ * @see https://developer.android.com/topic/performance/graphics/load-bitmap
+ */
+ public static Bitmap decodeSampledBitmapFromStream(ContentResolver resolver,
+ Uri uri, int reqWidth, int reqHeight) throws IOException {
+
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ try (InputStream is = resolver.openInputStream(uri)) {
+ // First decode with inJustDecodeBounds=true to check dimensions
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeStream(is, null, options);
+
+ options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
+ }
+
+ // need to do this twice as the InputStream is consumed in the first call,
+ // and not all InputStreams support marks
+ try (InputStream is = resolver.openInputStream(uri)) {
+ options.inJustDecodeBounds = false;
+ return BitmapFactory.decodeStream(is, null, options);
+ }
+ }
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 0752efe..cb18ca1 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -34,9 +34,9 @@
int untrustedDisplayId);
// TODO: Use ParceledListSlice instead
- List<InputMethodInfo> getInputMethodList();
+ List<InputMethodInfo> getInputMethodList(int userId);
// 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 794238a..664643c 100644
--- a/core/java/com/android/internal/view/IInputMethodSession.aidl
+++ b/core/java/com/android/internal/view/IInputMethodSession.aidl
@@ -48,4 +48,6 @@
void finishSession();
void updateCursorAnchorInfo(in CursorAnchorInfo cursorAnchorInfo);
+
+ void notifyImeHidden();
}
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index e6d3460..64291de 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -26,6 +26,7 @@
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.Build;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.Gravity;
@@ -107,10 +108,13 @@
* <p>
* Changes take effect on the next call to show().
*
+ * This method should not be accessed directly outside the framework, please use
+ * {@link android.widget.PopupMenu#setForceShowIcon(boolean)} instead.
+ *
* @param forceShowIcon {@code true} to force icons to be shown, or
* {@code false} for icons to be optionally shown
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public void setForceShowIcon(boolean forceShowIcon) {
mForceShowIcon = forceShowIcon;
if (mPopup != null) {
diff --git a/core/java/com/android/internal/widget/MediaNotificationView.java b/core/java/com/android/internal/widget/MediaNotificationView.java
index 7609b67..498bc5a 100644
--- a/core/java/com/android/internal/widget/MediaNotificationView.java
+++ b/core/java/com/android/internal/widget/MediaNotificationView.java
@@ -39,6 +39,7 @@
private View mActions;
private View mHeader;
private View mMainColumn;
+ private View mMediaContent;
private int mImagePushIn;
public MediaNotificationView(Context context) {
@@ -70,7 +71,7 @@
(MarginLayoutParams) mRightIcon.getLayoutParams();
int imageEndMargin = layoutParams.getMarginEnd();
size -= imageEndMargin;
- int fullHeight = getMeasuredHeight();
+ int fullHeight = mMediaContent.getMeasuredHeight();
if (size > fullHeight) {
size = fullHeight;
} else if (size < fullHeight) {
@@ -154,5 +155,6 @@
mActions = findViewById(com.android.internal.R.id.media_actions);
mHeader = findViewById(com.android.internal.R.id.notification_header);
mMainColumn = findViewById(com.android.internal.R.id.notification_main_column);
+ mMediaContent = findViewById(com.android.internal.R.id.notification_media_content);
}
}
diff --git a/core/java/com/android/server/backup/PermissionBackupHelper.java b/core/java/com/android/server/backup/PermissionBackupHelper.java
index ff0e63d..c0ba181 100644
--- a/core/java/com/android/server/backup/PermissionBackupHelper.java
+++ b/core/java/com/android/server/backup/PermissionBackupHelper.java
@@ -19,7 +19,6 @@
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 @@
// 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 @@
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 @@
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 47e7a0e7..8878421 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -19,7 +19,7 @@
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.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 @@
// 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 @@
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 e7a1c49..345058b 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -276,6 +276,7 @@
"libmediametrics",
"libmeminfo",
"libaudioclient",
+ "libaudiopolicy",
"libjpeg",
"libusbhost",
"libharfbuzz_ng",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 49598bb..d69d416 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -230,6 +230,11 @@
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 @@
}
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/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 79bfa17..70e6604f 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -507,9 +507,18 @@
ninePatchChunk, ninePatchInsets, -1);
}
+ // Speculative fix for b/112551574. It doesn't seem like |b| can be null. If it is, print some
+ // info that might be helpful to diagnose.
+ Bitmap* b = defaultAllocator.getStorageObjAndReset();
+ if (!b) {
+ ALOGW("defaultAllocator has no storage object!");
+ ALOGW("\tjavaBitmap: %s", (javaBitmap == nullptr ? "null" : "present"));
+ ALOGW("\tisHardware: %s", (isHardware ? "true" : "false"));
+ ALOGW("\twillScale: %s", (willScale ? "true" : "false"));
+ return nullptr;
+ }
// now create the java bitmap
- return bitmap::createBitmap(env, defaultAllocator.getStorageObjAndReset(),
- bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1);
+ return bitmap::createBitmap(env, b, bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1);
}
static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index a24f920..f784661 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -59,12 +59,84 @@
android:layout_centerHorizontal="true"/>
</RelativeLayout>
+ <!-- The following 3 layouts are mutually exclusive. One of them will be
+ set VISIBLE programatically, when the optimal preview type can be
+ determined by inspecting the data being shared. This path was chosen
+ b/c inflating layouts in code had sizing problems with this widget. -->
+
+ <!-- Layout Option 1: Supporting up to 3 images for preview -->
<LinearLayout
- android:id="@+id/content_preview"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:background="?attr/colorBackgroundFloating">
+ <RelativeLayout
+ android:id="@+id/content_preview_image_area"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:paddingBottom="@dimen/chooser_view_spacing"
+ android:visibility="gone"
+ android:background="?attr/colorBackgroundFloating">
+
+ <view class="com.android.internal.app.ChooserActivity$RoundedRectImageView"
+ android:id="@+id/content_preview_image_1_large"
+ android:visibility="gone"
+ android:layout_width="120dp"
+ android:layout_height="140dp"
+ android:layout_alignParentTop="true"
+ android:adjustViewBounds="true"
+ android:gravity="center"
+ android:scaleType="centerCrop"/>
+
+ <view class="com.android.internal.app.ChooserActivity$RoundedRectImageView"
+ android:id="@+id/content_preview_image_2_large"
+ android:visibility="gone"
+ android:layout_width="120dp"
+ android:layout_height="140dp"
+ android:layout_alignParentTop="true"
+ android:layout_toRightOf="@id/content_preview_image_1_large"
+ android:layout_marginLeft="10dp"
+ android:adjustViewBounds="true"
+ android:gravity="center"
+ android:scaleType="centerCrop"/>
+
+ <view class="com.android.internal.app.ChooserActivity$RoundedRectImageView"
+ android:id="@+id/content_preview_image_2_small"
+ android:visibility="gone"
+ android:layout_width="120dp"
+ android:layout_height="65dp"
+ android:layout_alignParentTop="true"
+ android:layout_toRightOf="@id/content_preview_image_1_large"
+ android:layout_marginLeft="10dp"
+ android:adjustViewBounds="true"
+ android:gravity="center"
+ android:scaleType="centerCrop"/>
+
+ <view class="com.android.internal.app.ChooserActivity$RoundedRectImageView"
+ android:id="@+id/content_preview_image_3_small"
+ android:visibility="gone"
+ android:layout_width="120dp"
+ android:layout_height="65dp"
+ android:layout_below="@id/content_preview_image_2_small"
+ android:layout_toRightOf="@id/content_preview_image_1_large"
+ android:layout_marginLeft="10dp"
+ android:layout_marginTop="10dp"
+ android:adjustViewBounds="true"
+ android:gravity="center"
+ android:scaleType="centerCrop"/>
+
+ </RelativeLayout>
+ </LinearLayout>
+
+ <!-- Layout Option 2: Text preview, with optional title and thumbnail -->
+ <LinearLayout
+ android:id="@+id/content_preview_text_area"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="@dimen/chooser_view_spacing"
+ android:visibility="gone"
android:background="?attr/colorBackgroundFloating">
<LinearLayout
@@ -108,15 +180,12 @@
<view class="com.android.internal.app.ChooserActivity$RoundedRectImageView"
android:id="@+id/content_preview_thumbnail"
- android:layout_width="80dp"
- android:layout_height="80dp"
- android:layout_marginRight="12dp"
+ android:layout_width="75dp"
+ android:layout_height="75dp"
+ android:layout_marginRight="16dp"
android:adjustViewBounds="true"
android:layout_gravity="center_vertical"
android:gravity="center"
- android:maxWidth="70dp"
- android:maxHeight="70dp"
- android:padding="5dp"
android:scaleType="centerCrop"/>
<TextView
diff --git a/core/res/res/layout/notification_material_media_seekbar.xml b/core/res/res/layout/notification_material_media_seekbar.xml
new file mode 100644
index 0000000..1b691d6
--- /dev/null
+++ b/core/res/res/layout/notification_material_media_seekbar.xml
@@ -0,0 +1,64 @@
+<?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
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/notification_media_progress"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_alignParentBottom="true"
+ >
+ <SeekBar android:id="@+id/notification_media_progress_bar"
+ style="@style/Widget.ProgressBar.Horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:maxHeight="3dp"
+ android:paddingTop="24dp"
+ android:paddingBottom="24dp"
+ android:layout_marginBottom="-24dp"
+ android:layout_marginTop="-12dp"
+ android:splitTrack="false"
+ />
+ <FrameLayout
+ android:id="@+id/notification_media_progress_time"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginBottom="11dp"
+ >
+
+ <!-- width is set to "match_parent" to avoid extra layout calls -->
+ <TextView android:id="@+id/notification_media_elapsed_time"
+ style="@style/Widget.DeviceDefault.Notification.Text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:gravity="left"
+ />
+
+ <TextView android:id="@+id/notification_media_total_time"
+ style="@style/Widget.DeviceDefault.Notification.Text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:gravity="right"
+ />
+ </FrameLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/notification_template_material_big_media.xml b/core/res/res/layout/notification_template_material_big_media.xml
index 56f7a59..3267f72 100644
--- a/core/res/res/layout/notification_template_material_big_media.xml
+++ b/core/res/res/layout/notification_template_material_big_media.xml
@@ -39,6 +39,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
+ android:id="@+id/notification_media_content"
>
<LinearLayout
android:id="@+id/notification_main_column"
@@ -84,5 +85,9 @@
android:id="@+id/media_seamless"
/>
</LinearLayout>
+ <ViewStub android:id="@+id/notification_media_seekbar_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
</LinearLayout>
</com.android.internal.widget.MediaNotificationView>
diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
index 01b0866..64d91ad 100644
--- a/core/res/res/layout/notification_template_material_media.xml
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -34,49 +34,61 @@
android:layout_width="match_parent"
android:layout_height="@dimen/media_notification_header_height" />
<LinearLayout
- android:id="@+id/notification_main_column"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:layout_marginStart="@dimen/notification_content_margin_start"
- android:layout_marginTop="@dimen/notification_content_margin_top"
- android:tag="media"
+ android:orientation="vertical"
+ android:id="@+id/notification_media_content"
>
<LinearLayout
- android:id="@+id/notification_content_container"
+ android:id="@+id/notification_main_column"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="fill_vertical"
- android:layout_weight="1"
- android:minHeight="@dimen/notification_min_content_height"
- android:paddingBottom="@dimen/notification_content_margin"
- android:orientation="vertical"
- >
- <include layout="@layout/notification_template_part_line1"/>
- <include layout="@layout/notification_template_text"/>
- </LinearLayout>
- <LinearLayout
- android:id="@+id/media_actions"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom|end"
- android:layout_marginStart="10dp"
- android:layout_marginBottom="@dimen/media_notification_actions_padding_bottom"
- android:layoutDirection="ltr"
android:orientation="horizontal"
+ android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:layout_marginTop="@dimen/notification_content_margin_top"
+ android:layout_alignParentTop="true"
+ android:tag="media"
>
- <include
- layout="@layout/notification_material_media_action"
- android:id="@+id/action0"
- />
- <include
- layout="@layout/notification_material_media_action"
- android:id="@+id/action1"
- />
- <include
- layout="@layout/notification_material_media_action"
- android:id="@+id/action2"
- />
+ <LinearLayout
+ android:id="@+id/notification_content_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="fill_vertical"
+ android:layout_weight="1"
+ android:minHeight="@dimen/notification_min_content_height"
+ android:paddingBottom="@dimen/notification_content_margin"
+ android:orientation="vertical"
+ >
+ <include layout="@layout/notification_template_part_line1"/>
+ <include layout="@layout/notification_template_text"/>
+ </LinearLayout>
+ <LinearLayout
+ android:id="@+id/media_actions"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|end"
+ android:layout_marginStart="10dp"
+ android:layoutDirection="ltr"
+ android:orientation="horizontal"
+ >
+ <include
+ layout="@layout/notification_material_media_action"
+ android:id="@+id/action0"
+ />
+ <include
+ layout="@layout/notification_material_media_action"
+ android:id="@+id/action1"
+ />
+ <include
+ layout="@layout/notification_material_media_action"
+ android:id="@+id/action2"
+ />
+ </LinearLayout>
</LinearLayout>
+ <ViewStub android:id="@+id/notification_media_seekbar_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ />
</LinearLayout>
</FrameLayout>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index df20f85..d65ef03 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1933,18 +1933,11 @@
the platform will search for an SMS app and use that (if there is one)-->
<string name="default_sms_application" translatable="false">com.android.messaging</string>
- <!-- Default web browser. This is the package name of the application that will
- be the default browser when the device first boots. Afterwards the user
- can select whatever browser app they wish to use as the default.
-
- If this string is empty or the specified package does not exist, then
- the behavior will be as though no app was named as an explicit default. -->
- <string name="default_browser" translatable="false"></string>
-
<!-- Default role holders. This will be an array of roles and package names of their default
holders, with each item in the format of "ROLE_NAME: PACKAGE_NAME_1, PACKAGE_NAME_2". -->
<string-array name="config_defaultRoleHolders" translatable="false">
<item>android.app.role.SMS: com.android.messaging</item>
+ <item>android.app.role.DIALER: com.android.phone</item>
</string-array>
<!-- Enable/disable default bluetooth profiles:
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 38367fb..9f86f84 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -720,4 +720,5 @@
<dimen name="chooser_view_spacing">18dp</dimen>
<dimen name="chooser_edge_margin_thin">16dp</dimen>
<dimen name="chooser_edge_margin_normal">24dp</dimen>
+ <dimen name="chooser_preview_image_font_size">20sp</dimen>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c909873..6d4b04c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -50,9 +50,14 @@
<java-symbol type="id" name="characterPicker" />
<java-symbol type="id" name="clearDefaultHint" />
<java-symbol type="id" name="contentPanel" />
- <java-symbol type="id" name="content_preview" />
+ <java-symbol type="id" name="content_preview_image_area" />
+ <java-symbol type="id" name="content_preview_image_1_large" />
+ <java-symbol type="id" name="content_preview_image_2_large" />
+ <java-symbol type="id" name="content_preview_image_2_small" />
+ <java-symbol type="id" name="content_preview_image_3_small" />
<java-symbol type="id" name="content_preview_thumbnail" />
<java-symbol type="id" name="content_preview_text" />
+ <java-symbol type="id" name="content_preview_text_area" />
<java-symbol type="id" name="content_preview_text_layout" />
<java-symbol type="id" name="content_preview_title" />
<java-symbol type="id" name="content_preview_title_layout" />
@@ -195,6 +200,12 @@
<java-symbol type="id" name="action3" />
<java-symbol type="id" name="action4" />
<java-symbol type="id" name="media_seamless" />
+ <java-symbol type="id" name="notification_media_seekbar_container" />
+ <java-symbol type="id" name="notification_media_content" />
+ <java-symbol type="id" name="notification_media_progress" />
+ <java-symbol type="id" name="notification_media_progress_bar" />
+ <java-symbol type="id" name="notification_media_elapsed_time" />
+ <java-symbol type="id" name="notification_media_total_time" />
<java-symbol type="id" name="big_picture" />
<java-symbol type="id" name="big_text" />
<java-symbol type="id" name="chronometer" />
@@ -1023,7 +1034,6 @@
<java-symbol type="string" name="sipAddressTypeOther" />
<java-symbol type="string" name="sipAddressTypeWork" />
<java-symbol type="string" name="default_sms_application" />
- <java-symbol type="string" name="default_browser" />
<java-symbol type="string" name="sms_control_message" />
<java-symbol type="string" name="sms_control_title" />
<java-symbol type="string" name="sms_control_no" />
@@ -1573,6 +1583,7 @@
<java-symbol type="layout" name="immersive_mode_cling" />
<java-symbol type="layout" name="user_switching_dialog" />
<java-symbol type="layout" name="common_tab_settings" />
+ <java-symbol type="layout" name="notification_material_media_seekbar" />
<java-symbol type="anim" name="slide_in_child_bottom" />
<java-symbol type="anim" name="slide_in_right" />
@@ -2725,6 +2736,7 @@
<java-symbol type="dimen" name="chooser_view_spacing" />
<java-symbol type="dimen" name="chooser_edge_margin_thin" />
<java-symbol type="dimen" name="chooser_edge_margin_normal" />
+ <java-symbol type="dimen" name="chooser_preview_image_font_size"/>
<java-symbol type="layout" name="chooser_grid" />
<java-symbol type="layout" name="resolve_grid_item" />
<java-symbol type="id" name="day_picker_view_pager" />
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index 6a81050..40ebd44 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -36,12 +36,11 @@
frameworks-core-util-lib \
mockwebserver \
guava \
- androidx.test.runner \
- androidx.test.ext.junit \
- androidx.test.rules \
androidx.test.espresso.core \
+ androidx.test.ext.junit \
+ androidx.test.runner \
+ androidx.test.rules \
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 be1d44c..c71bc73 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 @@
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 55837ba..7a1de0c 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 @@
}
}
- @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 @@
a5 = ValueAnimator.ofFloat(10f, 5f).setDuration(850);
}
- @Override
+ @After
public void tearDown() throws Exception {
mActivity = null;
a1 = null;
@@ -86,10 +96,9 @@
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 @@
}
}
- @SmallTest
+ @Test
public void testTotalDuration() {
ArrayList<Animator> list = getAnimatorList();
@@ -192,7 +201,7 @@
}
- @SmallTest
+ @Test
public void testGetDuration() {
AnimatorSet s = new AnimatorSet();
assertTrue(s.getDuration() < 0);
@@ -205,8 +214,8 @@
}
- @SmallTest
@UiThreadTest
+ @Test
public void testSetDuration() {
AnimatorSet s = getSequentialSet();
assertTrue(s.getDuration() < 0);
@@ -224,7 +233,7 @@
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 @@
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 @@
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 @@
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 @@
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());
+ 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());
- }
+ 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 @@
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 @@
}
}
- @SmallTest
+ @Test
public void testCancel() throws Throwable {
// Check whether cancel would trigger onAnimationCanceled and cancel all the unfinished
// animations
@@ -411,42 +397,33 @@
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);
- }
- }
- 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);
+ 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);
+ }
+ });
}
- @SmallTest
+ @Test
public void testIsRunning() throws Throwable {
final AnimatorSet s = new AnimatorSet();
final long startDelay = 500;
@@ -455,12 +432,7 @@
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 @@
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 @@
// 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 @@
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 @@
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);
+ 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();
- }
+ 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);
+ 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();
- }
+ 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);
+ 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));
- }
+ // 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 @@
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 4e90d1a..94c90aa 100644
--- a/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java
+++ b/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java
@@ -23,6 +23,8 @@
import com.android.frameworks.coretests.R;
+import org.junit.Test;
+
import java.util.concurrent.TimeUnit;
/**
@@ -36,7 +38,7 @@
@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 @@
* 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 b3ec92c..7df7336 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 @@
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 @@
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 @@
}
@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 ba7413a..0c40a95 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 @@
* 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 @@
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 @@
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 @@
mFuture.setException(new RuntimeException(e));
}
}
- };
+ }
/**
* Timeout length, based on when the animation should reasonably be complete.
@@ -95,10 +107,12 @@
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 @@
mFuture.setException(new RuntimeException(e));
}
}
- };
+ }
/**
* Pauses the given animator. Used to delay pausing until some later time (after the
@@ -116,10 +130,12 @@
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 @@
mFuture.setException(new RuntimeException(e));
}
}
- };
+ }
/**
* Resumes the given animator. Used to delay resuming until some later time (after the
@@ -137,10 +153,12 @@
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 @@
mFuture.setException(new RuntimeException(e));
}
}
- };
+ }
/**
* Releases the given Future object when the listener's end() event is called. Specifically,
@@ -171,28 +189,14 @@
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 @@
* 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 @@
}
};
- mAnimator.addListener(mListener);
+ mAnimator.addListener(listener);
mAnimator.setDuration(ANIM_DURATION);
-
mFuture = new FutureWaiter();
mRunning = false;
@@ -251,6 +253,7 @@
*/
@UiThreadTest
@SmallTest
+ @Test
public void testCancel() throws Exception {
mAnimator.cancel();
}
@@ -260,6 +263,7 @@
*/
@UiThreadTest
@SmallTest
+ @Test
public void testEnd() throws Exception {
mRunning = true; // end() implicitly starts an unstarted animator
mAnimator.end();
@@ -270,19 +274,17 @@
*/
@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 @@
*/
@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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 53f9472..63ad061 100644
--- a/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java
+++ b/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java
@@ -27,11 +27,10 @@
@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 e755b89..12f1977 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);
- }
+ @Rule
+ public final ActivityTestRule<BasicAnimatorActivity> mActivityRule =
+ new ActivityTestRule<>(BasicAnimatorActivity.class);
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- }
-
+ @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 @@
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 @@
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 f6d71b8..ba9aef8 100644
--- a/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java
+++ b/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java
@@ -26,5 +26,4 @@
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 997af00..81cd4da 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 @@
* 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 @@
mFuture.setException(new RuntimeException(e));
}
}
- };
+ }
/**
* Timeout length, based on when the animation should reasonably be complete.
@@ -108,28 +118,14 @@
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 @@
* 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 @@
*/
@UiThreadTest
@SmallTest
+ @Test
public void testCancel() throws Exception {
mAnimator.cancel();
}
@@ -204,19 +199,17 @@
*/
@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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 16be0b0..d8799cb 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 @@
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 d0719cb..7855ef9 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.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.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 b07cb99..da81d17 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -24,6 +24,7 @@
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 @@
@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 7cd3c44..71ce02d 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -19,13 +19,23 @@
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 @@
private SurfaceSession mSession = new SurfaceSession();
private SurfaceControl mTopLeash;
private SurfaceControl mNavLeash;
+ private InsetsState mInsetsState;
@Mock Transaction mMockTransaction;
@Mock InsetsController mMockController;
@@ -63,6 +74,7 @@
@Before
public void setup() {
+ ViewRootImpl.sNewInsetsMode = NEW_INSETS_MODE_FULL;
MockitoAnnotations.initMocks(this);
mTopLeash = new SurfaceControl.Builder(mSession)
.setName("testSurface")
@@ -70,24 +82,25 @@
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 @@
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 8f21096..6dad6a2 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -27,6 +27,7 @@
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 @@
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 @@
@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 @@
@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 @@
mController.show(Type.ime());
mController.cancelExistingAnimation();
- assertTrue(mController.getSourceConsumer(ime.getType()).isVisible());
+ // no focused view, no IME.
+ assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ });
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
- mController.hide(Type.ime());
+ @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();
+ }
+
+ @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 82cd213..66146c9 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.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 @@
.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 @@
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 bec9b55..cfdec4d 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 1990135..f63a454 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 71612e6..34fdebf 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 @@
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 e4f55df..585c601 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 483270e..f6e02bc 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.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 @@
getInstrumentation().waitForIdleSync();
}
- @SmallTest
+ @Test
public void testOnTextContextMenuItem() {
final String text = "abc def ghi";
@@ -94,14 +97,14 @@
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 @@
assertSuggestionsPopupIsDisplayed();
}
- @SmallTest
+ @Test
public void testInsertionActionMode() {
final String text = "abc def ghi";
@@ -146,13 +149,13 @@
}
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 @@
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 @@
// TODO: Check if add to dictionary dialog is displayed.
}
- @SmallTest
+ @Test
public void testEasyCorrect() {
final String text = "abc def ghi";
@@ -253,7 +256,7 @@
getActivity().getString(com.android.internal.R.string.delete));
}
- @SmallTest
+ @Test
public void testTextAppearanceInSuggestionsPopup() {
final String text = "abc def ghi";
@@ -302,53 +305,49 @@
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);
+ 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);
+ 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]));
+ // 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 41fa08b..b411668 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.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 9d93421..267a9c8 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.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.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.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 02ee9be..0f8faa8 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 daf9e78..217553e 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 b2c8e38..afdc4d3 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 1693e54..1b849b5 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 0355f82..d45d4b0 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 b50d6f4..f56af5a 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 022be76..abee736 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 @@
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 @@
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 b5a96ae..32c02403 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 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;
-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;
-
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 b4c547e..83ce67b 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.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 5ed69e0..d638da5 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 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 8bce1b0..1e6447d 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/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 6b9e69c..b6f56ad 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -421,6 +421,83 @@
assertThat("text/plain", is(clipDescription.getMimeType(0)));
}
+ @Test
+ public void oneVisibleImagePreview() throws InterruptedException {
+ Uri uri = Uri.parse("android.resource://com.android.frameworks.coretests/"
+ + com.android.frameworks.coretests.R.drawable.test320x240);
+
+ ArrayList<Uri> uris = new ArrayList<>();
+ uris.add(uri);
+
+ Intent sendIntent = createSendImageIntentWithPreview(uris);
+ sOverrides.previewThumbnail = createBitmap();
+
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+ onView(withId(R.id.content_preview_image_1_large)).check(matches(isDisplayed()));
+ onView(withId(R.id.content_preview_image_2_large)).check(matches(not(isDisplayed())));
+ onView(withId(R.id.content_preview_image_2_small)).check(matches(not(isDisplayed())));
+ onView(withId(R.id.content_preview_image_3_small)).check(matches(not(isDisplayed())));
+ }
+
+ @Test
+ public void twoVisibleImagePreview() throws InterruptedException {
+ Uri uri = Uri.parse("android.resource://com.android.frameworks.coretests/"
+ + com.android.frameworks.coretests.R.drawable.test320x240);
+
+ ArrayList<Uri> uris = new ArrayList<>();
+ uris.add(uri);
+ uris.add(uri);
+
+ Intent sendIntent = createSendImageIntentWithPreview(uris);
+ sOverrides.previewThumbnail = createBitmap();
+
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+ onView(withId(R.id.content_preview_image_1_large)).check(matches(isDisplayed()));
+ onView(withId(R.id.content_preview_image_2_large)).check(matches(isDisplayed()));
+ onView(withId(R.id.content_preview_image_2_small)).check(matches(not(isDisplayed())));
+ onView(withId(R.id.content_preview_image_3_small)).check(matches(not(isDisplayed())));
+ }
+
+ @Test
+ public void threeOrMoreVisibleImagePreview() throws InterruptedException {
+ Uri uri = Uri.parse("android.resource://com.android.frameworks.coretests/"
+ + com.android.frameworks.coretests.R.drawable.test320x240);
+
+ ArrayList<Uri> uris = new ArrayList<>();
+ uris.add(uri);
+ uris.add(uri);
+ uris.add(uri);
+ uris.add(uri);
+ uris.add(uri);
+
+ Intent sendIntent = createSendImageIntentWithPreview(uris);
+ sOverrides.previewThumbnail = createBitmap();
+
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+ onView(withId(R.id.content_preview_image_1_large)).check(matches(isDisplayed()));
+ onView(withId(R.id.content_preview_image_2_large)).check(matches(not(isDisplayed())));
+ onView(withId(R.id.content_preview_image_2_small)).check(matches(isDisplayed()));
+ onView(withId(R.id.content_preview_image_3_small)).check(matches(isDisplayed()));
+ }
+
private Intent createSendTextIntent() {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
@@ -441,6 +518,20 @@
return sendIntent;
}
+ private Intent createSendImageIntentWithPreview(ArrayList<Uri> uris) {
+ Intent sendIntent = new Intent();
+
+ if (uris.size() > 1) {
+ sendIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
+ sendIntent.putExtra(Intent.EXTRA_STREAM, uris);
+ } else {
+ sendIntent.setAction(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_STREAM, uris.get(0));
+ }
+
+ return sendIntent;
+ }
+
private Intent createViewTextIntent() {
Intent viewIntent = new Intent();
viewIntent.setAction(Intent.ACTION_VIEW);
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index 637d2ea..ec8122f 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -85,6 +85,15 @@
return super.loadThumbnail(uri, size);
}
+ @Override
+ protected boolean isImageType(String mimeType) {
+ if (sOverrides.previewThumbnail != null) {
+ return true;
+ }
+
+ return super.isImageType(mimeType);
+ }
+
/**
* We cannot directly mock the activity created since instrumentation creates it.
* <p>
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 fe2fb85..7430c7a 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 3ddd8aa..64b7c2cc 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 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 b740ecc..22432a8 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 @@
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/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
index 28a8afe..0a729a9 100644
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -181,6 +181,11 @@
}
@Override
+ public void sendVolumeKeyEvent(
+ final int deviceType, final int keyCode, final boolean isPressed) {
+ }
+
+ @Override
public void oneTouchPlay(final IHdmiControlCallback callback) {
}
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 035ee10..bb47658 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -58,6 +58,14 @@
}
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 0000000..ccdb21f
--- /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 2e7b998..0f35918 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -49,17 +49,6 @@
<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 @@
<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 080c5d5..18f0cae 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -25,6 +25,7 @@
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 @@
*/
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 d0fe022..15f53f2 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -42,7 +42,13 @@
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 @@
"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 @@
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 9343076..2c24edd 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 @@
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 2f8d381..fe2d41e 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -43,7 +43,9 @@
, mImageInfo(image_info) {}
VkFunctorDrawHandler::~VkFunctorDrawHandler() {
- mFunctorHandle->postDrawVk();
+ if (mDrawn) {
+ mFunctorHandle->postDrawVk();
+ }
}
void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) {
@@ -77,6 +79,7 @@
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 1a53c8f..d3f9777 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
@@ -44,6 +44,8 @@
const SkMatrix mMatrix;
const SkIRect mClip;
const SkImageInfo mImageInfo;
+
+ bool mDrawn = false;
};
/**
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 6c04232..6da8062 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -117,7 +117,6 @@
auto& cache = skiapipeline::ShaderCache::get();
cache.initShaderDiskCache(identity, size);
contextOptions->fPersistentCache = &cache;
- contextOptions->fGpuPathRenderers &= ~GpuPathRenderers::kCoverageCounting;
}
void CacheManager::trimMemory(TrimMemoryMode mode) {
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 8bef359..3b37c83 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -203,11 +203,17 @@
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 582d51e..90397fd 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -89,7 +89,7 @@
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 @@
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 @@
hasKHRAndroidSurfaceExtension = true;
}
}
- if (!hasKHRSurfaceExtension || !hasKHRAndroidSurfaceExtension) {
- this->destroy();
- return false;
- }
+ LOG_ALWAYS_FATAL_IF(!hasKHRSurfaceExtension || !hasKHRAndroidSurfaceExtension);
}
const VkInstanceCreateInfo instance_create = {
@@ -146,10 +139,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
GET_DEV_PROC(DestroyFence);
GET_DEV_PROC(WaitForFences);
GET_DEV_PROC(ResetFences);
-
- return true;
}
void VulkanManager::initialize() {
@@ -381,7 +334,7 @@
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 @@
if (!setupDummyCommandBuffer()) {
this->destroy();
- return;
+ // Pass through will crash on next line.
}
LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE);
@@ -520,6 +473,9 @@
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 6426fe2..1fe6c65 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -151,7 +151,7 @@
// 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 e795b81..1aeefb8 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -85,12 +85,12 @@
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 8334071..c027fd4 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -2413,17 +2413,18 @@
}
/**
- * Return the package that implements the {@link #NETWORK_PROVIDER} functionality.
+ * Returns true if the given package name matches a location provider package, and false
+ * otherwise.
*
* @hide
*/
@SystemApi
- public @Nullable String getNetworkProviderPackage() {
+ public boolean isProviderPackage(String packageName) {
try {
- return mService.getNetworkProviderPackage();
+ return mService.isProviderPackage(packageName);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
- return null;
+ return false;
}
}
diff --git a/location/java/com/android/internal/location/ILocationProviderManager.aidl b/location/java/com/android/internal/location/ILocationProviderManager.aidl
index b1b8f0c..79166ae 100644
--- a/location/java/com/android/internal/location/ILocationProviderManager.aidl
+++ b/location/java/com/android/internal/location/ILocationProviderManager.aidl
@@ -26,6 +26,8 @@
*/
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 c721218..67d6496 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -19,6 +19,7 @@
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 5bcec92..7cd7207 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 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 @@
@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 @@
mManager = null;
mProperties = properties.getProviderProperties();
mEnabled = true;
+ mAdditionalProviderPackages = new ArrayList<>(0);
}
public IBinder getBinder() {
@@ -160,6 +164,29 @@
}
/**
+ * 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 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 1aefebe..141d415c 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -102,7 +102,8 @@
"--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 @@
":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 925ca0d..f7afa31 100644
--- a/media/apex/java/android/media/MediaPlayer2.java
+++ b/media/apex/java/android/media/MediaPlayer2.java
@@ -86,6 +86,12 @@
/**
* 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 1d763ce..0af47e8 100644
--- a/media/java/android/media/AudioRecordingConfiguration.java
+++ b/media/java/android/media/AudioRecordingConfiguration.java
@@ -402,7 +402,7 @@
&& (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 4f74ec9..963b1d1 100644
--- a/media/java/android/media/CamcorderProfile.java
+++ b/media/java/android/media/CamcorderProfile.java
@@ -97,9 +97,33 @@
*/
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 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 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 ce631a433..cf5711d 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -18,8 +18,12 @@
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 @@
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 @@
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 @@
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 @@
}
/**
+ * 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 @@
}
/**
+ * 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 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 @@
/**
* 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(MediaCas MediaCas, int event, int arg, @Nullable byte[] data);
+ 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.
+ */
+ 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 @@
}
}
- /**
+ /**
* Initiate a provisioning operation for a CA system.
*
* @param provisionString string containing information needed for the
@@ -603,4 +683,4 @@
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 0c3d625..c6c2fdd 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -3373,6 +3373,8 @@
/**
* 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 @@
* 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 c82b5f6..4ca0216 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -463,6 +463,63 @@
= "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 5f51320..e08dab4 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 @@
// 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/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index e66a4624..2dba1d5b 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -105,8 +105,6 @@
mHvacController.connectToCarService();
CarSystemUIFactory factory = SystemUIFactory.getInstance();
- mCarFacetButtonController = factory.getCarDependencyComponent()
- .getCarFacetButtonController();
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned();
if (!mDeviceIsProvisioned) {
@@ -137,7 +135,7 @@
/**
* Remove all content from navbars and rebuild them. Used to allow for different nav bars
- * before and after the device is provisioned
+ * before and after the device is provisioned. . Also for change of density and font size.
*/
private void restartNavBars() {
// remove and reattach all hvac components such that we don't keep a reference to unused
@@ -253,6 +251,9 @@
super.makeStatusBarView();
mHvacController = new HvacController(mContext);
+ CarSystemUIFactory factory = SystemUIFactory.getInstance();
+ mCarFacetButtonController = factory.getCarDependencyComponent()
+ .getCarFacetButtonController();
mNotificationPanelBackground = getDefaultWallpaper();
mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
@@ -589,6 +590,7 @@
@Override
public void onDensityOrFontScaleChanged() {
super.onDensityOrFontScaleChanged();
+ restartNavBars();
// Need to update the background on density changed in case the change was due to night
// mode.
mNotificationPanelBackground = getDefaultWallpaper();
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index b7d8492..b6b229c 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 @@
protected AssistantSettings.Factory mSettingsFactory = AssistantSettings.FACTORY;
@VisibleForTesting
protected AssistantSettings mSettings;
+ private SmsHelper mSmsHelper;
public Assistant() {
}
@@ -122,6 +123,18 @@
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 @@
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 @@
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 @@
@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 @@
@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 b077138..07be0b8 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 @@
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/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 5a81f8b..36ee813 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -20,7 +20,7 @@
"SettingsLibLayoutPreference",
"SettingsLibActionButtonsPreference",
"SettingsLibEntityHeaderWidgets",
- "SettingsLibBarChartPreference"
+ "SettingsLibBarChartPreference",
],
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
@@ -34,4 +34,18 @@
}
-// For the test package.
+// NOTE: Keep this module in sync with ./common.mk
+java_defaults {
+ name: "SettingsLibDefaults",
+ static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.lifecycle_lifecycle-common",
+ "androidx.legacy_legacy-support-v4",
+ "androidx.lifecycle_lifecycle-runtime",
+ "androidx.recyclerview_recyclerview",
+ "androidx.preference_preference",
+ "androidx.appcompat_appcompat",
+ "androidx.legacy_legacy-preference-v14",
+ "SettingsLib",
+ ],
+}
diff --git a/packages/SettingsLib/common.mk b/packages/SettingsLib/common.mk
index 834b83b..8c309ff 100644
--- a/packages/SettingsLib/common.mk
+++ b/packages/SettingsLib/common.mk
@@ -12,6 +12,11 @@
#
# include frameworks/base/packages/SettingsLib/common.mk
#
+# During the conversion to Soong bluprint files, the equivalent
+# functionality is provided by adding
+# defaults: ["SettingsLibDefaults"],
+# to the corresponding module.
+# NOTE: keep this file and ./Android.bp in sync.
LOCAL_STATIC_JAVA_LIBRARIES += \
androidx.annotation_annotation \
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
index 9451b36..ec8bb80 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
@@ -420,7 +420,7 @@
List<InputMethodInfo> getInputMethodList() {
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(
Context.INPUT_METHOD_SERVICE);
- return imm.getInputMethodList();
+ return imm.getInputMethodListAsUser(mUser.getIdentifier());
}
}
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index c032683..e8c728d 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 815ae9a..866b46f 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 a67b5e8..300f63a 100644
--- a/packages/SystemUI/docs/physics-animation-layout.md
+++ b/packages/SystemUI/docs/physics-animation-layout.md
@@ -26,7 +26,7 @@
### 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
new file mode 100644
index 0000000..67f072f
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_preview.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_thumbnail.png
new file mode 100644
index 0000000..8d0e6ed
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_thumbnail.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_preview.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_preview.png
new file mode 100644
index 0000000..035a4df
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_preview.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_thumbnail.png
new file mode 100644
index 0000000..1ac0113
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_thumbnail.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_preview.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_preview.png
new file mode 100644
index 0000000..63927bc
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_preview.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_thumbnail.png
new file mode 100644
index 0000000..83d714b
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_thumbnail.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_preview.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_preview.png
new file mode 100644
index 0000000..a538149
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_preview.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_thumbnail.png
new file mode 100644
index 0000000..2bfd655
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_thumbnail.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/layout/type_clock.xml b/packages/SystemUI/res-keyguard/layout/type_clock.xml
index 21c64e9..f4a7376 100644
--- a/packages/SystemUI/res-keyguard/layout/type_clock.xml
+++ b/packages/SystemUI/res-keyguard/layout/type_clock.xml
@@ -24,32 +24,8 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- >
- <TextView
- android:id="@+id/header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="50dp"
- style="@style/widget_big"
- android:textColor="@color/typeClockAccentColor"
- android:text="@string/type_clock_header"
- android:textSize="40dp"
- />
- <TextView
- android:id="@+id/hour"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="50dp"
- style="@style/widget_big"
- android:textSize="40dp"
- />
- <TextView
- android:id="@+id/minute"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="50dp"
- style="@style/widget_big"
- android:textSize="40dp"
- />
- </com.android.keyguard.clock.TypographicClock>
+ android:paddingLeft="50dp"
+ style="@style/widget_big"
+ android:textSize="40dp"
+ />
</com.android.keyguard.clock.ClockLayout>
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 41acf82..94481e7 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -402,8 +402,25 @@
number">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact carrier for details.</item>
</plurals>
- <!-- Header for typographic clock face. [CHAR LIMIT=8] -->
- <string name="type_clock_header">It\u2019s</string>
+ <!-- Time displayed on typographic clock face, which displays the time in words.
+ Example:
+
+ It's
+ Four
+ Twenty
+ Nine
+
+ This string requires two arguments: the first in the hours of the time and
+ the second is the minutes of the time. The hours string is obtained from
+ string-array type_clock_hours below and the minutes string is obtained
+ from string-array type_clock_minutes below.
+
+ [CHAR LIMIT=8] -->
+ <plurals name="type_clock_header">
+ <item quantity="one"><annotation name="color">It\u2019s</annotation>\n^1\n^2</item>
+ <item quantity="few"><annotation name="color">It\u2019s</annotation>\n^1\n^2</item>
+ <item quantity="other"><annotation name="color">It\u2019s</annotation>\n^1\n^2</item>
+ </plurals>
<!-- Hour displayed in words on the typographic clock face. [CHAR LIMIT=12] -->
<string-array name="type_clock_hours">
@@ -485,4 +502,17 @@
<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 1060211..a5c8eaf 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 -->
@@ -83,6 +84,9 @@
<!-- Increased height of a small notification in the status bar -->
<dimen name="notification_min_height_increased">146dp</dimen>
+ <!-- Increased height of a collapsed media notification in the status bar -->
+ <dimen name="notification_min_height_media">160dp</dimen>
+
<!-- Height of a small notification in the status bar which was used before android N -->
<dimen name="notification_min_height_legacy">64dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 56d9bf4c..5365dcf 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 2ce6965..d5bd2b2 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.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 @@
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 @@
// 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 @@
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 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 64c5b17..261f391 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -264,7 +264,8 @@
*/
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 0000000..812f215
--- /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 3217ca6..9598142 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 @@
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 @@
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 @@
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 @@
}
}
+ /**
+ * 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 0000000..5ef35be
--- /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/keyguard/clock/TypographicClock.java b/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java
index 5f9da3e..8feae53 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java
@@ -17,9 +17,14 @@
import android.content.Context;
import android.content.res.Resources;
+import android.text.Annotation;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.SpannedString;
+import android.text.TextUtils;
import android.text.format.DateFormat;
+import android.text.style.ForegroundColorSpan;
import android.util.AttributeSet;
-import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.keyguard.R;
@@ -31,13 +36,14 @@
/**
* Clock that presents the time in words.
*/
-public class TypographicClock extends LinearLayout {
+public class TypographicClock extends TextView {
+ private static final String ANNOTATION_COLOR = "color";
+
+ private final Resources mResources;
private final String[] mHours;
private final String[] mMinutes;
- private TextView mHeaderText;
- private TextView mHourText;
- private TextView mMinuteText;
+ private final int mAccentColor;
private Calendar mTime;
private String mDescFormat;
private TimeZone mTimeZone;
@@ -54,9 +60,10 @@
super(context, attrs, defStyleAttr);
mTime = Calendar.getInstance();
mDescFormat = ((SimpleDateFormat) DateFormat.getTimeFormat(context)).toLocalizedPattern();
- Resources res = context.getResources();
- mHours = res.getStringArray(R.array.type_clock_hours);
- mMinutes = res.getStringArray(R.array.type_clock_minutes);
+ mResources = context.getResources();
+ mHours = mResources.getStringArray(R.array.type_clock_hours);
+ mMinutes = mResources.getStringArray(R.array.type_clock_minutes);
+ mAccentColor = mResources.getColor(R.color.typeClockAccentColor, null);
}
/**
@@ -65,11 +72,28 @@
public void onTimeChanged() {
mTime.setTimeInMillis(System.currentTimeMillis());
setContentDescription(DateFormat.format(mDescFormat, mTime));
- final int hour = mTime.get(Calendar.HOUR);
- mHourText.setText(mHours[hour % 12]);
- final int minute = mTime.get(Calendar.MINUTE);
- mMinuteText.setText(mMinutes[minute % 60]);
- invalidate();
+ final int hour = mTime.get(Calendar.HOUR) % 12;
+ final int minute = mTime.get(Calendar.MINUTE) % 60;
+
+ // Get the quantity based on the hour for languages like Portuguese and Czech.
+ SpannedString typeTemplate = (SpannedString) mResources.getQuantityText(
+ R.plurals.type_clock_header, hour);
+
+ // Find the "color" annotation and set the foreground color to the accent color.
+ Annotation[] annotations = typeTemplate.getSpans(0, typeTemplate.length(),
+ Annotation.class);
+ SpannableString spanType = new SpannableString(typeTemplate);
+ for (int i = 0; i < annotations.length; i++) {
+ Annotation annotation = annotations[i];
+ String key = annotation.getValue();
+ if (ANNOTATION_COLOR.equals(key)) {
+ spanType.setSpan(new ForegroundColorSpan(mAccentColor),
+ spanType.getSpanStart(annotation), spanType.getSpanEnd(annotation),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+
+ setText(TextUtils.expandTemplate(spanType, mHours[hour], mMinutes[minute]));
}
/**
@@ -82,25 +106,6 @@
mTime.setTimeZone(timeZone);
}
- /**
- * Set the color of the text used to display the time.
- *
- * This is necessary when the wallpaper shown behind the clock on the
- * lock screen changes.
- */
- public void setTextColor(int color) {
- mHourText.setTextColor(color);
- mMinuteText.setTextColor(color);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mHeaderText = findViewById(R.id.header);
- mHourText = findViewById(R.id.hour);
- mMinuteText = findViewById(R.id.minute);
- }
-
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 404f2e5..9a9a52f 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 @@
* 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 4f870f6..1644064 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 @@
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 @@
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 @@
@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 onChildToBeRemoved(View child, int index, Runnable actuallyRemove) {
- // TODO: Animate the bubble out, and pull the other bubbles into its position.
- actuallyRemove.run();
+ 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
+ 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 e4e6bc9..a4ddbf7 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 @@
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 @@
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 @@
// 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 @@
/** 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 23c6fd3..0c089a75 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 @@
}
@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/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index e338a34..3250182 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -71,7 +71,7 @@
new DozeScreenState(wrappedService, handler, params, wakeLock),
createDozeScreenBrightness(context, wrappedService, sensorManager, host, params,
handler),
- new DozeWallpaperState(context),
+ new DozeWallpaperState(context, machine),
new DozeDockHandler(context, machine, host, config, handler, dockManager)
});
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
index be504ef..ca9f24f 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
@@ -37,17 +37,20 @@
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final IWallpaperManager mWallpaperManagerService;
- private boolean mIsAmbientMode;
private final DozeParameters mDozeParameters;
+ private final DozeMachine mMachine;
+ private boolean mIsAmbientMode;
- public DozeWallpaperState(Context context) {
- this(IWallpaperManager.Stub.asInterface(
+ public DozeWallpaperState(Context context, DozeMachine machine) {
+ this(machine, IWallpaperManager.Stub.asInterface(
ServiceManager.getService(Context.WALLPAPER_SERVICE)),
DozeParameters.getInstance(context));
}
@VisibleForTesting
- DozeWallpaperState(IWallpaperManager wallpaperManagerService, DozeParameters parameters) {
+ DozeWallpaperState(DozeMachine machine, IWallpaperManager wallpaperManagerService,
+ DozeParameters parameters) {
+ mMachine = machine;
mWallpaperManagerService = wallpaperManagerService;
mDozeParameters = parameters;
}
@@ -61,10 +64,13 @@
case DOZE_AOD_PAUSING:
case DOZE_AOD_PAUSED:
case DOZE_REQUEST_PULSE:
- case DOZE_PULSING:
case DOZE_PULSE_DONE:
isAmbientMode = true;
break;
+ case DOZE_PULSING:
+ isAmbientMode =
+ mMachine.getPulseReason() != DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
+ break;
default:
isAmbientMode = false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index c0ed4b9..b865ce8 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.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 @@
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 @@
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 @@
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 @@
boolean roaming;
}
-
/**
* TextView that changes its ellipsize value with its visibility.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 2d64ecd..74e82b2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -67,6 +67,7 @@
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.privacy.OngoingPrivacyChip;
import com.android.systemui.privacy.OngoingPrivacyDialog;
+import com.android.systemui.privacy.PrivacyDialogBuilder;
import com.android.systemui.privacy.PrivacyItem;
import com.android.systemui.privacy.PrivacyItemController;
import com.android.systemui.qs.QSDetail.Callback;
@@ -534,10 +535,12 @@
mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
AlarmClock.ACTION_SHOW_ALARMS),0);
} else if (v == mPrivacyChip) {
+ // Makes sure that the builder is grabbed as soon as the chip is pressed
+ PrivacyDialogBuilder builder = mPrivacyChip.getBuilder();
+ if (builder.getAppsAndTypes().size() == 0) return;
Handler mUiHandler = new Handler(Looper.getMainLooper());
mUiHandler.post(() -> {
- Dialog mDialog = new OngoingPrivacyDialog(mContext,
- mPrivacyChip.getBuilder()).createDialog();
+ Dialog mDialog = new OngoingPrivacyDialog(mContext, builder).createDialog();
mDialog.getWindow().setType(
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
SystemUIDialog.setShowForAllUsers(mDialog, true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 31d1621..491f310 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.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 @@
return false;
}
- return activateRemoteInput(view, inputs, input, pendingIntent);
+ return activateRemoteInput(view, inputs, input, pendingIntent,
+ null /* editedSuggestionInfo */);
}
};
@@ -291,6 +293,19 @@
}
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 @@
* @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 @@
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 5a8f71d..736b9eb 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 @@
* 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 @@
* 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 db9fcc8..9f1693c 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 @@
/** 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 @@
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/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index bed2426..b8e33a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -90,6 +90,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationCounters;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationMediaTemplateViewWrapper;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -144,6 +145,7 @@
private int mNotificationMinHeightBeforeP;
private int mNotificationMinHeight;
private int mNotificationMinHeightLarge;
+ private int mNotificationMinHeightMedia;
private int mNotificationMaxHeight;
private int mIncreasedPaddingBetweenElements;
private int mNotificationLaunchHeight;
@@ -652,8 +654,15 @@
boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N;
boolean beforeP = mEntry.targetSdk < Build.VERSION_CODES.P;
int minHeight;
+
+ View expandedView = layout.getExpandedChild();
+ boolean isMediaLayout = expandedView != null
+ && expandedView.findViewById(com.android.internal.R.id.media_actions) != null;
+
if (customView && beforeP && !mIsSummaryWithChildren) {
minHeight = beforeN ? mNotificationMinHeightBeforeN : mNotificationMinHeightBeforeP;
+ } else if (isMediaLayout && !NotificationMediaTemplateViewWrapper.HIDE_COMPACT_SCRUBBER) {
+ minHeight = mNotificationMinHeightMedia;
} else if (mUseIncreasedCollapsedHeight && layout == mPrivateLayout) {
minHeight = mNotificationMinHeightLarge;
} else {
@@ -1642,6 +1651,8 @@
R.dimen.notification_min_height);
mNotificationMinHeightLarge = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_min_height_increased);
+ mNotificationMinHeightMedia = NotificationUtils.getFontScaledHeight(mContext,
+ R.dimen.notification_min_height_media);
mNotificationMaxHeight = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_max_height);
mMaxHeadsUpHeightBeforeN = NotificationUtils.getFontScaledHeight(mContext,
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 878d533..8095615 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 @@
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/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
index 5a9a568..ddda3e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
@@ -16,26 +16,222 @@
package com.android.systemui.statusbar.notification.row.wrapper;
-import android.content.Context;
-import android.view.View;
+import static com.android.systemui.Dependency.MAIN_HANDLER;
+import android.app.Notification;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.media.MediaMetadata;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+import android.os.Handler;
+import android.text.format.DateUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewStub;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import com.android.internal.R;
+import com.android.systemui.Dependency;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import java.util.Timer;
+import java.util.TimerTask;
+
/**
* Wraps a notification containing a media template
*/
public class NotificationMediaTemplateViewWrapper extends NotificationTemplateViewWrapper {
+ private static final String TAG = "NotificationMediaTVW";
+ private static final long PROGRESS_UPDATE_INTERVAL = 1000; // 1s
+ private static final String COMPACT_MEDIA_TAG = "media";
+ private final Handler mHandler = Dependency.get(MAIN_HANDLER);
+ private Timer mSeekBarTimer;
+ private View mActions;
+ private SeekBar mSeekBar;
+ private TextView mSeekBarElapsedTime;
+ private TextView mSeekBarTotalTime;
+ private long mDuration = 0;
+ private MediaController mMediaController;
+ private View mSeekBarView;
+ private Context mContext;
+
+ // TODO: implement as phenotype flag
+ public static final boolean HIDE_COMPACT_SCRUBBER = true;
+
+ private SeekBar.OnSeekBarChangeListener mSeekListener = new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ if (mMediaController != null && canSeekMedia()) {
+ mMediaController.getTransportControls().seekTo(mSeekBar.getProgress());
+ }
+ }
+ };
+
+ private MediaController.Callback mMediaCallback = new MediaController.Callback() {
+ @Override
+ public void onSessionDestroyed() {
+ clearTimer();
+ mMediaController.unregisterCallback(this);
+ }
+
+ @Override
+ public void onPlaybackStateChanged(PlaybackState state) {
+ if (state.getState() != PlaybackState.STATE_PLAYING) {
+ clearTimer();
+ } else if (mSeekBarTimer == null) {
+ startTimer();
+ }
+ }
+ };
+
protected NotificationMediaTemplateViewWrapper(Context ctx, View view,
ExpandableNotificationRow row) {
super(ctx, view, row);
+ mContext = ctx;
}
- View mActions;
-
private void resolveViews() {
mActions = mView.findViewById(com.android.internal.R.id.media_actions);
+
+ final MediaSession.Token token = mRow.getEntry().notification.getNotification().extras
+ .getParcelable(Notification.EXTRA_MEDIA_SESSION);
+
+ if (token == null || (COMPACT_MEDIA_TAG.equals(mView.getTag()) && HIDE_COMPACT_SCRUBBER)) {
+ if (mSeekBarView != null) {
+ mSeekBarView.setVisibility(View.GONE);
+ }
+ return;
+ }
+
+ // Check for existing media controller and clean up / create as necessary
+ if (mMediaController == null || !mMediaController.getSessionToken().equals(token)) {
+ if (mMediaController != null) {
+ mMediaController.unregisterCallback(mMediaCallback);
+ }
+ mMediaController = new MediaController(mContext, token);
+ }
+
+ if (mMediaController.getMetadata() != null) {
+ long duration = mMediaController.getMetadata().getLong(
+ MediaMetadata.METADATA_KEY_DURATION);
+ if (duration <= 0) {
+ // Don't include the seekbar if this is a livestream
+ Log.d(TAG, "removing seekbar");
+ if (mSeekBarView != null) {
+ mSeekBarView.setVisibility(View.GONE);
+ }
+ return;
+ } else {
+ // Otherwise, make sure the seekbar is visible
+ if (mSeekBarView != null) {
+ mSeekBarView.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+ // Inflate the seekbar template
+ ViewStub stub = mView.findViewById(R.id.notification_media_seekbar_container);
+ if (stub instanceof ViewStub) {
+ LayoutInflater layoutInflater = LayoutInflater.from(stub.getContext());
+ stub.setLayoutInflater(layoutInflater);
+ stub.setLayoutResource(R.layout.notification_material_media_seekbar);
+ mSeekBarView = stub.inflate();
+
+ mSeekBar = mSeekBarView.findViewById(R.id.notification_media_progress_bar);
+ mSeekBar.setOnSeekBarChangeListener(mSeekListener);
+
+ mSeekBarElapsedTime = mSeekBarView.findViewById(R.id.notification_media_elapsed_time);
+ mSeekBarTotalTime = mSeekBarView.findViewById(R.id.notification_media_total_time);
+
+ if (mSeekBarTimer == null) {
+ // Disable seeking if it is not supported for this media session
+ if (!canSeekMedia()) {
+ mSeekBar.getThumb().setAlpha(0);
+ mSeekBar.setEnabled(false);
+ } else {
+ mSeekBar.getThumb().setAlpha(255);
+ mSeekBar.setEnabled(true);
+ }
+
+ startTimer();
+
+ mMediaController.registerCallback(mMediaCallback);
+ }
+ }
+ updateSeekBarTint(mSeekBarView);
+ }
+
+ private void startTimer() {
+ clearTimer();
+ mSeekBarTimer = new Timer(true /* isDaemon */);
+ mSeekBarTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ mHandler.post(mUpdatePlaybackUi);
+ }
+ }, 0, PROGRESS_UPDATE_INTERVAL);
+ }
+
+ private void clearTimer() {
+ if (mSeekBarTimer != null) {
+ // TODO: also trigger this when the notification panel is collapsed
+ mSeekBarTimer.cancel();
+ mSeekBarTimer.purge();
+ mSeekBarTimer = null;
+ }
+ }
+
+ private boolean canSeekMedia() {
+ if (mMediaController == null || mMediaController.getPlaybackState() == null) {
+ return false;
+ }
+
+ long actions = mMediaController.getPlaybackState().getActions();
+ return (actions == 0 || (actions & PlaybackState.ACTION_SEEK_TO) != 0);
+ }
+
+ protected final Runnable mUpdatePlaybackUi = new Runnable() {
+ @Override
+ public void run() {
+ if (mMediaController != null && mMediaController.getMetadata() != null
+ && mSeekBar != null) {
+ long position = mMediaController.getPlaybackState().getPosition();
+ long duration = mMediaController.getMetadata().getLong(
+ MediaMetadata.METADATA_KEY_DURATION);
+
+ if (mDuration != duration) {
+ mDuration = duration;
+ mSeekBar.setMax((int) mDuration);
+ mSeekBarTotalTime.setText(millisecondsToTimeString(duration));
+ }
+ mSeekBar.setProgress((int) position);
+
+ mSeekBarElapsedTime.setText(millisecondsToTimeString(position));
+ } else {
+ // We no longer have a media session / notification
+ clearTimer();
+ }
+ }
+ };
+
+ private String millisecondsToTimeString(long milliseconds) {
+ long seconds = milliseconds / 1000;
+ String text = DateUtils.formatElapsedTime(seconds);
+ return text;
}
@Override
@@ -46,6 +242,27 @@
super.onContentUpdated(row);
}
+ private void updateSeekBarTint(View seekBarContainer) {
+ if (seekBarContainer == null) {
+ return;
+ }
+
+ if (this.getNotificationHeader() == null) {
+ return;
+ }
+
+ int tintColor = getNotificationHeader().getOriginalIconColor();
+ mSeekBarElapsedTime.setTextColor(tintColor);
+ mSeekBarTotalTime.setTextColor(tintColor);
+
+ ColorStateList tintList = ColorStateList.valueOf(tintColor);
+ mSeekBar.setThumbTintList(tintList);
+ tintList = tintList.withAlpha(192); // 75%
+ mSeekBar.setProgressTintList(tintList);
+ tintList = tintList.withAlpha(128); // 50%
+ mSeekBar.setProgressBackgroundTintList(tintList);
+ }
+
@Override
protected void updateTransformedTypes() {
// This also clears the existing types
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 0000000..dae4da7
--- /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 02683c1..651670c 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 @@
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.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.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 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 @@
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 @@
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 @@
}
};
+ 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 @@
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 @@
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 @@
return mDownHitTarget;
}
+ public @WindowTarget int getWindowTarget() {
+ return mWindowHitTarget;
+ }
+
public void abortCurrentGesture() {
getHomeButton().abortCurrentGesture();
}
@@ -837,24 +882,32 @@
}
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 (enable) {
+ lp.flags |= flags;
+ } else {
+ lp.flags &= ~flags;
}
- if (changed) {
- WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE);
- wm.updateViewLayout(navbarView, lp);
- }
+ WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
+ wm.updateViewLayout(navbarView, lp);
}
public void setMenuVisibility(final boolean show) {
@@ -1016,6 +1069,17 @@
} 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 @@
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 @@
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 b4feb25..8421e23 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 @@
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 @@
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 @@
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 @@
} 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 @@
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 d5d283c..84f1cef 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_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.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 @@
/** 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 @@
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 @@
};
private final Runnable mClickThroughResetTap = () -> {
- setWindowTouchable(true);
+ mNavigationBarView.setWindowTouchable(true);
mClickThroughPressed = false;
};
@@ -210,7 +211,8 @@
// 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 @@
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 @@
} 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 @@
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);
+ } 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())) {
- setWindowTouchable(false);
- mClickThroughPressX = event.getRawX();
- mClickThroughPressY = event.getRawY();
- mClickThroughPressed = true;
- mNavigationBarView.postDelayed(mClickThroughSendTap,
- CLICK_THROUGH_TAP_DELAY);
+ 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 @@
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 @@
}
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 @@
}
}
+ /**
+ * 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 @@
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 40f2392..974de4b 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 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 @@
*/
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 @@
@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 @@
@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/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index bf143c8..ee1e3c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -272,9 +272,8 @@
// AOD wallpapers should fade away after a while.
// Docking pulses may take a long time, wallpapers should also fade away after a while.
- if (mWallpaperSupportsAmbientMode && (
- mDozeParameters.getAlwaysOn() && mState == ScrimState.AOD
- || mState == ScrimState.PULSING && mCallback != null)) {
+ if (mWallpaperSupportsAmbientMode && mDozeParameters.getAlwaysOn()
+ && mState == ScrimState.AOD) {
if (!mWallpaperVisibilityTimedOut) {
mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(),
AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 11a2d32..0f85c18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -128,7 +128,8 @@
public void prepare(ScrimState previousState) {
mCurrentInFrontAlpha = 0f;
if (mPulseReason == DozeLog.PULSE_REASON_NOTIFICATION
- || mPulseReason == DozeLog.PULSE_REASON_DOCKING) {
+ || mPulseReason == DozeLog.PULSE_REASON_DOCKING
+ || mPulseReason == DozeLog.PULSE_REASON_INTENT) {
mCurrentBehindAlpha = previousState.getBehindAlpha();
mCurrentBehindTint = Color.BLACK;
} else {
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 ffaa236..86e17f3 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 @@
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 @@
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 @@
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 7881df9..1e09063 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 @@
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.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 @@
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 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 @@
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 1d2d6f7..45d215e 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.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 @@
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 0000000..d2b2654
--- /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 0000000..0cd6f9a
--- /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 e32d48d..49b4641 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 @@
// 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 @@
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 1bb7ef4..c0aac7e 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 @@
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 @@
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 5be991f..c3214040 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.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 @@
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 @@
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 @@
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/doze/DozeWallpaperStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
index ec2319d..9eb94f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
@@ -44,11 +44,12 @@
private DozeWallpaperState mDozeWallpaperState;
@Mock IWallpaperManager mIWallpaperManager;
@Mock DozeParameters mDozeParameters;
+ @Mock DozeMachine mMachine;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mDozeWallpaperState = new DozeWallpaperState(mIWallpaperManager, mDozeParameters);
+ mDozeWallpaperState = new DozeWallpaperState(mMachine, mIWallpaperManager, mDozeParameters);
}
@Test
@@ -108,14 +109,28 @@
}
@Test
- public void testTransitionTo_pulseIsAmbientMode() throws RemoteException {
+ public void testTransitionTo_notificationPulseIsAmbientMode() throws RemoteException {
+ when(mMachine.getPulseReason()).thenReturn(DozeLog.PULSE_REASON_NOTIFICATION);
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_REQUEST_PULSE,
DozeMachine.State.DOZE_PULSING);
verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(0L));
}
@Test
+ public void testTransitionTo_wakeFromPulseIsNotAmbientMode() throws RemoteException {
+ when(mMachine.getPulseReason()).thenReturn(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN);
+ mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD,
+ DozeMachine.State.DOZE_REQUEST_PULSE);
+ reset(mIWallpaperManager);
+
+ mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_REQUEST_PULSE,
+ DozeMachine.State.DOZE_PULSING);
+ verify(mIWallpaperManager).setInAmbientMode(eq(false), anyLong());
+ }
+
+ @Test
public void testTransitionTo_animatesWhenWakingUpFromPulse() throws RemoteException {
+ when(mMachine.getPulseReason()).thenReturn(DozeLog.PULSE_REASON_NOTIFICATION);
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_REQUEST_PULSE,
DozeMachine.State.DOZE_PULSING);
reset(mIWallpaperManager);
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 8503962..374a73c 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 @@
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 @@
// 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 f34e1a6..31cd280 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 @@
@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 @@
@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 382dde9..dbf00a3 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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 ed98c62..568ff55f 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 @@
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 5570122..6793eca 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 @@
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/packages/overlays/IconPackCircularSettingsOverlay/Android.mk b/packages/overlays/IconPackCircularSettingsOverlay/Android.mk
new file mode 100644
index 0000000..ad7324d
--- /dev/null
+++ b/packages/overlays/IconPackCircularSettingsOverlay/Android.mk
@@ -0,0 +1,31 @@
+#
+# 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := IconPackCircularSettings
+LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := IconPackCircularSettingsOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/AndroidManifest.xml b/packages/overlays/IconPackCircularSettingsOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..c1a5698
--- /dev/null
+++ b/packages/overlays/IconPackCircularSettingsOverlay/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.theme.icon_pack.circular.settings"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="com.android.settings" android:category="android.theme.customization.icon_pack.settings" android:priority="1"/>
+ <application android:label="Circular" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_apps.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_apps.xml
new file mode 100644
index 0000000..dbac48d
--- /dev/null
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_apps.xml
@@ -0,0 +1,54 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M6,4A2,2,0,0,0,6,8H7.5A0.5 0.5 ,0,0,0,8,7.5V6A2,2,0,0,0,6,4Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,8h1.5a0.5 0.5 ,0,0,0,0.5-0.5V6a2,2,0,1,0-2,2Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M20,6a2,2,0,1,0-2,2h1.5a0.5 0.5 ,0,0,0,0.5-0.5Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M6,14H7.5a0.5 0.5 ,0,0,0,0.5-0.5V12a2,2,0,1,0-2,2Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,14h1.5a0.5 0.5 ,0,0,0,0.5-0.5V12a2,2,0,1,0-2,2Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M20,12a2,2,0,1,0-2,2h1.5a0.5 0.5 ,0,0,0,0.5-0.5Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M6,20H7.5a0.5 0.5 ,0,0,0,0.5-0.5V18a2,2,0,1,0-2,2Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,20h1.5a0.5 0.5 ,0,0,0,0.5-0.5V18a2,2,0,1,0-2,2Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M20,18a2,2,0,1,0-2,2h1.5a0.5 0.5 ,0,0,0,0.5-0.5Z" />
+</vector>
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_devices_other.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_devices_other.xml
new file mode 100644
index 0000000..a407bd6
--- /dev/null
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_devices_other.xml
@@ -0,0 +1,36 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M2,16.5A2.5,2.5,0,0,0,4.5,19h2a0.5 0.5 ,0,0,0,0-1h-2A1.5,1.5,0,0,1,3,16.5v-9A1.5,1.5,0,0,1,4.5,6h17a0.5 0.5 ,0,0,0,0-1H4.5A2.5,2.5,0,0,0,2,7.5Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M14,16a3,3,0,1,0-3,3A3,3,0,0,0,14,16ZM9,16a2,2,0,1,1,2,2A2,2,0,0,1,9,16Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M22,17V10a2,2,0,0,0-2-2H17a2,2,0,0,0-2,2v7a2,2,0,0,0,2,2h3A2,2,0,0,0,22,17Zm-6,0V10a1,1,0,0,1,1-1h3a1,1,0,0,1,1,1v7a1,1,0,0,1-1,1H17A1,1,0,0,1,16,17Z" />
+</vector>
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_help.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_help.xml
new file mode 100644
index 0000000..c906847
--- /dev/null
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_help.xml
@@ -0,0 +1,36 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,22h0A10,10,0,0,0,22,12v0A10,10,0,1,0,12,22ZM12,3a9,9,0,1,1-9,9A9,9,0,0,1,12,3Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M8.62,9.65a0.5 0.5 ,0,0,0,0.61-0.37,2.94,2.94,0,0,1,5-1.41A2.64,2.64,0,0,1,15,10a2.27,2.27,0,0,1-1,1.69l-0.44 0.26 a3.21,3.21,0,0,0-1.91,2.47A0.49 0.49 ,0,0,0,12,15h0.08a0.5 0.5 ,0,0,0,0.49-0.42A2.25,2.25,0,0,1,14,12.81l0.5-0.29A3.27,3.27,0,0,0,16,10.09,3.62,3.62,0,0,0,14.9,7.16a4,4,0,0,0-5.6,0A4.06,4.06,0,0,0,8.25,9.05 0.5 0.5,0,0,0,8.62,9.65Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 12 16.25 C 12.4142135624 16.25 12.75 16.5857864376 12.75 17 C 12.75 17.4142135624 12.4142135624 17.75 12 17.75 C 11.5857864376 17.75 11.25 17.4142135624 11.25 17 C 11.25 16.5857864376 11.5857864376 16.25 12 16.25 Z" />
+</vector>
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_phone_info.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_phone_info.xml
new file mode 100644
index 0000000..f3419d4
--- /dev/null
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_phone_info.xml
@@ -0,0 +1,36 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M15,22a3,3,0,0,0,3-3V5a3,3,0,0,0-3-3H9A3,3,0,0,0,6,5V19a3,3,0,0,0,3,3ZM7,6H17V18H7ZM9,3h6a2,2,0,0,1,2,2H7A2,2,0,0,1,9,3ZM7,19H17a2,2,0,0,1-2,2H9A2,2,0,0,1,7,19Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,10.5a0.5 0.5 ,0,0,0-0.5 0.5 v4.5a0.5 0.5 ,0,0,0,1,0V11A0.5 0.5 ,0,0,0,12,10.5Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 12 8 C 12.4142135624 8 12.75 8.33578643763 12.75 8.75 C 12.75 9.16421356237 12.4142135624 9.5 12 9.5 C 11.5857864376 9.5 11.25 9.16421356237 11.25 8.75 C 11.25 8.33578643763 11.5857864376 8 12 8 Z" />
+</vector>
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accessibility.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accessibility.xml
new file mode 100644
index 0000000..8a8ddec
--- /dev/null
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accessibility.xml
@@ -0,0 +1,42 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M21,5.35A0.51 0.51 ,0,0,0,20.37,5,37.25,37.25,0,0,1,3.63,5,0.51 0.51 ,0,0,0,3,5.35 0.51 0.51,0,0,0,3.37,6,32.05,32.05,0,0,0,9,6.87V20a0.5 0.5 ,0,0,0,1,0V13.5h4V20a0.5 0.5 ,0,0,0,1,0V6.87A32.05,32.05,0,0,0,20.63,6,0.51 0.51 ,0,0,0,21,5.35Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 12 22 C 12.5522847498 22 13 22.4477152502 13 23 C 13 23.5522847498 12.5522847498 24 12 24 C 11.4477152502 24 11 23.5522847498 11 23 C 11 22.4477152502 11.4477152502 22 12 22 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 16 22 C 16.5522847498 22 17 22.4477152502 17 23 C 17 23.5522847498 16.5522847498 24 16 24 C 15.4477152502 24 15 23.5522847498 15 23 C 15 22.4477152502 15.4477152502 22 16 22 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 8 22 C 8.55228474983 22 9 22.4477152502 9 23 C 9 23.5522847498 8.55228474983 24 8 24 C 7.44771525017 24 7 23.5522847498 7 23 C 7 22.4477152502 7.44771525017 22 8 22 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 12 1 C 13.1045694997 1 14 1.89543050034 14 3 C 14 4.10456949966 13.1045694997 5 12 5 C 10.8954305003 5 10 4.10456949966 10 3 C 10 1.89543050034 10.8954305003 1 12 1 Z" />
+</vector>
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accounts.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accounts.xml
new file mode 100644
index 0000000..01fc4b9
--- /dev/null
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accounts.xml
@@ -0,0 +1,33 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,22A10,10,0,1,0,2,12,10,10,0,0,0,12,22Zm0-1a9,9,0,0,1-7-3.31,10.37,10.37,0,0,1,13.94,0A9,9,0,0,1,12,21ZM3,12a9,9,0,0,1,18,0,8.88,8.88,0,0,1-1.45,4.88,11.35,11.35,0,0,0-15.1,0A8.83,8.83,0,0,1,3,12Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M15.5,9.5A3.5,3.5,0,1,0,12,13,3.5,3.5,0,0,0,15.5,9.5Zm-6,0A2.5,2.5,0,1,1,12,12,2.5,2.5,0,0,1,9.5,9.5Z" />
+</vector>
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_battery_white.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_battery_white.xml
new file mode 100644
index 0000000..b43923f
--- /dev/null
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_battery_white.xml
@@ -0,0 +1,30 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M14,4a1,1,0,0,0-1-1H11a1,1,0,0,0-1,1H9A2,2,0,0,0,7,6V19a2,2,0,0,0,2,2h6a2,2,0,0,0,2-2V6a2,2,0,0,0-2-2Z" />
+</vector>
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_display_white.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_display_white.xml
new file mode 100644
index 0000000..580271b
--- /dev/null
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_display_white.xml
@@ -0,0 +1,33 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M3.41,14.72A2,2,0,0,1,4,16.14V18a2,2,0,0,0,2,2H7.86a2,2,0,0,1,1.42 0.59 l1.31,1.31a2,2,0,0,0,2.82,0l1.31-1.31A2,2,0,0,1,16.14,20H18a2,2,0,0,0,2-2V16.14a2,2,0,0,1,0.59-1.42l1.31-1.31a2,2,0,0,0,0-2.82L20.59,9.28A2,2,0,0,1,20,7.86V6a2,2,0,0,0-2-2H16.14a2,2,0,0,1-1.42-0.59L13.41,2.1a2,2,0,0,0-2.82,0L9.28,3.41A2,2,0,0,1,7.86,4H6A2,2,0,0,0,4,6V7.86a2,2,0,0,1-0.59,1.42L2.1,10.59a2,2,0,0,0,0,2.82Zm-0.6-3.43L4.12,10A3,3,0,0,0,5,7.86V6A1,1,0,0,1,6,5H7.86A3,3,0,0,0,10,4.12l1.31-1.31a1,1,0,0,1,1.42,0L14,4.12A3,3,0,0,0,16.14,5H18a1,1,0,0,1,1,1V7.86A3,3,0,0,0,19.88,10l1.31,1.31a1,1,0,0,1,0,1.42L19.88,14A3,3,0,0,0,19,16.14V18a1,1,0,0,1-1,1H16.14a3,3,0,0,0-2.12 0.88 l-1.31,1.31a1,1,0,0,1-1.42,0L10,19.88A3,3,0,0,0,7.86,19H6a1,1,0,0,1-1-1V16.14A3,3,0,0,0,4.12,14L2.81,12.71a1,1,0,0,1,0-1.42Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M17,12a5,5,0,0,0-5-5V17A5,5,0,0,0,17,12Z" />
+</vector>
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_location.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_location.xml
new file mode 100644
index 0000000..37d5576
--- /dev/null
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_location.xml
@@ -0,0 +1,33 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,21.5s7-5.34,7-11.25A7.13,7.13,0,0,0,12,3a7.13,7.13,0,0,0-7,7.25C5,16.16,12,21.5,12,21.5ZM12,4a6.13,6.13,0,0,1,6,6.25c0,4.37-4.37,8.54-6,10-1.63-1.4-6-5.57-6-9.95A6.13,6.13,0,0,1,12,4Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M15,10a3,3,0,1,0-3,3A3,3,0,0,0,15,10Zm-5,0a2,2,0,1,1,2,2A2,2,0,0,1,10,10Z" />
+</vector>
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_privacy.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_privacy.xml
new file mode 100644
index 0000000..32f9e53
--- /dev/null
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_privacy.xml
@@ -0,0 +1,36 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,17a9.48,9.48,0,0,1-8.92-5.5A9.48,9.48,0,0,1,12,6a9.5,9.5,0,0,1,8.65,5h1.13A10.5,10.5,0,0,0,12,5,10.47,10.47,0,0,0,2,11.5,10.47,10.47,0,0,0,12,18a11.48,11.48,0,0,0,4-0.7V16.22A10.48,10.48,0,0,1,12,17Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,8a3.5,3.5,0,1,0,3.5,3.5A3.5,3.5,0,0,0,12,8Zm0,6a2.5,2.5,0,1,1,2.5-2.5A2.5,2.5,0,0,1,12,14Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M22,15V14a2,2,0,0,0-4,0v1a1,1,0,0,0-1,1v3a1,1,0,0,0,1,1h4a1,1,0,0,0,1-1V16A1,1,0,0,0,22,15Zm-3,0V14a1,1,0,0,1,2,0v1Z" />
+</vector>
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_security_white.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_security_white.xml
new file mode 100644
index 0000000..71d427a
--- /dev/null
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_security_white.xml
@@ -0,0 +1,33 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,12a1.5,1.5,0,0,0-0.5,2.91V16.5a0.5 0.5 ,0,0,0,1,0V14.91A1.5,1.5,0,0,0,12,12Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M23,5a4,4,0,0,0-8,0V8H6A1,1,0,0,0,5,9v9a3,3,0,0,0,3,3h8a3,3,0,0,0,3-3V9a1,1,0,0,0-1-1H16V5a3,3,0,0,1,6,0,0.5 0.5 ,0,0,0,1,0ZM18,9v9a2,2,0,0,1-2,2H8a2,2,0,0,1-2-2V9H18Z" />
+</vector>
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
new file mode 100644
index 0000000..f4b29ae
--- /dev/null
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
@@ -0,0 +1,36 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,22A10,10,0,1,0,2,12,10,10,0,0,0,12,22ZM12,3a9,9,0,1,1-9,9A9,9,0,0,1,12,3Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,11a0.5 0.5 ,0,0,0-0.5 0.5 v5a0.5 0.5 ,0,0,0,1,0v-5A0.5 0.5 ,0,0,0,12,11Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 12 7 C 12.4142135624 7 12.75 7.33578643763 12.75 7.75 C 12.75 8.16421356237 12.4142135624 8.5 12 8.5 C 11.5857864376 8.5 11.25 8.16421356237 11.25 7.75 C 11.25 7.33578643763 11.5857864376 7 12 7 Z" />
+</vector>
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_wireless_white.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_wireless_white.xml
new file mode 100644
index 0000000..85abfff
--- /dev/null
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_wireless_white.xml
@@ -0,0 +1,39 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 11.99 16.5 C 12.8184271247 16.5 13.49 17.1715728753 13.49 18 C 13.49 18.8284271247 12.8184271247 19.5 11.99 19.5 C 11.1615728753 19.5 10.49 18.8284271247 10.49 18 C 10.49 17.1715728753 11.1615728753 16.5 11.99 16.5 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M18.76,11.8a0.54 0.54 ,0,0,0,0.36-0.14 0.51 0.51,0,0,0,0-0.71A10.08,10.08,0,0,0,4.87,11a0.5 0.5 ,0,0,0,0,0.71 0.51 0.51,0,0,0,0.71,0,9.07,9.07,0,0,1,12.83,0A0.54 0.54 ,0,0,0,18.76,11.8Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M2.15,8.15a0.49 0.49 ,0,0,0,0.7 0.7 ,12.8,12.8,0,0,1,18.3,0,0.48 0.48 ,0,0,0,0.7,0,0.48 0.48 ,0,0,0,0-0.7A13.77,13.77,0,0,0,2.15,8.15Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M15.93,14.64a0.49 0.49 ,0,0,0,0.35-0.15 0.5 0.5,0,0,0,0-0.71,6.08,6.08,0,0,0-8.58,0,0.51 0.51 ,0,0,0,0,0.71 0.5 0.5,0,0,0,0.71,0,5.07,5.07,0,0,1,7.16,0A0.51 0.51 ,0,0,0,15.93,14.64Z" />
+</vector>
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_storage_white.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_storage_white.xml
new file mode 100644
index 0000000..ea7a97f
--- /dev/null
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_storage_white.xml
@@ -0,0 +1,45 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M3,5V7A1,1,0,0,0,4,8H20a1,1,0,0,0,1-1V5a2,2,0,0,0-2-2H5A2,2,0,0,0,3,5ZM20,5V7H4V5A1,1,0,0,1,5,4H19A1,1,0,0,1,20,5Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M3,19a1,1,0,0,0,1,1H20a1,1,0,0,0,1-1V17a2,2,0,0,0-2-2H5a2,2,0,0,0-2,2Zm1-2a1,1,0,0,1,1-1H19a1,1,0,0,1,1,1v2H4Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M3,13a1,1,0,0,0,1,1H20a1,1,0,0,0,1-1V11a2,2,0,0,0-2-2H5a2,2,0,0,0-2,2Zm1-2a1,1,0,0,1,1-1H19a1,1,0,0,1,1,1v2H4Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 6.01 4.75 C 6.42421356237 4.75 6.76 5.08578643763 6.76 5.5 C 6.76 5.91421356237 6.42421356237 6.25 6.01 6.25 C 5.59578643763 6.25 5.26 5.91421356237 5.26 5.5 C 5.26 5.08578643763 5.59578643763 4.75 6.01 4.75 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 6.01 10.75 C 6.42421356237 10.75 6.76 11.0857864376 6.76 11.5 C 6.76 11.9142135624 6.42421356237 12.25 6.01 12.25 C 5.59578643763 12.25 5.26 11.9142135624 5.26 11.5 C 5.26 11.0857864376 5.59578643763 10.75 6.01 10.75 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 6.01 16.75 C 6.42421356237 16.75 6.76 17.0857864376 6.76 17.5 C 6.76 17.9142135624 6.42421356237 18.25 6.01 18.25 C 5.59578643763 18.25 5.26 17.9142135624 5.26 17.5 C 5.26 17.0857864376 5.59578643763 16.75 6.01 16.75 Z" />
+</vector>
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_volume_up_24dp.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
new file mode 100644
index 0000000..7ed248d
--- /dev/null
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
@@ -0,0 +1,36 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M13.79,13.79a0.5 0.5 ,0,0,0,0.21,1,0.54 0.54 ,0,0,0,0.21-0.05,2.92,2.92,0,0,0,0-5.39 0.49 0.49,0,0,0-0.66 0.24 0.5 0.5 ,0,0,0,0.24 0.67 ,1.93,1.93,0,0,1,0,3.58Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M13.89,17.67a0.5 0.5 ,0,0,0,0.11,1l0.11,0a6.78,6.78,0,0,0,0-13.28 0.5 0.5,0,1,0-0.22,1,5.79,5.79,0,0,1,0,11.34Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M5,15H7l4.15,4.15a0.47 0.47 ,0,0,0,0.35 0.14 0.5 0.5 ,0,0,0,0.5-0.5V5.21a0.5 0.5 ,0,0,0-0.5-0.5 0.47 0.47,0,0,0-0.35 0.14 L7,9H5a2,2,0,0,0-2,2v2A2,2,0,0,0,5,15ZM4,11a1,1,0,0,1,1-1H7.41l0.3-0.29L11,6.41V17.59l-3.29-3.3L7.41,14H5a1,1,0,0,1-1-1Z" />
+</vector>
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_wifi_tethering.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_wifi_tethering.xml
new file mode 100644
index 0000000..10c1592
--- /dev/null
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_wifi_tethering.xml
@@ -0,0 +1,36 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 12 11 C 12.8284271247 11 13.5 11.6715728753 13.5 12.5 C 13.5 13.3284271247 12.8284271247 14 12 14 C 11.1715728753 14 10.5 13.3284271247 10.5 12.5 C 10.5 11.6715728753 11.1715728753 11 12 11 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M15.18,16.39a0.51 0.51 ,0,0,0,0.71,0,5.5,5.5,0,0,0,0-7.78,5.52,5.52,0,0,0-7.78,0,5.5,5.5,0,0,0,0,7.78 0.5 0.5,0,0,0,0.35 0.15 0.51 0.51 ,0,0,0,0.36-0.15 0.51 0.51,0,0,0,0-0.71,4.5,4.5,0,1,1,6.36,0A0.51 0.51 ,0,0,0,15.18,16.39Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M18,19.22a0.49 0.49 ,0,0,0,0.35 0.14 0.5 0.5 ,0,0,0,0.36-0.14,9.5,9.5,0,1,0-13.44,0,0.51 0.51 ,0,0,0,0.71,0,0.5 0.5 ,0,0,0,0-0.71,8.5,8.5,0,0,1,12-12,8.5,8.5,0,0,1,0,12A0.5 0.5 ,0,0,0,18,19.22Z" />
+</vector>
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/Android.mk b/packages/overlays/IconPackFilledSettingsOverlay/Android.mk
new file mode 100644
index 0000000..0443560
--- /dev/null
+++ b/packages/overlays/IconPackFilledSettingsOverlay/Android.mk
@@ -0,0 +1,31 @@
+#
+# 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := IconPackFilledSettings
+LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := IconPackFilledSettingsOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/AndroidManifest.xml b/packages/overlays/IconPackFilledSettingsOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..de81e21
--- /dev/null
+++ b/packages/overlays/IconPackFilledSettingsOverlay/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.theme.icon_pack.filled.settings"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="com.android.settings" android:category="android.theme.customization.icon_pack.settings" android:priority="1"/>
+ <application android:label="Filled" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_apps.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_apps.xml
new file mode 100644
index 0000000..015e73e
--- /dev/null
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_apps.xml
@@ -0,0 +1,52 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 4.5 4 L 7.5 4 Q 8 4 8 4.5 L 8 7.5 Q 8 8 7.5 8 L 4.5 8 Q 4 8 4 7.5 L 4 4.5 Q 4 4 4.5 4 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 10.5 4 L 13.5 4 Q 14 4 14 4.5 L 14 7.5 Q 14 8 13.5 8 L 10.5 8 Q 10 8 10 7.5 L 10 4.5 Q 10 4 10.5 4 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 16.5 4 L 19.5 4 Q 20 4 20 4.5 L 20 7.5 Q 20 8 19.5 8 L 16.5 8 Q 16 8 16 7.5 L 16 4.5 Q 16 4 16.5 4 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 4.5 10 L 7.5 10 Q 8 10 8 10.5 L 8 13.5 Q 8 14 7.5 14 L 4.5 14 Q 4 14 4 13.5 L 4 10.5 Q 4 10 4.5 10 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 10.5 10 L 13.5 10 Q 14 10 14 10.5 L 14 13.5 Q 14 14 13.5 14 L 10.5 14 Q 10 14 10 13.5 L 10 10.5 Q 10 10 10.5 10 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 16.5 10 L 19.5 10 Q 20 10 20 10.5 L 20 13.5 Q 20 14 19.5 14 L 16.5 14 Q 16 14 16 13.5 L 16 10.5 Q 16 10 16.5 10 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 4.5 16 L 7.5 16 Q 8 16 8 16.5 L 8 19.5 Q 8 20 7.5 20 L 4.5 20 Q 4 20 4 19.5 L 4 16.5 Q 4 16 4.5 16 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 10.5 16 L 13.5 16 Q 14 16 14 16.5 L 14 19.5 Q 14 20 13.5 20 L 10.5 20 Q 10 20 10 19.5 L 10 16.5 Q 10 16 10.5 16 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 16.5 16 L 19.5 16 Q 20 16 20 16.5 L 20 19.5 Q 20 20 19.5 20 L 16.5 20 Q 16 20 16 19.5 L 16 16.5 Q 16 16 16.5 16 Z" />
+</vector>
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_devices_other.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_devices_other.xml
new file mode 100644
index 0000000..0b12655
--- /dev/null
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_devices_other.xml
@@ -0,0 +1,34 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M3,20H6a1,1,0,0,0,0-2H3V6H20a1,1,0,0,0,0-2H3A2,2,0,0,0,1,6V18A2,2,0,0,0,3,20Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M23,19V9a1.08,1.08,0,0,0-1-1H16a1.08,1.08,0,0,0-1,1V19a1.08,1.08,0,0,0,1,1h6A1.08,1.08,0,0,0,23,19Zm-2-1H17V10h4Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M9,12v1.78a3,3,0,0,0,0,4.44V20h4V18.22a3,3,0,0,0,0-4.44V12Zm2,5.5A1.5,1.5,0,1,1,12.5,16,1.5,1.5,0,0,1,11,17.5Z" />
+</vector>
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_help.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_help.xml
new file mode 100644
index 0000000..097894d
--- /dev/null
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_help.xml
@@ -0,0 +1,28 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,2A10,10,0,1,0,22,12,10,10,0,0,0,12,2Zm0,16.81a1.3,1.3,0,1,1,1.3-1.3A1.3,1.3,0,0,1,12,18.81Zm1.07-4.62a1,1,0,0,1-1.13 0.79 ,1,1,0,0,1-0.83-1.23c0.52-2.61,2.66-2.84,2.87-4.5a2,2,0,0,0-1.34-2.17,2,2,0,0,0-2.55,1.37,1,1,0,1,1-1.93-0.53A4,4,0,0,1,16,9.13C15.92,11.57,13.5,11.74,13.07,14.19Z" />
+</vector>
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_phone_info.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_phone_info.xml
new file mode 100644
index 0000000..80f3d1e7
--- /dev/null
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_phone_info.xml
@@ -0,0 +1,34 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M7,1A2,2,0,0,0,5,3V21a2,2,0,0,0,2,2H17a2,2,0,0,0,2-2V3a2,2,0,0,0-2-2ZM17,19H7V5H17Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,11.62a1,1,0,0,0-1,0.9v3.59a1,1,0,0,0,2,0V12.52A1,1,0,0,0,12,11.62Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 12 7.06 C 12.6903559373 7.06 13.25 7.61964406271 13.25 8.31 C 13.25 9.00035593729 12.6903559373 9.56 12 9.56 C 11.3096440627 9.56 10.75 9.00035593729 10.75 8.31 C 10.75 7.61964406271 11.3096440627 7.06 12 7.06 Z" />
+</vector>
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accessibility.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accessibility.xml
new file mode 100644
index 0000000..9c8287b
--- /dev/null
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accessibility.xml
@@ -0,0 +1,40 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M20.76,5l0-0.06a1,1,0,0,0-1.2-0.72A35.66,35.66,0,0,1,12,5a35.66,35.66,0,0,1-7.54-0.76A1,1,0,0,0,3.26,5l0,0.06A1,1,0,0,0,4,6.24,37,37,0,0,0,9,7V19a1,1,0,0,0,2,0V14h2v5a1,1,0,0,0,2,0V7a37,37,0,0,0,5-0.76A1,1,0,0,0,20.76,5Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 12 0 C 13.1045694997 0 14 0.895430500338 14 2 C 14 3.10456949966 13.1045694997 4 12 4 C 10.8954305003 4 10 3.10456949966 10 2 C 10 0.895430500338 10.8954305003 0 12 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 12 22 C 12.5522847498 22 13 22.4477152502 13 23 C 13 23.5522847498 12.5522847498 24 12 24 C 11.4477152502 24 11 23.5522847498 11 23 C 11 22.4477152502 11.4477152502 22 12 22 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 16 22 C 16.5522847498 22 17 22.4477152502 17 23 C 17 23.5522847498 16.5522847498 24 16 24 C 15.4477152502 24 15 23.5522847498 15 23 C 15 22.4477152502 15.4477152502 22 16 22 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 8 22 C 8.55228474983 22 9 22.4477152502 9 23 C 9 23.5522847498 8.55228474983 24 8 24 C 7.44771525017 24 7 23.5522847498 7 23 C 7 22.4477152502 7.44771525017 22 8 22 Z" />
+</vector>
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accounts.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accounts.xml
new file mode 100644
index 0000000..ea418a8
--- /dev/null
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accounts.xml
@@ -0,0 +1,31 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,22A10,10,0,1,0,2,12,10,10,0,0,0,12,22ZM12,4a8,8,0,0,1,6.36,12.83c-1.43-1.74-4.9-2.33-6.36-2.33s-4.93 0.59 -6.36,2.33A8,8,0,0,1,12,4Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 12 6 C 13.9329966244 6 15.5 7.56700337559 15.5 9.5 C 15.5 11.4329966244 13.9329966244 13 12 13 C 10.0670033756 13 8.5 11.4329966244 8.5 9.5 C 8.5 7.56700337559 10.0670033756 6 12 6 Z" />
+</vector>
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_battery_white.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_battery_white.xml
new file mode 100644
index 0000000..1c5df00
--- /dev/null
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_battery_white.xml
@@ -0,0 +1,28 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M10,2V4H8.33A1.34,1.34,0,0,0,7,5.33V20.66A1.34,1.34,0,0,0,8.33,22h7.33A1.34,1.34,0,0,0,17,20.67V5.33A1.34,1.34,0,0,0,15.67,4H14V2Z" />
+</vector>
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_display_white.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_display_white.xml
new file mode 100644
index 0000000..02e15d2
--- /dev/null
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_display_white.xml
@@ -0,0 +1,28 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M4,15.3V19a1,1,0,0,0,1,1H8.69l2.6,2.6a1,1,0,0,0,1.41,0L15.3,20H19a1,1,0,0,0,1-1V15.31l2.6-2.6a1,1,0,0,0,0-1.41L20,8.69V5a1,1,0,0,0-1-1H15.31l-2.6-2.6a1,1,0,0,0-1.41,0L8.69,4H5A1,1,0,0,0,4,5V8.69l-2.6,2.6a1,1,0,0,0,0,1.41ZM12,6a6,6,0,0,1,0,12Z" />
+</vector>
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_location.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_location.xml
new file mode 100644
index 0000000..818236b
--- /dev/null
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_location.xml
@@ -0,0 +1,28 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,2A7,7,0,0,0,5,9c0,4.17,4.42,9.92,6.24,12.11a1,1,0,0,0,1.53,0C14.58,18.92,19,13.17,19,9A7,7,0,0,0,12,2Zm0,9.5A2.5,2.5,0,1,1,14.5,9,2.5,2.5,0,0,1,12,11.5Z" />
+</vector>
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_privacy.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_privacy.xml
new file mode 100644
index 0000000..b080882
--- /dev/null
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_privacy.xml
@@ -0,0 +1,34 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,19a12,12,0,0,0,2-0.17v-2A10.13,10.13,0,0,1,12,17a9.77,9.77,0,0,1-8.82-5.5,9.82,9.82,0,0,1,17.64,0H23a11.82,11.82,0,0,0-22,0A11.83,11.83,0,0,0,12,19Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M16.5,11.5a4.55,4.55,0,1,0-2.38,3.95,5,5,0,0,1,2.31-3.22A4.4,4.4,0,0,0,16.5,11.5ZM12,14.2a2.7,2.7,0,1,1,2.7-2.7A2.7,2.7,0,0,1,12,14.2Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M23,21V17a1.08,1.08,0,0,0-1-1v-0.5a2.5,2.5,0,0,0-5,0V16a1.08,1.08,0,0,0-1,1v4a1.08,1.08,0,0,0,1,1h5A1.08,1.08,0,0,0,23,21Zm-2.5-5h-2v-0.5a1,1,0,0,1,2,0Z" />
+</vector>
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_security_white.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_security_white.xml
new file mode 100644
index 0000000..39ac0d7
--- /dev/null
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_security_white.xml
@@ -0,0 +1,28 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M23,6a1,1,0,0,0,0.92-1.2A5,5,0,0,0,18.57,1a5.15,5.15,0,0,0-4.51,5.19V8H6a2,2,0,0,0-2,2V20a2,2,0,0,0,2,2H18a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2H16V6.12A3.18,3.18,0,0,1,18.44,3a3.1,3.1,0,0,1,3.62,2.27A1,1,0,0,0,23,6ZM12,17a2,2,0,1,1,2-2A2,2,0,0,1,12,17Z" />
+</vector>
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
new file mode 100644
index 0000000..3c29998
--- /dev/null
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
@@ -0,0 +1,28 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M2,12A10,10,0,1,0,12,2,10,10,0,0,0,2,12Zm11,5.42a1,1,0,0,1-2,0V10.68a1,1,0,0,1,2,0ZM12,5.58a1.35,1.35,0,1,1-1.35,1.35A1.34,1.34,0,0,1,12,5.58Z" />
+</vector>
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_wireless_white.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_wireless_white.xml
new file mode 100644
index 0000000..8fa846e
--- /dev/null
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_wireless_white.xml
@@ -0,0 +1,34 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M11.29,19.29a1,1,0,0,0,1.42,0L14,18a1,1,0,0,0-0.22-1.58A3.92,3.92,0,0,0,12,16a4,4,0,0,0-1.77 0.41 A1,1,0,0,0,10,18Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M17.6,14.39l0.71-0.71a1,1,0,0,0-0.08-1.49,10,10,0,0,0-12.44,0,1,1,0,0,0-0.08,1.49l0.7 0.72 a1,1,0,0,0,1.32 0.08 A6.91,6.91,0,0,1,12,13a7,7,0,0,1,4.29,1.47A1,1,0,0,0,17.6,14.39Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M21.83,10.16l0.71-0.71A1,1,0,0,0,22.48,8a15.79,15.79,0,0,0-20.92,0,1,1,0,0,0-0.07,1.47l0.71 0.71 a1,1,0,0,0,1.35 0.07 ,12.79,12.79,0,0,1,16.94,0A1,1,0,0,0,21.83,10.16Z" />
+</vector>
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_storage_white.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_storage_white.xml
new file mode 100644
index 0000000..82733b6
--- /dev/null
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_storage_white.xml
@@ -0,0 +1,34 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M4,14H20a2,2,0,0,0,0-4H4a2,2,0,0,0,0,4Zm1-3.1A1.1,1.1,0,1,1,3.9,12,1.1,1.1,0,0,1,5,10.9Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M4,8H20a2,2,0,0,0,0-4H4A2,2,0,0,0,4,8ZM5,4.9A1.1,1.1,0,1,1,3.9,6,1.1,1.1,0,0,1,5,4.9Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M4,20H20a2,2,0,0,0,0-4H4a2,2,0,0,0,0,4Zm1-3.1A1.1,1.1,0,1,1,3.9,18,1.1,1.1,0,0,1,5,16.9Z" />
+</vector>
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_volume_up_24dp.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
new file mode 100644
index 0000000..9587e54
--- /dev/null
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
@@ -0,0 +1,34 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M10.29,5.7,7,9H4a1,1,0,0,0-1,1v4a1,1,0,0,0,1,1H7l3.29,3.29A1,1,0,0,0,12,17.58V6.41A1,1,0,0,0,10.29,5.7Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M16.5,12A4.5,4.5,0,0,0,14,8V16A4.47,4.47,0,0,0,16.5,12Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M14,19.54a0.91 0.91 ,0,0,0,1.22 0.86 ,9,9,0,0,0,0-16.8A0.91 0.91 ,0,0,0,14,4.46v0.19a0.92 0.92 ,0,0,0,0.61 0.85 ,7,7,0,0,1,0,13,0.92 0.92 ,0,0,0-0.61 0.85 Z" />
+</vector>
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_wifi_tethering.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_wifi_tethering.xml
new file mode 100644
index 0000000..84c347b
--- /dev/null
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_wifi_tethering.xml
@@ -0,0 +1,34 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M15.46,16.46h0A1,1,0,0,0,17,16.35,5.9,5.9,0,0,0,18,13,6,6,0,1,0,7,16.35a1,1,0,0,0,1.51 0.11 h0a1,1,0,0,0,0.11-1.29A3.9,3.9,0,0,1,8,12.44a4,4,0,1,1,7.32,2.73A1,1,0,0,0,15.46,16.46Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M10.86,3.06A10,10,0,0,0,4.19,19.25a1,1,0,0,0,1.49 0.07 A1,1,0,0,0,5.75,18a8.05,8.05,0,0,1-1.59-6.61A8,8,0,0,1,20,13a7.89,7.89,0,0,1-1.77,5,1,1,0,0,0,0.08,1.31h0a1,1,0,0,0,1.49-0.07A9.9,9.9,0,0,0,22,13,10,10,0,0,0,10.86,3.06Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M10,13a2,2,0,1,0,2-2A2,2,0,0,0,10,13Z" />
+</vector>
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/Android.mk b/packages/overlays/IconPackRoundedSettingsOverlay/Android.mk
new file mode 100644
index 0000000..44ac6dd
--- /dev/null
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/Android.mk
@@ -0,0 +1,31 @@
+#
+# 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := IconPackRoundedSettings
+LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := IconPackRoundedSettingsOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/AndroidManifest.xml b/packages/overlays/IconPackRoundedSettingsOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..df71e15
--- /dev/null
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.theme.icon_pack.rounded.settings"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="com.android.settings" android:category="android.theme.customization.icon_pack.settings" android:priority="1"/>
+ <application android:label="Rounded" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_apps.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_apps.xml
new file mode 100644
index 0000000..62acfc6
--- /dev/null
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_apps.xml
@@ -0,0 +1,52 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 5.5 4 L 6.5 4 Q 8 4 8 5.5 L 8 6.5 Q 8 8 6.5 8 L 5.5 8 Q 4 8 4 6.5 L 4 5.5 Q 4 4 5.5 4 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 11.5 4 L 12.5 4 Q 14 4 14 5.5 L 14 6.5 Q 14 8 12.5 8 L 11.5 8 Q 10 8 10 6.5 L 10 5.5 Q 10 4 11.5 4 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 17.5 4 L 18.5 4 Q 20 4 20 5.5 L 20 6.5 Q 20 8 18.5 8 L 17.5 8 Q 16 8 16 6.5 L 16 5.5 Q 16 4 17.5 4 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 5.5 10 L 6.5 10 Q 8 10 8 11.5 L 8 12.5 Q 8 14 6.5 14 L 5.5 14 Q 4 14 4 12.5 L 4 11.5 Q 4 10 5.5 10 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 11.5 10 L 12.5 10 Q 14 10 14 11.5 L 14 12.5 Q 14 14 12.5 14 L 11.5 14 Q 10 14 10 12.5 L 10 11.5 Q 10 10 11.5 10 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 17.5 10 L 18.5 10 Q 20 10 20 11.5 L 20 12.5 Q 20 14 18.5 14 L 17.5 14 Q 16 14 16 12.5 L 16 11.5 Q 16 10 17.5 10 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 5.5 16 L 6.5 16 Q 8 16 8 17.5 L 8 18.5 Q 8 20 6.5 20 L 5.5 20 Q 4 20 4 18.5 L 4 17.5 Q 4 16 5.5 16 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 11.5 16 L 12.5 16 Q 14 16 14 17.5 L 14 18.5 Q 14 20 12.5 20 L 11.5 20 Q 10 20 10 18.5 L 10 17.5 Q 10 16 11.5 16 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 17.5 16 L 18.5 16 Q 20 16 20 17.5 L 20 18.5 Q 20 20 18.5 20 L 17.5 20 Q 16 20 16 18.5 L 16 17.5 Q 16 16 17.5 16 Z" />
+</vector>
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_devices_other.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_devices_other.xml
new file mode 100644
index 0000000..be7f297
--- /dev/null
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_devices_other.xml
@@ -0,0 +1,34 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M22,18V10a1.5,1.5,0,0,0-1.5-1.5h-4A1.5,1.5,0,0,0,15,10v8a1.5,1.5,0,0,0,1.5,1.5h4A1.5,1.5,0,0,0,22,18Zm-5.5-8h4v8h-4Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M13,17a2.5,2.5,0,1,0-2.5,2.5A2.5,2.5,0,0,0,13,17ZM9.5,17a1,1,0,1,1,1,1A1,1,0,0,1,9.5,17Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M21.25,4H3.5A1.5,1.5,0,0,0,2,5.5V18a1.5,1.5,0,0,0,1.5,1.5H5.25a0.75 0.75 ,0,0,0,0-1.5H3.5V5.5H21.25a0.75 0.75 ,0,0,0,0-1.5Z" />
+</vector>
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_help.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_help.xml
new file mode 100644
index 0000000..e8c3e47
--- /dev/null
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_help.xml
@@ -0,0 +1,34 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,22h0A10,10,0,0,0,22,12v0A10,10,0,1,0,12,22ZM12,3.5A8.51,8.51,0,0,1,20.5,12h0.75l-0.75,0A8.49,8.49,0,0,1,12,20.5h0a8.5,8.5,0,0,1,0-17Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M8.67,10a0.77 0.77 ,0,0,0,0.91-0.56,2.48,2.48,0,0,1,0.65-1.19,2.57,2.57,0,0,1,3.54,0A2.2,2.2,0,0,1,14.43,10a1.81,1.81,0,0,1-0.84,1.37c-0.13 0.09 -0.26 0.16 -0.4 0.24 a3.3,3.3,0,0,0-1.93,2.51 0.76 0.76,0,0,0,0.62 0.87 H12a0.75 0.75 ,0,0,0,0.74-0.62,1.84,1.84,0,0,1,1.19-1.46l0.49-0.29a3.32,3.32,0,0,0,1.5-2.48,3.71,3.71,0,0,0-1.09-3,4.1,4.1,0,0,0-5.66,0,4,4,0,0,0-1.05,1.9A0.75 0.75 ,0,0,0,8.67,10Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 12 16 C 12.5522847498 16 13 16.4477152502 13 17 C 13 17.5522847498 12.5522847498 18 12 18 C 11.4477152502 18 11 17.5522847498 11 17 C 11 16.4477152502 11.4477152502 16 12 16 Z" />
+</vector>
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_phone_info.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_phone_info.xml
new file mode 100644
index 0000000..e5e57d4
--- /dev/null
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_phone_info.xml
@@ -0,0 +1,34 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M8,1A3,3,0,0,0,5,4V20a3,3,0,0,0,3,3h8a3,3,0,0,0,3-3V4a3,3,0,0,0-3-3Zm8,20.5H8A1.5,1.5,0,0,1,6.5,20h11A1.5,1.5,0,0,1,16,21.5Zm1.5-3H6.5V5.5h11ZM17.5,4H6.5A1.5,1.5,0,0,1,8,2.5h8A1.5,1.5,0,0,1,17.5,4Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,11.25a0.76 0.76 ,0,0,0-0.75 0.75 v4a0.75 0.75 ,0,0,0,1.5,0V12A0.76 0.76 ,0,0,0,12,11.25Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 12 7 C 12.5522847498 7 13 7.44771525017 13 8 C 13 8.55228474983 12.5522847498 9 12 9 C 11.4477152502 9 11 8.55228474983 11 8 C 11 7.44771525017 11.4477152502 7 12 7 Z" />
+</vector>
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accessibility.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accessibility.xml
new file mode 100644
index 0000000..1ac58b5
--- /dev/null
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accessibility.xml
@@ -0,0 +1,40 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M14.25,2.5A2.25,2.25,0,1,0,12,4.75,2.25,2.25,0,0,0,14.25,2.5Zm-3,0a0.75 0.75 ,0,1,1,0.75 0.75 A0.76 0.76 ,0,0,1,11.25,2.5Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M20.72,5.28a0.77 0.77 ,0,0,0-0.94-0.5,29.53,29.53,0,0,1-7.78,1,29.72,29.72,0,0,1-7.78-1,0.75 0.75 ,0,0,0-0.44,1.44A28.14,28.14,0,0,0,9,7.12V19a0.75 0.75 ,0,0,0,1.5,0V14h3v5A0.75 0.75 ,0,0,0,15,19V7.12a28.14,28.14,0,0,0,5.22-0.9A0.76 0.76 ,0,0,0,20.72,5.28Zm-7.22,2V12.5h-3V7.25Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 8 22 C 8.55228474983 22 9 22.4477152502 9 23 C 9 23.5522847498 8.55228474983 24 8 24 C 7.44771525017 24 7 23.5522847498 7 23 C 7 22.4477152502 7.44771525017 22 8 22 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 12 22 C 12.5522847498 22 13 22.4477152502 13 23 C 13 23.5522847498 12.5522847498 24 12 24 C 11.4477152502 24 11 23.5522847498 11 23 C 11 22.4477152502 11.4477152502 22 12 22 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 16 22 C 16.5522847498 22 17 22.4477152502 17 23 C 17 23.5522847498 16.5522847498 24 16 24 C 15.4477152502 24 15 23.5522847498 15 23 C 15 22.4477152502 15.4477152502 22 16 22 Z" />
+</vector>
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accounts.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accounts.xml
new file mode 100644
index 0000000..90da97f
--- /dev/null
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accounts.xml
@@ -0,0 +1,31 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M6.21,20.14l0.08 0.06 A10,10,0,0,0,12,22h0a10,10,0,0,0,5.76-1.84h0A10,10,0,0,0,22,12v0A10,10,0,1,0,6.21,20.14Zm5.8 0.36 h0a8.45,8.45,0,0,1-4.5-1.3V15.75A0.76 0.76 ,0,0,1,8.25,15h7.5a0.76 0.76 ,0,0,1,0.75 0.75 V19.2A8.39,8.39,0,0,1,12,20.5Zm0-17A8.51,8.51,0,0,1,20.5,12h0.75l-0.75,0A8.47,8.47,0,0,1,18,18V15.75a2.25,2.25,0,0,0-2.25-2.25H8.25A2.25,2.25,0,0,0,6,15.75V18A8.49,8.49,0,0,1,12,3.5Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,12h0A3,3,0,1,0,9,9H9A3,3,0,0,0,12,12ZM10.5,9A1.5,1.5,0,1,1,12,10.5h0A1.5,1.5,0,0,1,10.5,9Z" />
+</vector>
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_battery_white.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_battery_white.xml
new file mode 100644
index 0000000..3f3b95a
--- /dev/null
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_battery_white.xml
@@ -0,0 +1,28 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M13,2.49H11a1,1,0,0,0-1,1V4H7A1,1,0,0,0,6,5V21a1,1,0,0,0,1,1H17a1,1,0,0,0,1-1V5a1,1,0,0,0-1-1H14V3.49A1,1,0,0,0,13,2.49Z" />
+</vector>
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_display_white.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_display_white.xml
new file mode 100644
index 0000000..54993e2
--- /dev/null
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_display_white.xml
@@ -0,0 +1,52 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M19.5,12h0A7.5,7.5,0,1,0,12,19.5h0A7.49,7.49,0,0,0,19.5,12ZM12,18h0a6,6,0,1,1,6-6h0a6,6,0,0,1-6,6Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 12 1.5 C 12.4142135624 1.5 12.75 1.83578643763 12.75 2.25 C 12.75 2.66421356237 12.4142135624 3 12 3 C 11.5857864376 3 11.25 2.66421356237 11.25 2.25 C 11.25 1.83578643763 11.5857864376 1.5 12 1.5 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 5.1 4.35 C 5.51421356237 4.35 5.85 4.68578643763 5.85 5.1 C 5.85 5.51421356237 5.51421356237 5.85 5.1 5.85 C 4.68578643763 5.85 4.35 5.51421356237 4.35 5.1 C 4.35 4.68578643763 4.68578643763 4.35 5.1 4.35 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 2.25 11.25 C 2.66421356237 11.25 3 11.5857864376 3 12 C 3 12.4142135624 2.66421356237 12.75 2.25 12.75 C 1.83578643763 12.75 1.5 12.4142135624 1.5 12 C 1.5 11.5857864376 1.83578643763 11.25 2.25 11.25 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 5.1 18.15 C 5.51421356237 18.15 5.85 18.4857864376 5.85 18.9 C 5.85 19.3142135624 5.51421356237 19.65 5.1 19.65 C 4.68578643763 19.65 4.35 19.3142135624 4.35 18.9 C 4.35 18.4857864376 4.68578643763 18.15 5.1 18.15 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 12 21 C 12.4142135624 21 12.75 21.3357864376 12.75 21.75 C 12.75 22.1642135624 12.4142135624 22.5 12 22.5 C 11.5857864376 22.5 11.25 22.1642135624 11.25 21.75 C 11.25 21.3357864376 11.5857864376 21 12 21 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 18.9 18.15 C 19.3142135624 18.15 19.65 18.4857864376 19.65 18.9 C 19.65 19.3142135624 19.3142135624 19.65 18.9 19.65 C 18.4857864376 19.65 18.15 19.3142135624 18.15 18.9 C 18.15 18.4857864376 18.4857864376 18.15 18.9 18.15 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 21.75 11.25 C 22.1642135624 11.25 22.5 11.5857864376 22.5 12 C 22.5 12.4142135624 22.1642135624 12.75 21.75 12.75 C 21.3357864376 12.75 21 12.4142135624 21 12 C 21 11.5857864376 21.3357864376 11.25 21.75 11.25 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 18.9 4.35 C 19.3142135624 4.35 19.65 4.68578643763 19.65 5.1 C 19.65 5.51421356237 19.3142135624 5.85 18.9 5.85 C 18.4857864376 5.85 18.15 5.51421356237 18.15 5.1 C 18.15 4.68578643763 18.4857864376 4.35 18.9 4.35 Z" />
+</vector>
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_location.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_location.xml
new file mode 100644
index 0000000..26aa308
--- /dev/null
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_location.xml
@@ -0,0 +1,31 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M15,10a3,3,0,1,0-3,3A3,3,0,0,0,15,10Zm-4.5,0A1.5,1.5,0,1,1,12,11.5,1.5,1.5,0,0,1,10.5,10Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M11.37,21.76A1,1,0,0,0,12,22a1,1,0,0,0,0.62-0.22C14.5,20.26,20,15.5,20,10a8,8,0,0,0-8-8,7.89,7.89,0,0,0-8,8C4,15.5,9.48,20.25,11.37,21.76ZM12,3.51A6.5,6.5,0,0,1,18.5,10c0,4.4-4.31,8.53-6.5,10.34C9.82,18.54,5.5,14.4,5.5,10A6.43,6.43,0,0,1,12,3.51Z" />
+</vector>
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_privacy.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_privacy.xml
new file mode 100644
index 0000000..a6619bd
--- /dev/null
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_privacy.xml
@@ -0,0 +1,34 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,8a4,4,0,0,0,0,8,3.94,3.94,0,0,0,2.23-0.68,5.06,5.06,0,0,1,1.67-2.47A4,4,0,0,0,16,12,4,4,0,0,0,12,8Zm0,6.5A2.5,2.5,0,1,1,14.5,12,2.5,2.5,0,0,1,12,14.5Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M1.55,12.27C1.67,12.58,4.58,20,12,20a10.57,10.57,0,0,0,2-0.21V18.27a8.44,8.44,0,0,1-2,0.23c-5.72,0-8.37-5.22-8.94-6.5C3.63,10.75,6.33,5.5,12,5.5s8.38,5.22,9,6.5l-0.06 0.12 a4.84,4.84,0,0,1,1.28 0.8 c0.17-0.36 0.27 -0.6 0.29 -0.65a0.72 0.72 ,0,0,0,0-0.54C22.34,11.42,19.43,4,12,4S1.67,11.42,1.55,11.73A0.72 0.72 ,0,0,0,1.55,12.27Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M21.25,16.5v-0.66a2.26,2.26,0,0,0-4.5,0v0.66H16V22h6V16.5Zm-3,0v-0.66c0-0.29 0.38 -0.59 0.75 -0.59s0.75 0.3 0.75 0.59 v0.66Z" />
+</vector>
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_security_white.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_security_white.xml
new file mode 100644
index 0000000..d769f4f
--- /dev/null
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_security_white.xml
@@ -0,0 +1,31 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 12 13.5 C 12.8284271247 13.5 13.5 14.1715728753 13.5 15 C 13.5 15.8284271247 12.8284271247 16.5 12 16.5 C 11.1715728753 16.5 10.5 15.8284271247 10.5 15 C 10.5 14.1715728753 11.1715728753 13.5 12 13.5 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M18.51,3A2.32,2.32,0,0,1,21,5.15a0.79 0.79 ,0,0,0,0.76 0.74 0.75 0.75 ,0,0,0,0.74-0.77,3.8,3.8,0,0,0-4-3.66,3.83,3.83,0,0,0-4,3.68V8.5h-9A1.5,1.5,0,0,0,4,10V20a1.5,1.5,0,0,0,1.5,1.5h13A1.5,1.5,0,0,0,20,20V10a1.5,1.5,0,0,0-1.5-1.5H16V5.15A2.35,2.35,0,0,1,18.51,3Zm0,17H5.5V10h13Z" />
+</vector>
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
new file mode 100644
index 0000000..ee990f9
--- /dev/null
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
@@ -0,0 +1,34 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M19.07,4.93A10,10,0,1,0,22,12,10,10,0,0,0,19.07,4.93ZM18,18a8.5,8.5,0,1,1,2.5-6A8.53,8.53,0,0,1,18,18Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,10a0.76 0.76 ,0,0,0-0.75 0.75 v5.5a0.75 0.75 ,0,0,0,1.5,0v-5.5A0.76 0.76 ,0,0,0,12,10Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 12 7 C 12.5522847498 7 13 7.44771525017 13 8 C 13 8.55228474983 12.5522847498 9 12 9 C 11.4477152502 9 11 8.55228474983 11 8 C 11 7.44771525017 11.4477152502 7 12 7 Z" />
+</vector>
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_wireless_white.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_wireless_white.xml
new file mode 100644
index 0000000..ffd2c64
--- /dev/null
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_wireless_white.xml
@@ -0,0 +1,37 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M15.78,14.82a0.57 0.57 ,0,0,0,0.16 0.15 0.75 0.75 ,0,0,0,1-0.2 0.76 0.76,0,0,0-0.2-1,6.77,6.77,0,0,0-9.55,0,0.76 0.76 ,0,0,0,0,1,0.75 0.75 ,0,0,0,1.06,0h0a5.24,5.24,0,0,1,7.42,0A0.08 0.08 ,0,0,0,15.78,14.82Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 12 17 C 12.8284271247 17 13.5 17.6715728753 13.5 18.5 C 13.5 19.3284271247 12.8284271247 20 12 20 C 11.1715728753 20 10.5 19.3284271247 10.5 18.5 C 10.5 17.6715728753 11.1715728753 17 12 17 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M20,11.79a0.77 0.77 ,0,0,0,0-1.07h0a11.59,11.59,0,0,0-8-3.5,11.63,11.63,0,0,0-8,3.5 0.77 0.77,0,0,0,0,1.07 0.76 0.76,0,0,0,1.07,0,10.12,10.12,0,0,1,7-3,10.12,10.12,0,0,1,7,3A0.75 0.75 ,0,0,0,20,11.79Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,2.75A14.76,14.76,0,0,0,1.22,7.2a0.77 0.77 ,0,0,0,0,1,0.75 0.75 ,0,0,0,1.06,0,13.25,13.25,0,0,1,9.7-4,13.27,13.27,0,0,1,9.72,4,0.73 0.73 ,0,0,0,1,0,0.75 0.75 ,0,0,0,0.05-1.06A14.76,14.76,0,0,0,12,2.75Z" />
+</vector>
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_storage_white.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_storage_white.xml
new file mode 100644
index 0000000..e6125db
--- /dev/null
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_storage_white.xml
@@ -0,0 +1,43 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M21,2.75A0.76 0.76 ,0,0,0,20.25,2H3.75A0.76 0.76 ,0,0,0,3,2.75v4.5A0.76 0.76 ,0,0,0,3.75,8h16.5A0.76 0.76 ,0,0,0,21,7.25ZM19.5,6.5H4.5v-3h15Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 5.25 4.25 H 6.75 V 5.75 H 5.25 V 4.25 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M21,9.75A0.76 0.76 ,0,0,0,20.25,9H3.75A0.76 0.76 ,0,0,0,3,9.75v4.5a0.76 0.76 ,0,0,0,0.75 0.75 h16.5a0.76 0.76 ,0,0,0,0.75-0.75ZM19.5,13.5H4.5v-3h15Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 5.25 11.25 H 6.75 V 12.75 H 5.25 V 11.25 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M21,21.25v-4.5a0.76 0.76 ,0,0,0-0.75-0.75H3.75a0.76 0.76 ,0,0,0-0.75 0.75 v4.5a0.76 0.76 ,0,0,0,0.75 0.75 h16.5A0.76 0.76 ,0,0,0,21,21.25Zm-1.5-0.75H4.5v-3h15Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 5.25 18.25 H 6.75 V 19.75 H 5.25 V 18.25 Z" />
+</vector>
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_volume_up_24dp.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
new file mode 100644
index 0000000..76d3e5f
--- /dev/null
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
@@ -0,0 +1,34 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M5.69,16l5,5a0.77 0.77 ,0,0,0,0.53 0.22 0.75 0.75 ,0,0,0,0.29-0.06A0.74 0.74 ,0,0,0,12,20.5V3.5A0.75 0.75 ,0,0,0,10.72,3l-5,5H3.49A1.52,1.52,0,0,0,2,9.5v5A1.5,1.5,0,0,0,3.5,16ZM3.5,9.5H6a0.75 0.75 ,0,0,0,0.53-0.22l4-4V18.69l-4-4A0.75 0.75 ,0,0,0,6,14.5H3.5Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M13.52,20.64a0.77 0.77 ,0,0,0,0.73 0.56 0.63 0.63 ,0,0,0,0.19,0,9.48,9.48,0,0,0,0-18.34 0.75 0.75,0,0,0-0.92 0.53 0.76 0.76 ,0,0,0,0.54 0.92 ,8,8,0,0,1,0,15.44A0.76 0.76 ,0,0,0,13.52,20.64Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M13.85,15a0.76 0.76 ,0,0,0-0.24,1,0.78 0.78 ,0,0,0,0.64 0.35 0.83 0.83 ,0,0,0,0.4-0.11,5,5,0,0,0,1.6-6.88,5.2,5.2,0,0,0-1.6-1.6A0.75 0.75 ,0,1,0,13.85,9,3.33,3.33,0,0,1,15,10.16,3.47,3.47,0,0,1,13.85,15Z" />
+</vector>
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_wifi_tethering.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_wifi_tethering.xml
new file mode 100644
index 0000000..eadd300
--- /dev/null
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_wifi_tethering.xml
@@ -0,0 +1,34 @@
+<!--
+/**
+ * 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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 12 11 C 12.8284271247 11 13.5 11.6715728753 13.5 12.5 C 13.5 13.3284271247 12.8284271247 14 12 14 C 11.1715728753 14 10.5 13.3284271247 10.5 12.5 C 10.5 11.6715728753 11.1715728753 11 12 11 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M15,16.57a0.75 0.75 ,0,0,0,1.06,0,5.75,5.75,0,1,0-8.14,0,0.79 0.79 ,0,0,0,0.53 0.22 A0.75 0.75 ,0,0,0,9,16.57a0.74 0.74 ,0,0,0,0-1.06,4.25,4.25,0,1,1,6,0A0.75 0.75 ,0,0,0,15,16.57Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M18.36,19.61a0.71 0.71 ,0,0,0,0.53-0.22A9.74,9.74,0,0,0,5.11,5.61a9.73,9.73,0,0,0,0,13.78 0.74 0.74,0,0,0,1.06,0,0.75 0.75 ,0,0,0,0-1.06A8.24,8.24,0,0,1,17.83,6.67a8.23,8.23,0,0,1,0,11.66 0.75 0.75,0,0,0,0,1.06A0.74 0.74 ,0,0,0,18.36,19.61Z" />
+</vector>
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 76bb24d..eb8710d 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6936,6 +6936,19 @@
// 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 7ee3047..593bb2c4 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.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.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 @@
// 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 c8a27f1..7102b82 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 @@
// 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 f9de554..259aa50 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -19,7 +19,6 @@
":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 33c6dd2..5278bbb 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -92,6 +92,7 @@
import com.android.internal.util.Preconditions;
import com.android.server.location.AbstractLocationProvider;
import com.android.server.location.ActivityRecognitionProxy;
+import com.android.server.location.CallerIdentity;
import com.android.server.location.GeocoderProxy;
import com.android.server.location.GeofenceManager;
import com.android.server.location.GeofenceProxy;
@@ -113,6 +114,7 @@
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;
@@ -223,10 +225,10 @@
private final ArraySet<String> mIgnoreSettingsPackageWhitelist = new ArraySet<>();
@GuardedBy("mLock")
- private final ArrayMap<IBinder, Identity> mGnssMeasurementsListeners = new ArrayMap<>();
+ private final ArrayMap<IBinder, CallerIdentity> mGnssMeasurementsListeners = new ArrayMap<>();
@GuardedBy("mLock")
- private final ArrayMap<IBinder, Identity>
+ private final ArrayMap<IBinder, CallerIdentity>
mGnssNavigationMessageListeners = new ArrayMap<>();
// current active user on the device - other users are denied location data
@@ -456,7 +458,7 @@
ArrayList<Receiver> deadReceivers = null;
for (Receiver receiver : mReceivers.values()) {
- if (receiver.mIdentity.mPackageName.equals(packageName)) {
+ if (receiver.mCallerIdentity.mPackageName.equals(packageName)) {
if (deadReceivers == null) {
deadReceivers = new ArrayList<>();
}
@@ -479,7 +481,7 @@
for (Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) {
String provider = entry.getKey();
for (UpdateRecord record : entry.getValue()) {
- if (record.mReceiver.mIdentity.mUid == uid
+ if (record.mReceiver.mCallerIdentity.mUid == uid
&& record.mIsForegroundUid != foreground) {
if (D) {
Log.d(TAG, "request from uid " + uid + " is now "
@@ -487,7 +489,7 @@
}
record.updateForeground(foreground);
- if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
+ if (!isThrottlingExemptLocked(record.mReceiver.mCallerIdentity)) {
affectedProviders.add(provider);
}
}
@@ -497,8 +499,8 @@
applyRequirementsLocked(provider);
}
- for (Entry<IBinder, Identity> entry : mGnssMeasurementsListeners.entrySet()) {
- Identity callerIdentity = entry.getValue();
+ for (Entry<IBinder, CallerIdentity> entry : mGnssMeasurementsListeners.entrySet()) {
+ CallerIdentity callerIdentity = entry.getValue();
if (callerIdentity.mUid == uid) {
if (D) {
Log.d(TAG, "gnss measurements listener from uid " + uid
@@ -507,7 +509,7 @@
if (foreground || isThrottlingExemptLocked(entry.getValue())) {
mGnssMeasurementsProvider.addListener(
IGnssMeasurementsListener.Stub.asInterface(entry.getKey()),
- callerIdentity.mUid, callerIdentity.mPackageName);
+ callerIdentity);
} else {
mGnssMeasurementsProvider.removeListener(
IGnssMeasurementsListener.Stub.asInterface(entry.getKey()));
@@ -515,8 +517,8 @@
}
}
- for (Entry<IBinder, Identity> entry : mGnssNavigationMessageListeners.entrySet()) {
- Identity callerIdentity = entry.getValue();
+ for (Entry<IBinder, CallerIdentity> entry : mGnssNavigationMessageListeners.entrySet()) {
+ CallerIdentity callerIdentity = entry.getValue();
if (callerIdentity.mUid == uid) {
if (D) {
Log.d(TAG, "gnss navigation message listener from uid "
@@ -526,7 +528,7 @@
if (foreground || isThrottlingExemptLocked(entry.getValue())) {
mGnssNavigationMessageProvider.addListener(
IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()),
- callerIdentity.mUid, callerIdentity.mPackageName);
+ callerIdentity);
} else {
mGnssNavigationMessageProvider.removeListener(
IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()));
@@ -668,7 +670,7 @@
// 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()) {
@@ -804,7 +806,7 @@
Integer.parseInt(fragments[9]) /* accuracy */);
LocationProvider testProviderManager = new LocationProvider(name);
addProviderLocked(testProviderManager);
- new MockProvider(testProviderManager, properties);
+ new MockProvider(mContext, testProviderManager, properties);
}
}
@@ -835,18 +837,6 @@
onProviderAllowedChangedLocked(false);
}
- private static final class Identity {
- final int mUid;
- final int mPid;
- final String mPackageName;
-
- Identity(int uid, int pid, String packageName) {
- mUid = uid;
- mPid = pid;
- mPackageName = packageName;
- }
- }
-
private class LocationProvider implements AbstractLocationProvider.LocationProviderManager {
private final String mName;
@@ -906,15 +896,12 @@
}
@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();
}
}
@@ -1234,7 +1221,7 @@
*/
private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000;
- final Identity mIdentity;
+ final CallerIdentity mCallerIdentity;
private final int mAllowedResolutionLevel; // resolution level allowed to receiver
private final ILocationListener mListener;
@@ -1262,7 +1249,7 @@
mKey = intent;
}
mAllowedResolutionLevel = getAllowedResolutionLevel(pid, uid);
- mIdentity = new Identity(uid, pid, packageName);
+ mCallerIdentity = new CallerIdentity(uid, pid, packageName);
if (workSource != null && workSource.isEmpty()) {
workSource = null;
}
@@ -1274,7 +1261,7 @@
// construct/configure wakelock
mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
if (workSource == null) {
- workSource = new WorkSource(mIdentity.mUid, mIdentity.mPackageName);
+ workSource = new WorkSource(mCallerIdentity.mUid, mCallerIdentity.mPackageName);
}
mWakeLock.setWorkSource(workSource);
@@ -1377,14 +1364,14 @@
int op) {
if (!currentlyMonitoring) {
if (allowMonitoring) {
- return mAppOps.startOpNoThrow(op, mIdentity.mUid, mIdentity.mPackageName)
- == AppOpsManager.MODE_ALLOWED;
+ return mAppOps.startOpNoThrow(op, mCallerIdentity.mUid,
+ mCallerIdentity.mPackageName) == AppOpsManager.MODE_ALLOWED;
}
} else {
if (!allowMonitoring
- || mAppOps.noteOpNoThrow(op, mIdentity.mUid, mIdentity.mPackageName)
- != AppOpsManager.MODE_ALLOWED) {
- mAppOps.finishOp(op, mIdentity.mUid, mIdentity.mPackageName);
+ || mAppOps.checkOpNoThrow(op, mCallerIdentity.mUid,
+ mCallerIdentity.mPackageName) != AppOpsManager.MODE_ALLOWED) {
+ mAppOps.finishOp(op, mCallerIdentity.mUid, mCallerIdentity.mPackageName);
return false;
}
}
@@ -1722,7 +1709,6 @@
}
}
-
@Override
public void flushGnssBatch(String packageName) {
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1918,7 +1904,7 @@
int allowedResolutionLevel) {
int op = resolutionLevelToOp(allowedResolutionLevel);
if (op >= 0) {
- if (mAppOps.noteOp(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
+ if (mAppOps.checkOp(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
return false;
}
}
@@ -2014,7 +2000,7 @@
if (records != null) {
for (UpdateRecord record : records) {
if (!isCurrentProfileLocked(
- UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
+ UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) {
continue;
}
@@ -2073,24 +2059,28 @@
providerRequest.lowPowerMode = true;
for (UpdateRecord record : records) {
if (!isCurrentProfileLocked(
- UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
+ UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) {
continue;
}
if (!checkLocationAccess(
- record.mReceiver.mIdentity.mPid,
- record.mReceiver.mIdentity.mUid,
- record.mReceiver.mIdentity.mPackageName,
+ record.mReceiver.mCallerIdentity.mPid,
+ record.mReceiver.mCallerIdentity.mUid,
+ record.mReceiver.mCallerIdentity.mPackageName,
record.mReceiver.mAllowedResolutionLevel)) {
continue;
}
- if (!provider.isUseableLocked() && !isSettingsExemptLocked(record)) {
- continue;
+ if (!provider.isUseableLocked()) {
+ if (isSettingsExemptLocked(record)) {
+ providerRequest.forceLocation = true;
+ } else {
+ continue;
+ }
}
LocationRequest locationRequest = record.mRealRequest;
long interval = locationRequest.getInterval();
- if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
+ if (!isThrottlingExemptLocked(record.mReceiver.mCallerIdentity)) {
if (!record.mIsForegroundUid) {
interval = Math.max(interval, backgroundThrottleInterval);
}
@@ -2120,7 +2110,7 @@
long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2;
for (UpdateRecord record : records) {
if (isCurrentProfileLocked(
- UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
+ UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) {
LocationRequest locationRequest = record.mRequest;
// Don't assign battery blame for update records whose
@@ -2137,8 +2127,8 @@
// Assign blame to caller if there's no WorkSource associated with
// the request or if it's invalid.
worksource.add(
- record.mReceiver.mIdentity.mUid,
- record.mReceiver.mIdentity.mPackageName);
+ record.mReceiver.mCallerIdentity.mUid,
+ record.mReceiver.mCallerIdentity.mPackageName);
}
}
}
@@ -2175,19 +2165,17 @@
}
@GuardedBy("mLock")
- private boolean isThrottlingExemptLocked(Identity identity) {
- if (identity.mUid == Process.SYSTEM_UID) {
+ private boolean isThrottlingExemptLocked(CallerIdentity callerIdentity) {
+ if (callerIdentity.mUid == Process.SYSTEM_UID) {
return true;
}
- if (mBackgroundThrottlePackageWhitelist.contains(identity.mPackageName)) {
+ if (mBackgroundThrottlePackageWhitelist.contains(callerIdentity.mPackageName)) {
return true;
}
- for (LocationProvider provider : mProviders) {
- if (identity.mPackageName.equals(provider.getPackageLocked())) {
- return true;
- }
+ if (isProviderPackage(callerIdentity.mPackageName)) {
+ return true;
}
return false;
@@ -2199,14 +2187,13 @@
return false;
}
- if (mIgnoreSettingsPackageWhitelist.contains(record.mReceiver.mIdentity.mPackageName)) {
+ if (mIgnoreSettingsPackageWhitelist.contains(
+ record.mReceiver.mCallerIdentity.mPackageName)) {
return true;
}
- for (LocationProvider provider : mProviders) {
- if (record.mReceiver.mIdentity.mPackageName.equals(provider.getPackageLocked())) {
- return true;
- }
+ if (isProviderPackage(record.mReceiver.mCallerIdentity.mPackageName)) {
+ return true;
}
return false;
@@ -2230,7 +2217,7 @@
mRequest = request;
mReceiver = receiver;
mIsForegroundUid = isImportanceForeground(
- mActivityManager.getPackageImportance(mReceiver.mIdentity.mPackageName));
+ mActivityManager.getPackageImportance(mReceiver.mCallerIdentity.mPackageName));
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
if (records == null) {
@@ -2243,7 +2230,7 @@
// Update statistics for historical location requests by package/provider
mRequestStatistics.startRequesting(
- mReceiver.mIdentity.mPackageName, provider, request.getInterval(),
+ mReceiver.mCallerIdentity.mPackageName, provider, request.getInterval(),
mIsForegroundUid);
}
@@ -2253,14 +2240,14 @@
private void updateForeground(boolean isForeground) {
mIsForegroundUid = isForeground;
mRequestStatistics.updateForeground(
- mReceiver.mIdentity.mPackageName, mProvider, isForeground);
+ mReceiver.mCallerIdentity.mPackageName, mProvider, isForeground);
}
/**
* Method to be called when a record will no longer be used.
*/
private void disposeLocked(boolean removeReceiver) {
- mRequestStatistics.stopRequesting(mReceiver.mIdentity.mPackageName, mProvider);
+ mRequestStatistics.stopRequesting(mReceiver.mCallerIdentity.mPackageName, mProvider);
// remove from mRecordsByProvider
ArrayList<UpdateRecord> globalRecords = mRecordsByProvider.get(this.mProvider);
@@ -2282,8 +2269,8 @@
@Override
public String toString() {
- return "UpdateRecord[" + mProvider + " " + mReceiver.mIdentity.mPackageName
- + "(" + mReceiver.mIdentity.mUid + (mIsForegroundUid ? " foreground"
+ return "UpdateRecord[" + mProvider + " " + mReceiver.mCallerIdentity.mPackageName
+ + "(" + mReceiver.mCallerIdentity.mUid + (mIsForegroundUid ? " foreground"
: " background")
+ ")" + " " + mRealRequest + " "
+ mReceiver.mWorkSource + "]";
@@ -2458,7 +2445,7 @@
Log.d(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver))
+ " " + name + " " + request + " from " + packageName + "(" + uid + " "
+ (record.mIsForegroundUid ? "foreground" : "background")
- + (isThrottlingExemptLocked(receiver.mIdentity)
+ + (isThrottlingExemptLocked(receiver.mCallerIdentity)
? " [whitelisted]" : "") + ")");
}
@@ -2566,13 +2553,6 @@
return null;
}
- if (!reportLocationAccessNoThrow(pid, uid, packageName, allowedResolutionLevel)) {
- if (D) {
- Log.d(TAG, "not returning last loc for no op app: "
- + packageName);
- }
- return null;
- }
// Figure out the provider. Either its explicitly request (deprecated API's),
// or use the fused provider
@@ -2582,8 +2562,8 @@
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;
}
@@ -2616,16 +2596,27 @@
return null;
}
+ Location lastLocation = null;
if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
Location noGPSLocation = location.getExtraLocation(
Location.EXTRA_NO_GPS_LOCATION);
if (noGPSLocation != null) {
- return new Location(mLocationFudger.getOrCreate(noGPSLocation));
+ lastLocation = new Location(mLocationFudger.getOrCreate(noGPSLocation));
}
} else {
- return new Location(location);
+ lastLocation = new Location(location);
}
- return null;
+ // Don't report location access if there is no last location to deliver.
+ if (lastLocation != null) {
+ if (!reportLocationAccessNoThrow(
+ pid, uid, packageName, allowedResolutionLevel)) {
+ if (D) {
+ Log.d(TAG, "not returning last loc for no op app: " + packageName);
+ }
+ lastLocation = null;
+ }
+ }
+ return lastLocation;
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -2734,7 +2725,8 @@
// TODO(b/120449926): The GNSS status listeners should be handled similar to the GNSS
// measurements listeners.
- return mGnssStatusProvider.addListener(callback, Binder.getCallingUid(), packageName);
+ return mGnssStatusProvider.addListener(callback, new CallerIdentity(Binder.getCallingUid(),
+ Binder.getCallingPid(), packageName));
}
@Override
@@ -2750,8 +2742,8 @@
}
synchronized (mLock) {
- Identity callerIdentity
- = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+ CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(),
+ Binder.getCallingPid(), packageName);
// TODO(b/120481270): Register for client death notification and update map.
mGnssMeasurementsListeners.put(listener.asBinder(), callerIdentity);
long identity = Binder.clearCallingIdentity();
@@ -2759,8 +2751,7 @@
if (isThrottlingExemptLocked(callerIdentity)
|| isImportanceForeground(
mActivityManager.getPackageImportance(packageName))) {
- return mGnssMeasurementsProvider.addListener(listener,
- callerIdentity.mUid, callerIdentity.mPackageName);
+ return mGnssMeasurementsProvider.addListener(listener, callerIdentity);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -2815,8 +2806,8 @@
}
synchronized (mLock) {
- Identity callerIdentity
- = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+ CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(),
+ Binder.getCallingPid(), packageName);
// TODO(b/120481270): Register for client death notification and update map.
mGnssNavigationMessageListeners.put(listener.asBinder(), callerIdentity);
@@ -2825,8 +2816,7 @@
if (isThrottlingExemptLocked(callerIdentity)
|| isImportanceForeground(
mActivityManager.getPackageImportance(packageName))) {
- return mGnssNavigationMessageProvider.addListener(listener,
- callerIdentity.mUid, callerIdentity.mPackageName);
+ return mGnssNavigationMessageProvider.addListener(listener, callerIdentity);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -2900,13 +2890,15 @@
}
@Override
- public String getNetworkProviderPackage() {
+ public boolean isProviderPackage(String packageName) {
synchronized (mLock) {
- LocationProvider provider = getLocationProviderLocked(NETWORK_PROVIDER);
- if (provider == null) {
- return null;
+ for (LocationProvider provider : mProviders) {
+ if (provider.getPackagesLocked().contains(packageName)) {
+ return true;
+ }
}
- return provider.getPackageLocked();
+
+ return false;
}
}
@@ -2988,28 +2980,6 @@
}
@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
@@ -3109,33 +3079,33 @@
continue;
}
- int receiverUserId = UserHandle.getUserId(receiver.mIdentity.mUid);
+ int receiverUserId = UserHandle.getUserId(receiver.mCallerIdentity.mUid);
if (!isCurrentProfileLocked(receiverUserId)
- && !isLocationProviderLocked(receiver.mIdentity.mUid)) {
+ && !isProviderPackage(receiver.mCallerIdentity.mPackageName)) {
if (D) {
Log.d(TAG, "skipping loc update for background user " + receiverUserId +
" (current user: " + mCurrentUserId + ", app: " +
- receiver.mIdentity.mPackageName + ")");
+ receiver.mCallerIdentity.mPackageName + ")");
}
continue;
}
- if (mBlacklist.isBlacklisted(receiver.mIdentity.mPackageName)) {
+ if (mBlacklist.isBlacklisted(receiver.mCallerIdentity.mPackageName)) {
if (D) {
Log.d(TAG, "skipping loc update for blacklisted app: " +
- receiver.mIdentity.mPackageName);
+ receiver.mCallerIdentity.mPackageName);
}
continue;
}
if (!reportLocationAccessNoThrow(
- receiver.mIdentity.mPid,
- receiver.mIdentity.mUid,
- receiver.mIdentity.mPackageName,
+ receiver.mCallerIdentity.mPid,
+ receiver.mCallerIdentity.mUid,
+ receiver.mCallerIdentity.mPackageName,
receiver.mAllowedResolutionLevel)) {
if (D) {
Log.d(TAG, "skipping loc update for no op app: " +
- receiver.mIdentity.mPackageName);
+ receiver.mCallerIdentity.mPackageName);
}
continue;
}
@@ -3271,7 +3241,7 @@
// Mock Providers
private boolean canCallerAccessMockLocation(String opPackageName) {
- return mAppOps.noteOp(AppOpsManager.OP_MOCK_LOCATION, Binder.getCallingUid(),
+ return mAppOps.checkOp(AppOpsManager.OP_MOCK_LOCATION, Binder.getCallingUid(),
opPackageName) == AppOpsManager.MODE_ALLOWED;
}
@@ -3300,7 +3270,8 @@
MockLocationProvider mockProviderManager = new MockLocationProvider(name);
addProviderLocked(mockProviderManager);
- mockProviderManager.attachLocked(new MockProvider(mockProviderManager, properties));
+ mockProviderManager.attachLocked(
+ new MockProvider(mContext, mockProviderManager, properties));
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -3424,21 +3395,23 @@
}
}
pw.println(" Active GnssMeasurement Listeners:");
- for (Identity identity : mGnssMeasurementsListeners.values()) {
- pw.println(" " + identity.mPid + " " + identity.mUid + " "
- + identity.mPackageName + ": " + isThrottlingExemptLocked(identity));
+ for (CallerIdentity callerIdentity : mGnssMeasurementsListeners.values()) {
+ pw.println(" " + callerIdentity.mPid + " " + callerIdentity.mUid + " "
+ + callerIdentity.mPackageName + ": "
+ + isThrottlingExemptLocked(callerIdentity));
}
pw.println(" Active GnssNavigationMessage Listeners:");
- for (Identity identity : mGnssNavigationMessageListeners.values()) {
- pw.println(" " + identity.mPid + " " + identity.mUid + " "
- + identity.mPackageName + ": " + isThrottlingExemptLocked(identity));
+ for (CallerIdentity callerIdentity : mGnssNavigationMessageListeners.values()) {
+ pw.println(" " + callerIdentity.mPid + " " + callerIdentity.mUid + " "
+ + callerIdentity.mPackageName + ": "
+ + isThrottlingExemptLocked(callerIdentity));
}
pw.println(" Overlay Provider Packages:");
for (LocationProvider provider : mProviders) {
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 e731f34..ddbb421 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 @@
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 c84b5c7..117174a 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 deaa931..a3bae52 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 @@
/** @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 @@
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 @@
// 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 @@
}
/*package*/ void disconnectAllBluetoothProfiles() {
- mBtHelper.disconnectAllBluetoothProfiles();
+ synchronized (mDeviceStateLock) {
+ mBtHelper.disconnectAllBluetoothProfiles();
+ }
}
/**
@@ -140,11 +149,13 @@
* @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 @@
}
/*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 @@
+ " 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 @@
+ " 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 @@
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 @@
.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 @@
/*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 @@
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 @@
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 @@
}
/*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 @@
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 97649a7..11fdc8f 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 @@
"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 @@
/*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 @@
/*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 @@
// 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 @@
// 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 9457fe3..de63d0e 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 @@
mPrescaleAbsoluteVolume[i] = preScale[i];
}
}
-
}
public void systemReady() {
@@ -1711,7 +1710,7 @@
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 @@
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 bf32501..b63af8a 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.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 @@
}
// 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 @@
// 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 @@
//----------------------------------------------------------------------
// Interface for AudioDeviceBroker
- /*package*/ void onSystemReady() {
+ /*package*/ synchronized void onSystemReady() {
mScoConnectionState = android.media.AudioManager.SCO_AUDIO_STATE_ERROR;
resetBluetoothSco();
getBluetoothHeadset();
@@ -165,45 +163,39 @@
}
}
- @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) {
+ /*package*/ synchronized void setAvrcpAbsoluteVolumeIndex(int index) {
+ if (mA2dp == null) {
+ if (AudioService.DEBUG_VOL) {
+ Log.d(TAG, "setAvrcpAbsoluteVolumeIndex: bailing due to null mA2dp");
return;
}
- if (AudioService.DEBUG_VOL) {
- Log.i(TAG, "setAvrcpAbsoluteVolumeIndex index=" + index);
- }
- 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 @@
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 @@
} 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;
+ 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;
}
- 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;
- }
- 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 @@
*
* @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 @@
*
* @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 @@
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 @@
}
- /*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 @@
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 @@
}
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 @@
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 @@
break;
case BluetoothProfile.HEADSET:
- synchronized (mScoClients) {
+ synchronized (BtHelper.this) {
// Discard timeout message
mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService();
mBluetoothHeadset = (BluetoothHeadset) proxy;
@@ -536,17 +534,19 @@
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 @@
}
};
- 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 @@
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 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");
- }
- }
- mStartcount++;
- }
- }
-
- public void decCount() {
- synchronized (mScoClients) {
- 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);
+ 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++;
}
- public void clearCount(boolean stopSco) {
- synchronized (mScoClients) {
- if (mStartcount != 0) {
+ void decCount() {
+ 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, "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);
}
}
- public int getCount() {
+ 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);
+ }
+ }
+
+ 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 @@
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,60 +845,43 @@
}
}
- /*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;
- }
+ 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);
- }
+ }
+ mScoClients.clear();
+ if (savedClient != null) {
+ mScoClients.add(savedClient);
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
index adc1cd7..2c0cacd 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
@@ -450,6 +450,20 @@
}
/**
+ * Returns {@code true} if given Android keycode is volume control related,
+ * otherwise {@code false}.
+ */
+ static boolean isVolumeKeycode(int androidKeycode) {
+ int cecKeyCode = HdmiCecKeycode.androidKeyToCecKey(androidKeycode)[0];
+ return isSupportedKeycode(androidKeycode)
+ && (cecKeyCode == CEC_KEYCODE_VOLUME_UP
+ || cecKeyCode == CEC_KEYCODE_VOLUME_DOWN
+ || cecKeyCode == CEC_KEYCODE_MUTE
+ || cecKeyCode == CEC_KEYCODE_MUTE_FUNCTION
+ || cecKeyCode == CEC_KEYCODE_RESTORE_VOLUME_FUNCTION);
+ }
+
+ /**
* Returns CEC keycode to control audio mute status.
*
* @param muting {@code true} if audio is being muted
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 414f6bb..78b091e 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -1016,6 +1016,40 @@
}
/**
+ * Send a volume key event to other CEC device. The logical address of target device will be
+ * given by {@link #findAudioReceiverAddress()}.
+ *
+ * @param keyCode key code defined in {@link android.view.KeyEvent}
+ * @param isPressed {@code true} for key down event
+ * @see #findAudioReceiverAddress()
+ */
+ @ServiceThreadOnly
+ protected void sendVolumeKeyEvent(int keyCode, boolean isPressed) {
+ assertRunOnServiceThread();
+ if (!HdmiCecKeycode.isVolumeKeycode(keyCode)) {
+ Slog.w(TAG, "Not a volume key: " + keyCode);
+ return;
+ }
+ List<SendKeyAction> action = getActions(SendKeyAction.class);
+ int logicalAddress = findAudioReceiverAddress();
+ if (logicalAddress == Constants.ADDR_INVALID || logicalAddress == mAddress) {
+ // Don't send key event to invalid device or itself.
+ Slog.w(
+ TAG,
+ "Discard volume key event: "
+ + keyCode
+ + ", pressed:"
+ + isPressed
+ + ", receiverAddr="
+ + logicalAddress);
+ } else if (!action.isEmpty()) {
+ action.get(0).processKeyEvent(keyCode, isPressed);
+ } else if (isPressed) {
+ addAndStartAction(new SendKeyAction(this, logicalAddress, keyCode));
+ }
+ }
+
+ /**
* Returns the logical address of the device which will receive key events via {@link
* #sendKeyEvent}.
*
@@ -1026,6 +1060,17 @@
return Constants.ADDR_INVALID;
}
+ /**
+ * Returns the logical address of the audio receiver device which will receive volume key events
+ * via {@link#sendVolumeKeyEvent}.
+ *
+ * @see #sendVolumeKeyEvent(int, boolean)
+ */
+ protected int findAudioReceiverAddress() {
+ Slog.w(TAG, "findAudioReceiverAddress is not implemented");
+ return Constants.ADDR_INVALID;
+ }
+
@ServiceThreadOnly
void invokeCallback(IHdmiControlCallback callback, int result) {
assertRunOnServiceThread();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 1ba0c52..6710986 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -68,11 +68,6 @@
private static final String TAG = "HdmiCecLocalDeviceAudioSystem";
- // Whether System audio mode is activated or not.
- // This becomes true only when all system audio sequences are finished.
- @GuardedBy("mLock")
- private boolean mSystemAudioActivated;
-
// Whether the System Audio Control feature is enabled or not. True by default.
@GuardedBy("mLock")
private boolean mSystemAudioControlFeatureEnabled;
@@ -271,7 +266,7 @@
synchronized (mLock) {
mService.writeStringSystemProperty(
Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL,
- mSystemAudioActivated ? "true" : "false");
+ isSystemAudioActivated() ? "true" : "false");
}
terminateSystemAudioMode();
}
@@ -786,7 +781,7 @@
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(
@@ -814,7 +809,7 @@
}
HdmiLogger.debug(
"System Audio Mode change[old:%b new:%b]",
- mSystemAudioActivated, newSystemAudioMode);
+ isSystemAudioActivated(), newSystemAudioMode);
// Wake up device if System Audio Control is turned on
if (newSystemAudioMode) {
mService.wakeUp();
@@ -854,8 +849,8 @@
}
updateAudioManagerForSystemAudio(newSystemAudioMode);
synchronized (mLock) {
- if (mSystemAudioActivated != newSystemAudioMode) {
- mSystemAudioActivated = newSystemAudioMode;
+ if (isSystemAudioActivated() != newSystemAudioMode) {
+ mService.setSystemAudioActivated(newSystemAudioMode);
mService.announceSystemAudioModeChange(newSystemAudioMode);
}
}
@@ -946,9 +941,7 @@
}
protected boolean isSystemAudioActivated() {
- synchronized (mLock) {
- return mSystemAudioActivated;
- }
+ return mService.isSystemAudioActivated();
}
protected void terminateSystemAudioMode() {
@@ -1215,7 +1208,6 @@
protected void dump(IndentingPrintWriter pw) {
pw.println("HdmiCecLocalDeviceAudioSystem:");
pw.increaseIndent();
- pw.println("mSystemAudioActivated: " + mSystemAudioActivated);
pw.println("isRoutingFeatureEnabled " + isRoutingControlFeatureEnabled());
pw.println("mSystemAudioControlFeatureEnabled: " + mSystemAudioControlFeatureEnabled);
pw.println("mTvSystemAudioModeSupport: " + mTvSystemAudioModeSupport);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index ef7d241..20933db 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -19,6 +19,7 @@
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.SystemProperties;
@@ -30,6 +31,7 @@
import com.android.internal.app.LocalePicker.LocaleInfo;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
+import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
import java.io.UnsupportedEncodingException;
import java.util.List;
@@ -85,6 +87,22 @@
mAddress, mService.getPhysicalAddress(), mDeviceType));
mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
mAddress, mService.getVendorId()));
+ if (mService.audioSystem() == null) {
+ // If current device is not a functional audio system device,
+ // send message to potential audio system device in the system to get the system
+ // audio mode status. If no response, set to false.
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
+ mAddress, Constants.ADDR_AUDIO_SYSTEM), new SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ if (error != SendMessageResult.SUCCESS) {
+ HdmiLogger.debug(
+ "AVR did not respond to <Give System Audio Mode Status>");
+ mService.setSystemAudioActivated(false);
+ }
+ }
+ });
+ }
startQueuedActions();
}
@@ -275,11 +293,50 @@
}
@Override
+ protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
+ // System Audio Mode only turns on/off when Audio System broadcasts on/off message.
+ // For device with type 4 and 5, it can set system audio mode on/off
+ // when there is another audio system device connected into the system first.
+ if (message.getDestination() != Constants.ADDR_BROADCAST
+ || message.getSource() != Constants.ADDR_AUDIO_SYSTEM
+ || mService.audioSystem() != null) {
+ return true;
+ }
+ boolean setSystemAudioModeOn = HdmiUtils.parseCommandParamSystemAudioStatus(message);
+ if (mService.isSystemAudioActivated() != setSystemAudioModeOn) {
+ mService.setSystemAudioActivated(setSystemAudioModeOn);
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
+ // Only directly addressed System Audio Mode Status message can change internal
+ // system audio mode status.
+ if (message.getDestination() == mAddress
+ && message.getSource() == Constants.ADDR_AUDIO_SYSTEM) {
+ boolean setSystemAudioModeOn = HdmiUtils.parseCommandParamSystemAudioStatus(message);
+ if (mService.isSystemAudioActivated() != setSystemAudioModeOn) {
+ mService.setSystemAudioActivated(setSystemAudioModeOn);
+ }
+ }
+ return true;
+ }
+
+ @Override
protected int findKeyReceiverAddress() {
return Constants.ADDR_TV;
}
@Override
+ protected int findAudioReceiverAddress() {
+ if (mService.isSystemAudioActivated()) {
+ return Constants.ADDR_AUDIO_SYSTEM;
+ }
+ return Constants.ADDR_TV;
+ }
+
+ @Override
@ServiceThreadOnly
protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) {
super.disableDevice(initiatedByCec, callback);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index a8c4350..8a7051f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -77,11 +77,6 @@
// True by default for all the ARC-enabled ports.
private final SparseBooleanArray mArcFeatureEnabled = new SparseBooleanArray();
- // Whether System audio mode is activated or not.
- // This becomes true only when all system audio sequences are finished.
- @GuardedBy("mLock")
- private boolean mSystemAudioActivated = false;
-
// Whether the System Audio Control feature is enabled or not. True by default.
@GuardedBy("mLock")
private boolean mSystemAudioControlFeatureEnabled;
@@ -829,11 +824,12 @@
+ "because the System Audio Control feature is disabled.");
return;
}
- HdmiLogger.debug("System Audio Mode change[old:%b new:%b]", mSystemAudioActivated, on);
+ HdmiLogger.debug("System Audio Mode change[old:%b new:%b]",
+ mService.isSystemAudioActivated(), on);
updateAudioManagerForSystemAudio(on);
synchronized (mLock) {
- if (mSystemAudioActivated != on) {
- mSystemAudioActivated = on;
+ if (mService.isSystemAudioActivated() != on) {
+ mService.setSystemAudioActivated(on);
mService.announceSystemAudioModeChange(on);
}
startArcAction(on);
@@ -849,9 +845,7 @@
if (!hasSystemAudioDevice()) {
return false;
}
- synchronized (mLock) {
- return mSystemAudioActivated;
- }
+ return mService.isSystemAudioActivated();
}
@ServiceThreadOnly
@@ -1904,7 +1898,6 @@
super.dump(pw);
pw.println("mArcEstablished: " + mArcEstablished);
pw.println("mArcFeatureEnabled: " + mArcFeatureEnabled);
- pw.println("mSystemAudioActivated: " + mSystemAudioActivated);
pw.println("mSystemAudioMute: " + mSystemAudioMute);
pw.println("mSystemAudioControlFeatureEnabled: " + mSystemAudioControlFeatureEnabled);
pw.println("mAutoDeviceOff: " + mAutoDeviceOff);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index c5eccdf..7376ed2 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -147,6 +147,10 @@
@GuardedBy("mLock")
protected final ActiveSource mActiveSource = new ActiveSource();
+ // Whether System Audio Mode is activated or not.
+ @GuardedBy("mLock")
+ private boolean mSystemAudioActivated = false;
+
private static final boolean isHdmiCecNeverClaimPlaybackLogicAddr =
SystemProperties.getBoolean(
Constants.PROPERTY_HDMI_CEC_NEVER_CLAIM_PLAYBACK_LOGICAL_ADDRESS, false);
@@ -1533,7 +1537,7 @@
if (mCecController != null) {
HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(deviceType);
if (localDevice == null) {
- Slog.w(TAG, "Local device not available");
+ Slog.w(TAG, "Local device not available to send key event.");
return;
}
localDevice.sendKeyEvent(keyCode, isPressed);
@@ -1543,6 +1547,28 @@
}
@Override
+ public void sendVolumeKeyEvent(
+ final int deviceType, final int keyCode, final boolean isPressed) {
+ enforceAccessPermission();
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ if (mCecController == null) {
+ Slog.w(TAG, "CEC controller not available to send volume key event.");
+ return;
+ }
+ HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(deviceType);
+ if (localDevice == null) {
+ Slog.w(TAG, "Local device " + deviceType
+ + " not available to send volume key event.");
+ return;
+ }
+ localDevice.sendVolumeKeyEvent(keyCode, isPressed);
+ }
+ });
+ }
+
+ @Override
public void oneTouchPlay(final IHdmiControlCallback callback) {
enforceAccessPermission();
runOnServiceThread(new Runnable() {
@@ -2010,6 +2036,7 @@
pw.increaseIndent();
pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled);
pw.println("mMhlInputChangeEnabled: " + mMhlInputChangeEnabled);
+ pw.println("mSystemAudioActivated: " + isSystemAudioActivated());
pw.decreaseIndent();
pw.println("mMhlController: ");
@@ -2636,6 +2663,18 @@
}
}
+ boolean isSystemAudioActivated() {
+ synchronized (mLock) {
+ return mSystemAudioActivated;
+ }
+ }
+
+ void setSystemAudioActivated(boolean on) {
+ synchronized (mLock) {
+ mSystemAudioActivated = on;
+ }
+ }
+
@ServiceThreadOnly
void setCecOption(int key, boolean value) {
assertRunOnServiceThread();
diff --git a/services/core/java/com/android/server/hdmi/SendKeyAction.java b/services/core/java/com/android/server/hdmi/SendKeyAction.java
index 40d2583..5ad7fab 100644
--- a/services/core/java/com/android/server/hdmi/SendKeyAction.java
+++ b/services/core/java/com/android/server/hdmi/SendKeyAction.java
@@ -17,8 +17,10 @@
import static com.android.server.hdmi.HdmiConfig.IRT_MS;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.util.Slog;
import android.view.KeyEvent;
+import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
/**
* Feature action that transmits remote control key command (User Control Press/
@@ -146,8 +148,26 @@
if (cecKeycodeAndParams == null) {
return;
}
- sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(),
- mTargetAddress, cecKeycodeAndParams));
+ // Devices that are not directly connected with audio system device can't detect if the
+ // audio system device is still plugged in. Framework checks if the volume key forwarding is
+ // successful or not every time to make sure the System Audio Mode status is still updated.
+ if (mTargetAddress == Constants.ADDR_AUDIO_SYSTEM
+ && localDevice().mAddress != Constants.ADDR_TV) {
+ sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(),
+ mTargetAddress, cecKeycodeAndParams), new SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ if (error != SendMessageResult.SUCCESS) {
+ HdmiLogger.debug(
+ "AVR did not respond to <User Control Pressed>");
+ localDevice().mService.setSystemAudioActivated(false);
+ }
+ }
+ });
+ } else {
+ sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(),
+ mTargetAddress, cecKeycodeAndParams));
+ }
}
private void sendKeyUp() {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index d20508a..144f2b6 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1627,10 +1627,12 @@
}
@Override
- public List<InputMethodInfo> getInputMethodList() {
- final int callingUserId = UserHandle.getCallingUserId();
+ public List<InputMethodInfo> getInputMethodList(@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();
@@ -1645,10 +1647,12 @@
}
@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 +4701,7 @@
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 3222143..500c388 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 @@
* 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 @@
ApiCallbacks(Context context, UserDataMap userDataMap,
UserToInputMethodInfoMap inputMethodInfoMap) {
+ mContext = context;
mUserDataMap = userDataMap;
mInputMethodInfoMap = inputMethodInfoMap;
mAppOpsManager = context.getSystemService(AppOpsManager.class);
@@ -1233,14 +1236,20 @@
@BinderThread
@Override
- public List<InputMethodInfo> getInputMethodList() {
- return mInputMethodInfoMap.getAsList(UserHandle.getUserId(Binder.getCallingUid()));
+ public List<InputMethodInfo> getInputMethodList(@UserIdInt int userId) {
+ if (UserHandle.getCallingUserId() != userId) {
+ mContext.enforceCallingPermission(INTERACT_ACROSS_USERS_FULL, null);
+ }
+ return mInputMethodInfoMap.getAsList(userId);
}
@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 cefe583..19d10ec 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 @@
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 b3f1018..0edd17b 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 java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Collections;
import java.util.List;
/**
@@ -65,9 +67,12 @@
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 @@
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/CallerIdentity.java b/services/core/java/com/android/server/location/CallerIdentity.java
new file mode 100644
index 0000000..da31d0b
--- /dev/null
+++ b/services/core/java/com/android/server/location/CallerIdentity.java
@@ -0,0 +1,32 @@
+/*
+ * 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.server.location;
+
+/**
+ * Represents the calling process's uid, pid, and package name.
+ */
+public class CallerIdentity {
+ public final int mUid;
+ public final int mPid;
+ public final String mPackageName;
+
+ public CallerIdentity(int uid, int pid, String packageName) {
+ mUid = uid;
+ mPid = pid;
+ mPackageName = packageName;
+ }
+}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index ab9f711..f368e7b 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.database.ContentObserver;
import android.hardware.location.GeofenceHardware;
import android.hardware.location.GeofenceHardwareImpl;
import android.location.Criteria;
@@ -168,21 +169,13 @@
private static final int AGPS_SUPL_MODE_MSA = 0x02;
private static final int AGPS_SUPL_MODE_MSB = 0x01;
- // Handler messages
- private static final int CHECK_LOCATION = 1;
- private static final int ENABLE = 2;
private static final int SET_REQUEST = 3;
- private static final int UPDATE_NETWORK_STATE = 4;
private static final int INJECT_NTP_TIME = 5;
private static final int DOWNLOAD_XTRA_DATA = 6;
private static final int UPDATE_LOCATION = 7; // Handle external location from network listener
- private static final int ADD_LISTENER = 8;
- private static final int REMOVE_LISTENER = 9;
private static final int DOWNLOAD_XTRA_DATA_FINISHED = 11;
private static final int SUBSCRIPTION_OR_CARRIER_CONFIG_CHANGED = 12;
private static final int INITIALIZE_HANDLER = 13;
- private static final int REQUEST_SUPL_CONNECTION = 14;
- private static final int RELEASE_SUPL_CONNECTION = 15;
private static final int REQUEST_LOCATION = 16;
private static final int REPORT_LOCATION = 17; // HAL reports location
private static final int REPORT_SV_STATUS = 18; // HAL reports SV status
@@ -311,6 +304,8 @@
// true if we are enabled, protected by this
private boolean mEnabled = true;
+ private boolean mShutdown;
+
// states for injecting ntp and downloading xtra data
private static final int STATE_PENDING_NETWORK = 0;
private static final int STATE_DOWNLOADING = 1;
@@ -349,7 +344,7 @@
private GnssPositionMode mLastPositionMode;
// Current request from underlying location clients.
- private ProviderRequest mProviderRequest = null;
+ private ProviderRequest mProviderRequest;
// The WorkSource associated with the most recent client request (i.e, most recent call to
// setRequest).
private WorkSource mWorkSource = null;
@@ -368,11 +363,9 @@
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;
- private final GnssSatelliteBlacklistHelper mGnssSatelliteBlacklistHelper;
private final GnssMeasurementsProvider mGnssMeasurementsProvider;
private final GnssNavigationMessageProvider mGnssNavigationMessageProvider;
private final LocationChangeListener mNetworkLocationListener = new NetworkLocationListener();
@@ -530,8 +523,8 @@
// Disable GPS if we are in device idle mode.
boolean disableGps = mPowerManager.isDeviceIdleMode();
final PowerSaveState result =
- mPowerManager.getPowerSaveState(ServiceType.GPS);
- switch (result.gpsMode) {
+ mPowerManager.getPowerSaveState(ServiceType.LOCATION);
+ switch (result.locationMode) {
case PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF:
// If we are in battery saver mode and the screen is off, disable GPS.
disableGps |= result.batterySaverEnabled && !mPowerManager.isInteractive();
@@ -539,6 +532,7 @@
}
if (disableGps != mDisableGps) {
mDisableGps = disableGps;
+ updateEnabled();
updateRequirements();
}
}
@@ -562,9 +556,8 @@
public GnssLocationProvider(Context context, LocationProviderManager locationProviderManager,
Looper looper) {
- super(locationProviderManager);
+ super(context, locationProviderManager);
- mContext = context;
mLooper = looper;
// Create a wake lock
@@ -637,23 +630,32 @@
mGnssMetrics = new GnssMetrics(mBatteryStats);
mNtpTimeHelper = new NtpTimeHelper(mContext, looper, this);
- mGnssSatelliteBlacklistHelper = new GnssSatelliteBlacklistHelper(mContext,
- looper, this);
- mHandler.post(mGnssSatelliteBlacklistHelper::updateSatelliteBlacklist);
+ GnssSatelliteBlacklistHelper gnssSatelliteBlacklistHelper =
+ new GnssSatelliteBlacklistHelper(mContext,
+ looper, this);
+ mHandler.post(gnssSatelliteBlacklistHelper::updateSatelliteBlacklist);
mGnssBatchingProvider = new GnssBatchingProvider();
mGnssGeofenceProvider = new GnssGeofenceProvider();
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_SHUTDOWN);
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (getSendingUserId() == UserHandle.USER_ALL) {
- mEnabled = false;
- handleDisable();
+ mShutdown = true;
+ updateEnabled();
}
}
- }, UserHandle.ALL, intentFilter, null, mHandler);
+ }, UserHandle.ALL, new IntentFilter(Intent.ACTION_SHUTDOWN), null, mHandler);
+
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.LOCATION_MODE),
+ true,
+ new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateEnabled();
+ }
+ }, UserHandle.USER_ALL);
setProperties(PROPERTIES);
setEnabled(true);
@@ -867,12 +869,12 @@
return GPS_POSITION_MODE_STANDALONE;
}
- private void handleEnable() {
+ private boolean handleEnable() {
if (DEBUG) Log.d(TAG, "handleEnable");
- boolean enabled = native_init();
+ boolean inited = native_init();
- if (enabled) {
+ if (inited) {
mSupportsXtra = native_supports_xtra();
// TODO: remove the following native calls if we can make sure they are redundant.
@@ -889,11 +891,10 @@
mGnssNavigationMessageProvider.onGpsEnabledChanged();
mGnssBatchingProvider.enable();
} else {
- synchronized (mLock) {
- mEnabled = false;
- }
Log.w(TAG, "Failed to enable location provider");
}
+
+ return inited;
}
private void handleDisable() {
@@ -912,6 +913,26 @@
mGnssNavigationMessageProvider.onGpsEnabledChanged();
}
+ private void updateEnabled() {
+ synchronized (mLock) {
+ boolean enabled =
+ ((mProviderRequest != null && mProviderRequest.reportLocation
+ && mProviderRequest.forceLocation) || (
+ mContext.getSystemService(LocationManager.class).isLocationEnabled()
+ && !mDisableGps)) && !mShutdown;
+ if (enabled == mEnabled) {
+ return;
+ }
+
+ if (enabled) {
+ mEnabled = handleEnable();
+ } else {
+ mEnabled = false;
+ handleDisable();
+ }
+ }
+ }
+
public boolean isEnabled() {
synchronized (mLock) {
return mEnabled;
@@ -944,6 +965,7 @@
private void handleSetRequest(ProviderRequest request, WorkSource source) {
mProviderRequest = request;
mWorkSource = source;
+ updateEnabled();
updateRequirements();
}
@@ -1492,8 +1514,11 @@
class_init_native();
native_init_once();
if (isEnabled()) {
- // re-calls native_init() and other setup.
- handleEnable();
+ synchronized (mLock) {
+ mEnabled = false;
+ }
+ updateEnabled();
+
// resend configuration into the restarted HAL service.
reloadGpsProperties();
}
@@ -1692,7 +1717,7 @@
/* requestorIdEncoding= */ 0,
/* textEncoding= */ 0,
mSuplEsEnabled,
- mEnabled,
+ isEnabled(),
userResponse);
return true;
@@ -1758,7 +1783,7 @@
notification.requestorIdEncoding,
notification.textEncoding,
mSuplEsEnabled,
- mEnabled,
+ isEnabled(),
/* userResponse= */ 0);
}
@@ -1883,9 +1908,6 @@
public void handleMessage(Message msg) {
int message = msg.what;
switch (message) {
- case ENABLE:
- handleEnable();
- break;
case SET_REQUEST:
GpsRequest gpsRequest = (GpsRequest) msg.obj;
handleSetRequest(gpsRequest.request, gpsRequest.source);
@@ -2000,8 +2022,7 @@
new NetworkLocationListener(),
getLooper());
- // enable gps provider, it will never be disabled (legacy behavior)
- sendEmptyMessage(ENABLE);
+ updateEnabled();
}
}
@@ -2045,8 +2066,6 @@
*/
private String messageIdAsString(int message) {
switch (message) {
- case ENABLE:
- return "ENABLE";
case SET_REQUEST:
return "SET_REQUEST";
case INJECT_NTP_TIME:
diff --git a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
index 77dee82..1fc7192 100644
--- a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
@@ -95,15 +95,11 @@
*/
public void injectGnssMeasurementCorrections(
GnssMeasurementCorrections measurementCorrections) {
- mHandler.post(
- new Runnable() {
- @Override
- public void run() {
- if (!mNative.injectGnssMeasurementCorrections(measurementCorrections)) {
- Log.e(TAG, "Failure in injecting GNSS corrections.");
- }
- }
- });
+ mHandler.post(() -> {
+ if (!mNative.injectGnssMeasurementCorrections(measurementCorrections)) {
+ Log.e(TAG, "Failure in injecting GNSS corrections.");
+ }
+ });
}
@Override
@@ -115,15 +111,14 @@
}
public void onMeasurementsAvailable(final GnssMeasurementsEvent event) {
- foreach(
- (IGnssMeasurementsListener listener, int uid, String packageName) -> {
- if (!hasPermission(uid, packageName)) {
- logPermissionDisabledEventNotReported(
- TAG, packageName, "GNSS measurements");
- return;
- }
- listener.onGnssMeasurementsReceived(event);
- });
+ foreach((IGnssMeasurementsListener listener, CallerIdentity callerIdentity) -> {
+ if (!hasPermission(mContext, callerIdentity)) {
+ logPermissionDisabledEventNotReported(
+ TAG, callerIdentity.mPackageName, "GNSS measurements");
+ return;
+ }
+ listener.onGnssMeasurementsReceived(event);
+ });
}
/** Updates the framework about the capabilities of the GNSS chipset */
@@ -182,7 +177,7 @@
@Override
public void execute(IGnssMeasurementsListener listener,
- int uid, String packageName) throws RemoteException {
+ CallerIdentity callerIdentity) throws RemoteException {
listener.onStatusChanged(mStatus);
}
}
diff --git a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
index 679919f..80a3f9b 100644
--- a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
@@ -86,7 +86,7 @@
}
public void onNavigationMessageAvailable(final GnssNavigationMessage event) {
- foreach((IGnssNavigationMessageListener listener, int uid, String packageName) -> {
+ foreach((IGnssNavigationMessageListener listener, CallerIdentity callerIdentity) -> {
listener.onGnssNavigationMessageReceived(event);
}
);
@@ -136,7 +136,7 @@
@Override
public void execute(IGnssNavigationMessageListener listener,
- int uid, String packageName) throws RemoteException {
+ CallerIdentity callerIdentity) throws RemoteException {
listener.onStatusChanged(mStatus);
}
}
diff --git a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
index 454dbdd..d67d0c5 100644
--- a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
+++ b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
@@ -48,18 +48,18 @@
public void onStatusChanged(boolean isNavigating) {
if (isNavigating) {
- foreach((IGnssStatusListener listener, int uid, String packageName) -> {
+ foreach((IGnssStatusListener listener, CallerIdentity callerIdentity) -> {
listener.onGnssStarted();
});
} else {
- foreach((IGnssStatusListener listener, int uid, String packageName) -> {
+ foreach((IGnssStatusListener listener, CallerIdentity callerIdentity) -> {
listener.onGnssStopped();
});
}
}
public void onFirstFix(final int timeToFirstFix) {
- foreach((IGnssStatusListener listener, int uid, String packageName) -> {
+ foreach((IGnssStatusListener listener, CallerIdentity callerIdentity) -> {
listener.onFirstFix(timeToFirstFix);
}
);
@@ -72,9 +72,10 @@
final float[] elevations,
final float[] azimuths,
final float[] carrierFreqs) {
- foreach((IGnssStatusListener listener, int uid, String packageName) -> {
- if (!hasPermission(uid, packageName)) {
- logPermissionDisabledEventNotReported(TAG, packageName, "GNSS status");
+ foreach((IGnssStatusListener listener, CallerIdentity callerIdentity) -> {
+ if (!hasPermission(mContext, callerIdentity)) {
+ logPermissionDisabledEventNotReported(TAG, callerIdentity.mPackageName,
+ "GNSS status");
return;
}
listener.onSvStatusChanged(svCount, prnWithFlags, cn0s, elevations, azimuths,
@@ -83,9 +84,9 @@
}
public void onNmeaReceived(final long timestamp, final String nmea) {
- foreach((IGnssStatusListener listener, int uid, String packageName) -> {
- if (!hasPermission(uid, packageName)) {
- logPermissionDisabledEventNotReported(TAG, packageName, "NMEA");
+ foreach((IGnssStatusListener listener, CallerIdentity callerIdentity) -> {
+ if (!hasPermission(mContext, callerIdentity)) {
+ logPermissionDisabledEventNotReported(TAG, callerIdentity.mPackageName, "NMEA");
return;
}
listener.onNmeaReceived(timestamp, nmea);
diff --git a/services/core/java/com/android/server/location/LocationPermissionUtil.java b/services/core/java/com/android/server/location/LocationPermissionUtil.java
new file mode 100644
index 0000000..4465f31
--- /dev/null
+++ b/services/core/java/com/android/server/location/LocationPermissionUtil.java
@@ -0,0 +1,62 @@
+/*
+ * 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.server.location;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+/**
+ * Encapsulates utility functions and classes related to location permission checking.
+ */
+public final class LocationPermissionUtil {
+ /**
+ * Returns true if the calling process identified by {@code callerIdentity} is enabled to
+ * report location to AppOps service before providing device location identifiable information
+ * to its clients. Packages with these permissions must report any reporting of location
+ * information to apps, via AppOps.
+ *
+ * <p>The calling package represented by {@code callerIdentity} is considered a part of the
+ * extended Location Manager Service if it has all of the permissions below.
+ * <ul>
+ * <li>{@link android.Manifest.permission#LOCATION_HARDWARE}
+ * <li>{@link android.Manifest.permission#UPDATE_APP_OPS_STATS}
+ * </ul>
+ *
+ * <p>Any package with these permissions, that passes along location information from Android
+ * framework to apps, must report to AppOps, similarly to Location Manager Service - i.e.
+ * whenever it reports device location or location identifiable information such as
+ * GNSS status, GNSS measurements, etc. to its clients.
+ */
+ public static boolean doesCallerReportToAppOps(Context context, CallerIdentity callerIdentity) {
+ return hasPermissionLocationHardware(context, callerIdentity)
+ && hasPermissionUpdateAppOpsStats(context, callerIdentity);
+ }
+
+ private static boolean hasPermissionLocationHardware(Context context,
+ CallerIdentity callerIdentity) {
+ return context.checkPermission(android.Manifest.permission.LOCATION_HARDWARE,
+ callerIdentity.mPid, callerIdentity.mUid) == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private static boolean hasPermissionUpdateAppOpsStats(Context context,
+ CallerIdentity callerIdentity) {
+ return context.checkPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
+ callerIdentity.mPid, callerIdentity.mUid) == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private LocationPermissionUtil() {}
+}
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index 776beb5..34c8786 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 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 @@
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 @@
}
};
+ private final ServiceWatcher mServiceWatcher;
+
+ @GuardedBy("mProviderPackagesLock")
+ private final CopyOnWriteArrayList<String> mProviderPackages = new CopyOnWriteArrayList<>();
+
@GuardedBy("mRequestLock")
@Nullable
private ProviderRequest mRequest;
@@ -101,7 +120,7 @@
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 @@
@Override
protected void onUnbind() {
+ resetProviderPackages(Collections.emptyList());
setEnabled(false);
setProperties(null);
}
@@ -131,6 +151,8 @@
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 @@
}
}
- @Nullable
- public String getConnectedPackageName() {
- return mServiceWatcher.getCurrentPackageName();
+ @Override
+ public List<String> getProviderPackages() {
+ synchronized (mProviderPackagesLock) {
+ return mProviderPackages;
+ }
}
@Override
@@ -193,4 +217,30 @@
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 fe91c63..6accad8 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 @@
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 30260b2..3a841c9 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 @@
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/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java
index e69b2ec..f03c99b 100644
--- a/services/core/java/com/android/server/location/RemoteListenerHelper.java
+++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java
@@ -71,10 +71,10 @@
return mIsRegistered;
}
- public boolean addListener(@NonNull TListener listener, int uid, String packageName) {
+ public boolean addListener(@NonNull TListener listener, CallerIdentity callerIdentity) {
Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener.");
IBinder binder = listener.asBinder();
- LinkedListener deathListener = new LinkedListener(listener, uid, packageName);
+ LinkedListener deathListener = new LinkedListener(listener, callerIdentity);
synchronized (mListenerMap) {
if (mListenerMap.containsKey(binder)) {
// listener already added
@@ -137,7 +137,7 @@
protected abstract ListenerOperation<TListener> getHandlerOperation(int result);
protected interface ListenerOperation<TListener extends IInterface> {
- void execute(TListener listener, int uid, String packageName) throws RemoteException;
+ void execute(TListener listener, CallerIdentity callerIdentity) throws RemoteException;
}
protected void foreach(ListenerOperation<TListener> operation) {
@@ -177,9 +177,16 @@
}
}
- protected boolean hasPermission(int uid, String packageName) {
- return mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, uid, packageName)
- == AppOpsManager.MODE_ALLOWED;
+ protected boolean hasPermission(Context context, CallerIdentity callerIdentity) {
+ if (LocationPermissionUtil.doesCallerReportToAppOps(context, callerIdentity)) {
+ // The caller is identified as a location provider that will report location
+ // access to AppOps. Skip noteOp but do checkOp to check for location permission.
+ return mAppOps.checkOpNoThrow(AppOpsManager.OP_FINE_LOCATION, callerIdentity.mUid,
+ callerIdentity.mPackageName) == AppOpsManager.MODE_ALLOWED;
+ }
+
+ return mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, callerIdentity.mUid,
+ callerIdentity.mPackageName) == AppOpsManager.MODE_ALLOWED;
}
protected void logPermissionDisabledEventNotReported(String tag, String packageName,
@@ -254,13 +261,11 @@
private class LinkedListener implements IBinder.DeathRecipient {
private final TListener mListener;
- private final int mUid;
- private final String mPackageName;
+ private final CallerIdentity mCallerIdentity;
- LinkedListener(@NonNull TListener listener, int uid, String packageName) {
+ LinkedListener(@NonNull TListener listener, CallerIdentity callerIdentity) {
mListener = listener;
- mUid = uid;
- mPackageName = packageName;
+ mCallerIdentity = callerIdentity;
}
@Override
@@ -282,8 +287,7 @@
@Override
public void run() {
try {
- mOperation.execute(mLinkedListener.mListener, mLinkedListener.mUid,
- mLinkedListener.mPackageName);
+ mOperation.execute(mLinkedListener.mListener, mLinkedListener.mCallerIdentity);
} catch (RemoteException e) {
Log.v(mTag, "Error in monitored listener.", e);
}
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 7304f07..0000000
--- 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 be15fda..b85abd9 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 @@
* 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 @@
* @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 ba187c0..34a6663c 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 @@
@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 @@
.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 @@
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 ab49ebb..b3394b4 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 @@
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 @@
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 f736056..1dada92 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.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 @@
@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 @@
&& 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);
}
- return true;
+ }
+
+ /**
+ * 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);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private boolean isDumpstateBinderServiceRunningLocked() {
+ IDumpstate ds = IDumpstate.Stub.asInterface(ServiceManager.getService("dumpstate"));
+ return ds != null;
}
/*
@@ -115,8 +178,12 @@
* <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 @@
}
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 e405f25..e18da7f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -984,6 +984,9 @@
@GuardedBy("mPackages")
private CheckPermissionDelegate mCheckPermissionDelegate;
+ @GuardedBy("mPackages")
+ private PackageManagerInternal.DefaultBrowserProvider mDefaultBrowserProvider;
+
private class IntentVerifierProxy implements IntentFilterVerifier<ActivityIntentInfo> {
private Context mContext;
private ComponentName mIntentFilterVerifierComponent;
@@ -1927,7 +1930,7 @@
final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting.getInstallReason(userId)
!= PackageManager.INSTALL_REASON_DEVICE_RESTORE) {
- mSettings.setDefaultBrowserPackageNameLPw(null, userId);
+ setDefaultBrowserPackageName(null, userId);
}
}
@@ -2941,7 +2944,6 @@
if (!onlyCore && (mPromoteSystemApps || mFirstBoot)) {
for (UserInfo user : sUserManager.getUsers(true)) {
mSettings.applyDefaultPreferredAppsLPw(user.id);
- applyFactoryDefaultBrowserLPw(user.id);
primeDomainVerificationsLPw(user.id);
}
}
@@ -3014,8 +3016,6 @@
ver.fingerprint = Build.FINGERPRINT;
}
- checkDefaultBrowser();
-
// clear only after permissions and other defaults have been updated
mExistingSystemPackages.clear();
mPromoteSystemApps = false;
@@ -3670,58 +3670,6 @@
scheduleWriteSettingsLocked();
}
- @GuardedBy("mPackages")
- private void applyFactoryDefaultBrowserLPw(int userId) {
- // The default browser app's package name is stored in a string resource,
- // with a product-specific overlay used for vendor customization.
- String browserPkg = mContext.getResources().getString(
- com.android.internal.R.string.default_browser);
- if (!TextUtils.isEmpty(browserPkg)) {
- // non-empty string => required to be a known package
- PackageSetting ps = mSettings.mPackages.get(browserPkg);
- if (ps == null) {
- Slog.e(TAG, "Product default browser app does not exist: " + browserPkg);
- browserPkg = null;
- } else {
- mSettings.setDefaultBrowserPackageNameLPw(browserPkg, userId);
- }
- }
-
- // Nothing valid explicitly set? Make the factory-installed browser the explicit
- // default. If there's more than one, just leave everything alone.
- if (browserPkg == null) {
- calculateDefaultBrowserLPw(userId);
- }
- }
-
- @GuardedBy("mPackages")
- private void calculateDefaultBrowserLPw(int userId) {
- List<String> allBrowsers = resolveAllBrowserApps(userId);
- final String browserPkg = (allBrowsers.size() == 1) ? allBrowsers.get(0) : null;
- mSettings.setDefaultBrowserPackageNameLPw(browserPkg, userId);
- }
-
- private List<String> resolveAllBrowserApps(int userId) {
- // Resolve the canonical browser intent and check that the handleAllWebDataURI boolean is set
- List<ResolveInfo> list = queryIntentActivitiesInternal(sBrowserIntent, null,
- PackageManager.MATCH_ALL, userId);
-
- final int count = list.size();
- List<String> result = new ArrayList<>(count);
- for (int i=0; i<count; i++) {
- ResolveInfo info = list.get(i);
- if (info.activityInfo == null
- || !info.handleAllWebDataURI
- || (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0
- || result.contains(info.activityInfo.packageName)) {
- continue;
- }
- result.add(info.activityInfo.packageName);
- }
-
- return result;
- }
-
private boolean packageIsBrowser(String packageName, int userId) {
List<ResolveInfo> list = queryIntentActivitiesInternal(sBrowserIntent, null,
PackageManager.MATCH_ALL, userId);
@@ -3735,20 +3683,6 @@
return false;
}
- private void checkDefaultBrowser() {
- final int myUserId = UserHandle.myUserId();
- final String packageName = getDefaultBrowserPackageName(myUserId);
- if (packageName != null) {
- PackageInfo info = getPackageInfo(packageName, 0, myUserId);
- if (info == null) {
- Slog.w(TAG, "Default browser no longer installed: " + packageName);
- synchronized (mPackages) {
- applyFactoryDefaultBrowserLPw(myUserId); // leaves ambiguous when > 1
- }
- }
- }
- }
-
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -4007,13 +3941,13 @@
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) {
@@ -13492,6 +13426,10 @@
return false;
}
+ if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) {
+ return false;
+ }
+
boolean ensureVerifyAppsEnabled = isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS);
// Check if installing from ADB
@@ -13645,15 +13583,28 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
}
-
- synchronized (mPackages) {
- boolean result = mSettings.setDefaultBrowserPackageNameLPw(packageName, userId);
- if (packageName != null) {
- mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultBrowser(
- packageName, userId);
- }
- return result;
+ if (userId == UserHandle.USER_ALL) {
+ return false;
}
+ PackageManagerInternal.DefaultBrowserProvider provider;
+ synchronized (mPackages) {
+ provider = mDefaultBrowserProvider;
+ }
+ if (provider == null) {
+ Slog.e(TAG, "mDefaultBrowserProvider is null");
+ return false;
+ }
+ boolean successful = provider.setDefaultBrowser(packageName, userId);
+ if (!successful) {
+ return false;
+ }
+ if (packageName != null) {
+ synchronized (mPackages) {
+ mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultBrowser(packageName,
+ userId);
+ }
+ }
+ return true;
}
@Override
@@ -13665,9 +13616,15 @@
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return null;
}
+ PackageManagerInternal.DefaultBrowserProvider provider;
synchronized (mPackages) {
- return mSettings.getDefaultBrowserPackageNameLPw(userId);
+ provider = mDefaultBrowserProvider;
}
+ if (provider == null) {
+ Slog.e(TAG, "mDefaultBrowserProvider is null");
+ return null;
+ }
+ return provider.getDefaultBrowser(userId);
}
/**
@@ -19419,7 +19376,7 @@
// significant refactoring to keep all default apps in the package
// manager (cleaner but more work) or have the services provide
// callbacks to the package manager to request a default app reset.
- applyFactoryDefaultBrowserLPw(userId);
+ setDefaultBrowserPackageName(null, userId);
clearIntentFilterVerificationsLPw(userId);
primeDomainVerificationsLPw(userId);
resetUserChangesToRuntimePermissionsAndFlagsLPw(userId);
@@ -22761,7 +22718,6 @@
synchronized (mPackages) {
scheduleWritePackageRestrictionsLocked(userId);
scheduleWritePackageListLocked(userId);
- applyFactoryDefaultBrowserLPw(userId);
primeDomainVerificationsLPw(userId);
}
}
@@ -23959,6 +23915,21 @@
public void finishPackageInstall(int token, boolean didLaunch) {
PackageManagerService.this.finishPackageInstall(token, didLaunch);
}
+
+ @Nullable
+ @Override
+ public String removeLegacyDefaultBrowserPackageName(int userId) {
+ synchronized (mPackages) {
+ 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/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index b0f2326..975ffb2 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1327,21 +1327,8 @@
return result;
}
- boolean setDefaultBrowserPackageNameLPw(String packageName, int userId) {
- if (userId == UserHandle.USER_ALL) {
- return false;
- }
- if (packageName != null) {
- mDefaultBrowserApp.put(userId, packageName);
- } else {
- mDefaultBrowserApp.remove(userId);
- }
- writePackageRestrictionsLPr(userId);
- return true;
- }
-
- String getDefaultBrowserPackageNameLPw(int userId) {
- return (userId == UserHandle.USER_ALL) ? null : mDefaultBrowserApp.get(userId);
+ String removeDefaultBrowserPackageNameLPw(int userId) {
+ return (userId == UserHandle.USER_ALL) ? null : mDefaultBrowserApp.removeReturnOld(userId);
}
boolean setDefaultDialerPackageNameLPw(String packageName, int userId) {
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 0371663..55eb7ea 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.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.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 @@
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 @@
}
}
+ 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 @@
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 09bacd6..0892b32 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/policy/role/LegacyRoleResolutionPolicy.java b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
index bfe7725..e7de8dd 100644
--- a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
+++ b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
@@ -20,6 +20,7 @@
import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManagerInternal;
import android.os.Debug;
import android.provider.Settings;
import android.telecom.TelecomManager;
@@ -29,6 +30,7 @@
import com.android.internal.telephony.SmsApplication;
import com.android.internal.util.CollectionUtils;
+import com.android.server.LocalServices;
import com.android.server.role.RoleManagerService;
import java.util.Collection;
@@ -113,6 +115,13 @@
? setting
: mContext.getSystemService(TelecomManager.class).getSystemDialerPackage());
}
+ case RoleManager.ROLE_BROWSER: {
+ PackageManagerInternal packageManagerInternal = LocalServices.getService(
+ PackageManagerInternal.class);
+ String packageName = packageManagerInternal.removeLegacyDefaultBrowserPackageName(
+ userId);
+ return CollectionUtils.singletonOrEmpty(packageName);
+ }
default: {
Slog.e(LOG_TAG, "Don't know how to find legacy role holders for " + roleName);
return Collections.emptyList();
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index 1d74e1f..a122ed85 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -128,7 +128,7 @@
new ArrayMap<>(), /* filesForNoninteractive */
false, /* forceAllAppsStandby */
false, /* forceBackgroundCheck */
- PowerManager.LOCATION_MODE_NO_CHANGE /* gpsMode */
+ PowerManager.LOCATION_MODE_NO_CHANGE /* locationMode */
);
private static final Policy DEFAULT_ADAPTIVE_POLICY = OFF_POLICY;
@@ -152,7 +152,7 @@
new ArrayMap<>(), /* filesForNoninteractive */
true, /* forceAllAppsStandby */
true, /* forceBackgroundCheck */
- PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF /* gpsMode */
+ PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF /* locationMode */
);
private final Object mLock;
@@ -416,7 +416,7 @@
if (currPolicy.disableAod) sb.append("o");
if (currPolicy.enableQuickDoze) sb.append("q");
- sb.append(currPolicy.gpsMode);
+ sb.append(currPolicy.locationMode);
mEventLogKeys = sb.toString();
}
@@ -554,12 +554,13 @@
public final boolean forceBackgroundCheck;
/**
- * This is the flag to decide the gps mode in battery saver mode.
+ * This is the flag to decide the location mode in battery saver mode. This was
+ * previously called gpsMode.
*
* @see Settings.Global#BATTERY_SAVER_CONSTANTS
* @see #KEY_GPS_MODE
*/
- public final int gpsMode;
+ public final int locationMode;
private final int mHashCode;
@@ -582,7 +583,7 @@
ArrayMap<String, String> filesForNoninteractive,
boolean forceAllAppsStandby,
boolean forceBackgroundCheck,
- int gpsMode) {
+ int locationMode) {
this.adjustBrightnessFactor = adjustBrightnessFactor;
this.advertiseIsEnabled = advertiseIsEnabled;
@@ -602,7 +603,7 @@
this.filesForNoninteractive = filesForNoninteractive;
this.forceAllAppsStandby = forceAllAppsStandby;
this.forceBackgroundCheck = forceBackgroundCheck;
- this.gpsMode = gpsMode;
+ this.locationMode = locationMode;
mHashCode = Objects.hash(
adjustBrightnessFactor,
@@ -623,7 +624,7 @@
filesForNoninteractive,
forceAllAppsStandby,
forceBackgroundCheck,
- gpsMode);
+ locationMode);
}
static Policy fromConfig(BatterySaverPolicyConfig config) {
@@ -721,7 +722,7 @@
defaultPolicy.forceAllAppsStandby);
boolean forceBackgroundCheck = parser.getBoolean(KEY_FORCE_BACKGROUND_CHECK,
defaultPolicy.forceBackgroundCheck);
- int gpsMode = parser.getInt(KEY_GPS_MODE, defaultPolicy.gpsMode);
+ int locationMode = parser.getInt(KEY_GPS_MODE, defaultPolicy.locationMode);
return new Policy(
adjustBrightnessFactor,
@@ -745,7 +746,7 @@
(new CpuFrequencies()).parseString(cpuFreqNoninteractive).toSysFileMap(),
forceAllAppsStandby,
forceBackgroundCheck,
- gpsMode
+ locationMode
);
}
@@ -770,7 +771,7 @@
&& enableQuickDoze == other.enableQuickDoze
&& forceAllAppsStandby == other.forceAllAppsStandby
&& forceBackgroundCheck == other.forceBackgroundCheck
- && gpsMode == other.gpsMode
+ && locationMode == other.locationMode
&& filesForInteractive.equals(other.filesForInteractive)
&& filesForNoninteractive.equals(other.filesForNoninteractive);
}
@@ -795,11 +796,11 @@
final PowerSaveState.Builder builder = new PowerSaveState.Builder()
.setGlobalBatterySaverEnabled(currPolicy.advertiseIsEnabled);
switch (type) {
- case ServiceType.GPS:
+ case ServiceType.LOCATION:
boolean isEnabled = currPolicy.advertiseIsEnabled
- || currPolicy.gpsMode != PowerManager.LOCATION_MODE_NO_CHANGE;
+ || currPolicy.locationMode != PowerManager.LOCATION_MODE_NO_CHANGE;
return builder.setBatterySaverEnabled(isEnabled)
- .setGpsMode(currPolicy.gpsMode)
+ .setLocationMode(currPolicy.locationMode)
.build();
case ServiceType.ANIMATION:
return builder.setBatterySaverEnabled(currPolicy.disableAnimation)
@@ -910,7 +911,7 @@
public int getGpsMode() {
synchronized (mLock) {
- return getCurrentPolicyLocked().gpsMode;
+ return getCurrentPolicyLocked().locationMode;
}
}
@@ -995,7 +996,7 @@
pw.print(indent);
pw.println(" " + KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + p.adjustBrightnessFactor);
pw.print(indent);
- pw.println(" " + KEY_GPS_MODE + "=" + p.gpsMode);
+ pw.println(" " + KEY_GPS_MODE + "=" + p.locationMode);
pw.print(indent);
pw.println(" " + KEY_FORCE_ALL_APPS_STANDBY + "=" + p.forceAllAppsStandby);
pw.print(indent);
diff --git a/services/core/java/com/android/server/role/RemoteRoleControllerService.java b/services/core/java/com/android/server/role/RemoteRoleControllerService.java
index be2544d..107cb2c 100644
--- a/services/core/java/com/android/server/role/RemoteRoleControllerService.java
+++ b/services/core/java/com/android/server/role/RemoteRoleControllerService.java
@@ -101,6 +101,20 @@
callback));
}
+ /**
+ * @see RoleControllerService#onSmsKillSwitchToggled(boolean)
+ */
+ public void onSmsKillSwitchToggled(boolean smsRestrictionEnabled) {
+ mConnection.enqueueCall(new Connection.Call(
+ (s, cb) -> s.onSmsKillSwitchToggled(smsRestrictionEnabled),
+ new IRoleManagerCallback.Default() {
+ @Override
+ public void onFailure() {
+ Slog.e(LOG_TAG, "Failed onSmsKillSwitchToggled");
+ }
+ }));
+ }
+
private static final class Connection implements ServiceConnection {
private static final long UNBIND_DELAY_MILLIS = 15 * 1000;
diff --git a/services/core/java/com/android/server/role/RoleManagerServiceInternal.java b/services/core/java/com/android/server/role/RoleManagerInternal.java
similarity index 71%
rename from services/core/java/com/android/server/role/RoleManagerServiceInternal.java
rename to services/core/java/com/android/server/role/RoleManagerInternal.java
index 3afc3f7..7598bf7 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 d72270e..84305be 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -38,7 +38,9 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
+import android.database.ContentObserver;
import android.database.CursorWindow;
+import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -49,6 +51,7 @@
import android.os.ShellCallback;
import android.os.UserHandle;
import android.os.UserManagerInternal;
+import android.provider.Settings;
import android.service.sms.FinancialSmsService;
import android.telephony.IFinancialSmsCallback;
import android.text.TextUtils;
@@ -146,8 +149,11 @@
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();
}
@@ -189,6 +195,18 @@
performInitialGrantsIfNecessary(userId);
}
}, UserHandle.SYSTEM, intentFilter, null /* broadcastPermission */, null /* handler */);
+
+ getContext().getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED), false,
+ new ContentObserver(getContext().getMainThreadHandler()) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri, int userId) {
+ getOrCreateControllerService(userId).onSmsKillSwitchToggled(
+ Settings.Global.getInt(
+ getContext().getContentResolver(),
+ Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, 0) == 1);
+ }
+ }, UserHandle.USER_ALL);
}
@Override
@@ -360,10 +378,10 @@
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 +404,6 @@
}
}
- /**
- * 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 +411,7 @@
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 +422,7 @@
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 +437,17 @@
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 +458,7 @@
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 +475,7 @@
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 +492,7 @@
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 +507,7 @@
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 +524,7 @@
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 +543,7 @@
"setRoleNamesFromController");
int userId = UserHandle.getCallingUserId();
- RoleUserState userState = getOrCreateUserState(userId);
- userState.setRoleNames(roleNames);
+ getOrCreateUserState(userId).setRoleNames(roleNames);
}
@Override
@@ -562,8 +556,7 @@
"addRoleHolderFromController");
int userId = UserHandle.getCallingUserId();
- RoleUserState userState = getOrCreateUserState(userId);
- return userState.addRoleHolder(roleName, packageName);
+ return getOrCreateUserState(userId).addRoleHolder(roleName, packageName);
}
@Override
@@ -576,8 +569,7 @@
"removeRoleHolderFromController");
int userId = UserHandle.getCallingUserId();
- RoleUserState userState = getOrCreateUserState(userId);
- return userState.removeRoleHolder(roleName, packageName);
+ return getOrCreateUserState(userId).removeRoleHolder(roleName, packageName);
}
@Override
@@ -588,13 +580,12 @@
"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 +685,51 @@
}
}
- /**
- * 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 5030e34..c7e3fa4 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 @@
* @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 @@
}
}
- 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 0000000..9efd292
--- /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 a4f3064..467bb28 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 @@
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 @@
// 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 f5b37b4..7885abe 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.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 @@
}
}, 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 @@
return false;
}
- return enableRollbackForSession(session, installedUsers);
+ return enableRollbackForSession(session, installedUsers, true);
}
/**
@@ -727,7 +739,7 @@
* 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 @@
}
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 @@
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 @@
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 @@
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 @@
});
}
+ @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,60 +947,100 @@
@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();
+ completeEnableRollback(sessionId, success);
+ }
+ }
- 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);
+ 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);
}
- } else {
- // The install session was aborted, clean up the pending
- // install.
+ // 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);
}
+ } 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");
+ }
+ }
+
/*
* Returns the RollbackData, if any, for an available rollback that would
* roll back the given package. Note: This assumes we have at most one
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 8e04160..3c6a54a 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.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 @@
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 @@
}
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;
}
+ 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;
- StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
- StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
- moduleMetadataPackage.getPackageName(),
- moduleMetadataPackage.getVersionCode());
+ 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 @@
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 @@
&& 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 @@
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 1069530..ff51690 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 @@
*/
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 @@
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 @@
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 @@
json.put("pendingBackups", convertToJsonArray(pendingBackups));
json.put("pendingRestores", convertToJsonArray(pendingRestores));
+ json.put("isApex", info.isApex());
+
return json;
}
@@ -345,8 +356,10 @@
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 48e64338..3586772 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.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 @@
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 @@
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 @@
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 d932a40..0493ae90 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 @@
@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 @@
@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 111808b..18df88b 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.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 @@
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 66666e6..f67b11b 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 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 @@
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 @@
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 @@
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 bb0cbb1..afae9c4 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 @@
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 39568fe..888d741 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 @@
mRotation = rotation;
+ updateSurfacePosition();
return boundsChange;
}
@@ -358,6 +359,7 @@
} 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 feac6c6..b246da4 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -45,6 +45,7 @@
import android.view.SurfaceControl.Builder;
import android.view.SurfaceSession;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.wm.SurfaceAnimator.Animatable;
@@ -1331,6 +1332,11 @@
mLastSurfacePosition.set(mTmpPos.x, mTmpPos.y);
}
+ @VisibleForTesting
+ 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 146a8ed..3bb6608 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 @@
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 @@
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 @@
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 @@
}
}
}
+ break;
}
- break;
case CHECK_IF_BOOT_ANIMATION_FINISHED: {
final boolean bootAnimationComplete;
synchronized (mGlobalLock) {
@@ -4667,15 +4670,15 @@
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 @@
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 @@
}
mWindowReplacementTimeouts.clear();
}
+ break;
}
- break;
case WINDOW_HIDE_TIMEOUT: {
final WindowState window = (WindowState) msg.obj;
synchronized (mGlobalLock) {
@@ -4724,44 +4727,44 @@
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 3729eaf..5c7b287 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -127,6 +127,7 @@
"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 024760d..5c19ad3 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 @@
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 @@
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 @@
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 0c9b5f4..9be728b 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::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 @@
}
static sp<ISystemSuspend> gSuspendHal = nullptr;
+static sp<ISuspendControlService> gSuspendControl = nullptr;
static sp<IWakeLock> gSuspendBlocker = nullptr;
static std::mutex gSuspendMutex;
@@ -191,18 +195,33 @@
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 988d75c..bf96f9a 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 @@
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 @@
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 @@
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 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 @@
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 @@
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 @@
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 71ed5ae..ab30cda 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.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 @@
}
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 @@
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 48c8902..11bd29d 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.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 @@
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 @@
@Override
void setArcStatus(boolean enabled) {
- mSetArcStatusCalled = true;
mArcEnabled = enabled;
}
};
@@ -110,45 +110,38 @@
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/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index feae4ee..b8799c3 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -60,6 +60,11 @@
boolean isControlEnabled() {
return true;
}
+
+ @Override
+ void writeStringSystemProperty(String key, String value) {
+ // do nothing
+ }
};
mMyLooper = mTestLooper.getLooper();
@@ -92,4 +97,39 @@
// TODO(amyjojo): Move set and get LocalActivePath to Control Service.
assertThat(mHdmiCecLocalDevicePlayback.getLocalActivePath()).isEqualTo(1);
}
+
+ @Test
+ public void handleSetSystemAudioModeOn_audioSystemBroadcast() {
+ mHdmiControlService.setSystemAudioActivated(false);
+ assertThat(mHdmiCecLocalDevicePlayback.mService.isSystemAudioActivated()).isFalse();
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildSetSystemAudioMode(
+ Constants.ADDR_AUDIO_SYSTEM, Constants.ADDR_BROADCAST, true);
+ assertThat(mHdmiCecLocalDevicePlayback.handleSetSystemAudioMode(message)).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.mService.isSystemAudioActivated()).isTrue();
+ }
+
+ @Test
+ public void handleSetSystemAudioModeOff_audioSystemToPlayback() {
+ mHdmiCecLocalDevicePlayback.mService.setSystemAudioActivated(true);
+ assertThat(mHdmiCecLocalDevicePlayback.mService.isSystemAudioActivated()).isTrue();
+ // This direct message to Playback device is invalid.
+ // Test should ignore it and still keep the system audio mode on.
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildSetSystemAudioMode(
+ Constants.ADDR_AUDIO_SYSTEM, mHdmiCecLocalDevicePlayback.mAddress, false);
+ assertThat(mHdmiCecLocalDevicePlayback.handleSetSystemAudioMode(message)).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.mService.isSystemAudioActivated()).isTrue();
+ }
+
+ @Test
+ public void handleSystemAudioModeStatusOn_DirectltToLocalDeviceFromAudioSystem() {
+ mHdmiControlService.setSystemAudioActivated(false);
+ assertThat(mHdmiCecLocalDevicePlayback.mService.isSystemAudioActivated()).isFalse();
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildReportSystemAudioMode(
+ Constants.ADDR_AUDIO_SYSTEM, mHdmiCecLocalDevicePlayback.mAddress, true);
+ assertThat(mHdmiCecLocalDevicePlayback.handleSystemAudioModeStatus(message)).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.mService.isSystemAudioActivated()).isTrue();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
index e32a789..9dcacf3 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
@@ -166,12 +166,12 @@
@SmallTest
public void testGetBatterySaverPolicy_PolicyGps_DefaultValueCorrect() {
- testServiceDefaultValue_On(ServiceType.GPS);
+ testServiceDefaultValue_On(ServiceType.LOCATION);
mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_FULL);
PowerSaveState stateOn =
- mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.GPS);
- assertThat(stateOn.gpsMode).isEqualTo(DEFAULT_GPS_MODE);
+ mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION);
+ assertThat(stateOn.locationMode).isEqualTo(DEFAULT_GPS_MODE);
}
@SmallTest
@@ -222,9 +222,9 @@
assertThat(dataSaverState.batterySaverEnabled).isTrue();
final PowerSaveState gpsState =
- mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.GPS);
+ mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION);
assertThat(gpsState.batterySaverEnabled).isTrue();
- assertThat(gpsState.gpsMode).isEqualTo(GPS_MODE);
+ assertThat(gpsState.locationMode).isEqualTo(GPS_MODE);
final PowerSaveState quickDozeState = mBatterySaverPolicy.getBatterySaverPolicy(
ServiceType.QUICK_DOZE);
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 33cbf7a..f1e6bc2 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 @@
}
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 4cae3b3..72aa0f2 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 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 ae211d3..4184201 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.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 @@
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 85939d4..a6d7ee6 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 @@
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 @@
*/
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 d52d32f..3cb2216 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 @@
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 f5b4308..37caeb2 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -58,6 +58,8 @@
"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 4da79b3..b407b2a 100644
--- a/telephony/java/android/telephony/AvailableNetworkInfo.java
+++ b/telephony/java/android/telephony/AvailableNetworkInfo.java
@@ -114,7 +114,7 @@
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 a4cce9c..0d4f09f 100644
--- a/telephony/java/android/telephony/CallAttributes.java
+++ b/telephony/java/android/telephony/CallAttributes.java
@@ -117,9 +117,9 @@
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/DebugEventReporter.java b/telephony/java/android/telephony/DebugEventReporter.java
new file mode 100644
index 0000000..14b7dd6
--- /dev/null
+++ b/telephony/java/android/telephony/DebugEventReporter.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.ParcelUuid;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * A Simple Surface for Telephony to notify a loosely-coupled debugger of particular issues.
+ *
+ * DebugEventReporter allows an optional external logging component to receive events detected by
+ * the framework and take action. This log surface is designed to provide maximium flexibility
+ * to the receiver of these events. Envisioned use cases of this include notifying a vendor
+ * component of: an event that necessitates (timely) log collection on non-AOSP components;
+ * notifying a vendor component of a rare event that should prompt further action such as a
+ * bug report or user intervention for debug purposes.
+ *
+ * <p>This surface is not intended to enable a diagnostic monitor, nor is it intended to support
+ * streaming logs.
+ *
+ * @hide
+ */
+public final class DebugEventReporter {
+ private static final String TAG = "DebugEventReporter";
+
+ private static Context sContext = null;
+
+ private static Map<UUID, Integer> sEvents = new ConcurrentHashMap<>();
+
+ /*
+ * Because this is only supporting system packages, once we find a package, it will be the
+ * same package until the next system upgrade. Thus, to save time in processing debug events
+ * we can cache this info and skip the resolution process after it's done the first time.
+ */
+ private static String sDebugPackageName = null;
+
+ private DebugEventReporter() {};
+
+ /**
+ * If enabled, build and send an intent to a Debug Service for logging.
+ *
+ * This method sends the {@link TelephonyManager#DEBUG_EVENT DEBUG_EVENT} broadcast, which is
+ * system protected. Invoking this method unless you are the system will result in an error.
+ *
+ * @param eventId a fixed event ID that will be sent for each instance of the same event. This
+ * ID should be generated randomly.
+ * @param description an optional description, that if included will be used as the subject for
+ * identification and discussion of this event. This description should ideally be
+ * static and must not contain any sensitive information (especially PII).
+ */
+ public static void sendEvent(@NonNull UUID eventId, String description) {
+ if (sContext == null) {
+ Rlog.w(TAG, "DebugEventReporter not yet initialized, dropping event=" + eventId);
+ return;
+ }
+
+ // If this event has already occurred, skip sending intents for it; regardless log its
+ // invocation here.
+ Integer count = sEvents.containsKey(eventId) ? sEvents.get(eventId) + 1 : 1;
+ sEvents.put(eventId, count);
+ if (count > 1) return;
+
+ // Even if we are initialized, that doesn't mean that a package name has been found.
+ // This is normal in many cases, such as when no debug package is installed on the system,
+ // so drop these events silently.
+ if (sDebugPackageName == null) return;
+
+ Intent dbgIntent = new Intent(TelephonyManager.ACTION_DEBUG_EVENT);
+ dbgIntent.putExtra(TelephonyManager.EXTRA_DEBUG_EVENT_ID, new ParcelUuid(eventId));
+ if (description != null) {
+ dbgIntent.putExtra(TelephonyManager.EXTRA_DEBUG_EVENT_DESCRIPTION, description);
+ }
+ dbgIntent.setPackage(sDebugPackageName);
+ sContext.sendBroadcast(dbgIntent, android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ }
+
+ /**
+ * Initialize the DebugEventReporter with the current context.
+ *
+ * This method must be invoked before any calls to sendEvent() will succeed. This method should
+ * only be invoked at most once.
+ *
+ * @param context a Context object used to initialize this singleton DebugEventReporter in
+ * the current process.
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public static void initialize(@NonNull Context context) {
+ if (context == null) {
+ throw new IllegalArgumentException("DebugEventReporter needs a non-null context.");
+ }
+
+ // Ensure that this context has sufficient permissions to send debug events.
+ context.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE,
+ "This app does not have privileges to send debug events");
+
+ sContext = context;
+
+ // Check to see if there is a valid debug package; if there are multiple, that's a config
+ // error, so just take the first one.
+ PackageManager pm = sContext.getPackageManager();
+ if (pm == null) return;
+ List<ResolveInfo> packages = pm.queryBroadcastReceivers(
+ new Intent(TelephonyManager.ACTION_DEBUG_EVENT),
+ PackageManager.MATCH_SYSTEM_ONLY
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
+ if (packages == null || packages.isEmpty()) return;
+ if (packages.size() > 1) {
+ Rlog.e(TAG, "Multiple DebugEvent Receivers installed.");
+ }
+
+ for (ResolveInfo r : packages) {
+ if (r.activityInfo == null
+ || pm.checkPermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ r.activityInfo.packageName)
+ != PackageManager.PERMISSION_GRANTED) {
+ Rlog.w(TAG,
+ "Found package without proper permissions or no activity"
+ + r.activityInfo.packageName);
+ continue;
+ }
+ Rlog.d(TAG, "Found a valid package " + r.activityInfo.packageName);
+ sDebugPackageName = r.activityInfo.packageName;
+ break;
+ }
+ // Initialization may only be performed once.
+ }
+
+ /** Dump the contents of the DebugEventReporter */
+ public static void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ if (sContext == null) return;
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
+ sContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "Requires DUMP");
+ pw.println("Initialized=" + (sContext != null ? "Yes" : "No"));
+ pw.println("Debug Package=" + sDebugPackageName);
+ pw.println("Event Counts:");
+ pw.increaseIndent();
+ for (UUID event : sEvents.keySet()) {
+ pw.println(event + ": " + sEvents.get(event));
+ }
+ pw.decreaseIndent();
+ pw.flush();
+ }
+}
diff --git a/telephony/java/android/telephony/PreciseCallState.java b/telephony/java/android/telephony/PreciseCallState.java
index 59f3e1f..19e1931 100644
--- a/telephony/java/android/telephony/PreciseCallState.java
+++ b/telephony/java/android/telephony/PreciseCallState.java
@@ -287,11 +287,11 @@
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 7c3bde4..7395894 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1351,6 +1351,38 @@
@SystemApi
public static final long MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS = 60000;
+ /**
+ * Intent sent when an error occurs that debug tools should log and possibly take further
+ * action such as capturing vendor-specific logs.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public static final String ACTION_DEBUG_EVENT = "android.telephony.action.DEBUG_EVENT";
+
+ /**
+ * An arbitrary ParcelUuid which should be consistent for each occurrence of the same event.
+ *
+ * This field must be included in all events.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_DEBUG_EVENT_ID = "android.telephony.extra.DEBUG_EVENT_ID";
+
+ /**
+ * A freeform string description of the event.
+ *
+ * This field is optional for all events and as a guideline should not exceed 80 characters
+ * and should be as short as possible to convey the essence of the event.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_DEBUG_EVENT_DESCRIPTION =
+ "android.telephony.extra.DEBUG_EVENT_DESCRIPTION";
+
//
//
// Device Info
@@ -7974,9 +8006,7 @@
* 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 +9732,10 @@
*
* <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 +10123,17 @@
* 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 4d95e55..d8d2d9e 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -465,7 +465,7 @@
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 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 @@
/**
* @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/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index b171f79..6e98a0a 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -372,14 +372,6 @@
}
}
- private static SubscriptionManager getSubscriptionManager(Context context) {
- SubscriptionManager manager = context.getSystemService(SubscriptionManager.class);
- if (manager == null) {
- throw new RuntimeException("Could not find SubscriptionManager.");
- }
- return manager;
- }
-
private static ITelephony getITelephony() {
ITelephony binder = ITelephony.Stub.asInterface(
ServiceManager.getService(Context.TELEPHONY_SERVICE));
diff --git a/telephony/java/android/telephony/mbms/GroupCallCallback.java b/telephony/java/android/telephony/mbms/GroupCallCallback.java
index 77e36bb..603f4e6 100644
--- a/telephony/java/android/telephony/mbms/GroupCallCallback.java
+++ b/telephony/java/android/telephony/mbms/GroupCallCallback.java
@@ -57,7 +57,7 @@
* @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 @@
* 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 @@
* {@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 04e7ba1..ac7e172 100644
--- a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
@@ -57,7 +57,7 @@
* @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 @@
* @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 @@
* @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 @@
* 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 780bb24..40d4eff 100644
--- a/tests/RollbackTest/Android.mk
+++ b/tests/RollbackTest/Android.mk
@@ -76,12 +76,14 @@
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 0000000..a2a8e17
--- /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 0000000..e871146
--- /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 0000000..c3239ca
--- /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 0000000..9de3f45
--- /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
new file mode 100644
index 0000000..b347331
--- /dev/null
+++ b/tests/RollbackTest/TestApex/com.android.tests.rollback.testapex.avbpubkey
Binary files differ
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 0000000..7181ce5
--- /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 ace0e6d..4b277ae 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.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 @@
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 c50e6a7..c5b9cf1 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -1099,9 +1099,13 @@
*/
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 @@
"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"
};
/**