diff options
272 files changed, 8761 insertions, 3074 deletions
diff --git a/apex/Android.bp b/apex/Android.bp index 0a535a8fe9b9..4e80acb64e05 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -15,6 +15,7 @@ mainline_stubs_args = "--error UnhiddenSystemApi " + "--hide BroadcastBehavior " + + "--hide CallbackInterface " + "--hide DeprecationMismatch " + "--hide HiddenSuperclass " + "--hide HiddenTypedefConstant " + diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt index 2be873cc8bca..380c64679fab 100644 --- a/apex/appsearch/framework/api/current.txt +++ b/apex/appsearch/framework/api/current.txt @@ -1,6 +1,27 @@ // Signature format: 2.0 package android.app.appsearch { + public final class AppSearchBatchResult<KeyType, ValueType> { + method @NonNull public java.util.Map<KeyType,android.app.appsearch.AppSearchResult<ValueType>> getFailures(); + method @NonNull public java.util.Map<KeyType,ValueType> getSuccesses(); + method public boolean isSuccess(); + } + + public final class AppSearchResult<ValueType> { + method @Nullable public String getErrorMessage(); + method public int getResultCode(); + method @Nullable public ValueType getResultValue(); + method public boolean isSuccess(); + field public static final int RESULT_INTERNAL_ERROR = 2; // 0x2 + field public static final int RESULT_INVALID_ARGUMENT = 3; // 0x3 + field public static final int RESULT_INVALID_SCHEMA = 7; // 0x7 + field public static final int RESULT_IO_ERROR = 4; // 0x4 + field public static final int RESULT_NOT_FOUND = 6; // 0x6 + field public static final int RESULT_OK = 0; // 0x0 + field public static final int RESULT_OUT_OF_SPACE = 5; // 0x5 + field public static final int RESULT_UNKNOWN_ERROR = 1; // 0x1 + } + public final class AppSearchSchema { method @NonNull public java.util.List<android.app.appsearch.AppSearchSchema.PropertyConfig> getProperties(); method @NonNull public String getSchemaType(); @@ -85,6 +106,43 @@ package android.app.appsearch { method @NonNull public BuilderType setTtlMillis(long); } + public final class GetByUriRequest { + method @NonNull public String getNamespace(); + method @NonNull public java.util.Set<java.lang.String> getUris(); + } + + public static final class GetByUriRequest.Builder { + ctor public GetByUriRequest.Builder(); + method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUri(@NonNull java.lang.String...); + method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUri(@NonNull java.util.Collection<java.lang.String>); + method @NonNull public android.app.appsearch.GetByUriRequest build(); + method @NonNull public android.app.appsearch.GetByUriRequest.Builder setNamespace(@NonNull String); + } + + public final class PutDocumentsRequest { + method @NonNull public java.util.List<android.app.appsearch.GenericDocument> getDocuments(); + } + + public static final class PutDocumentsRequest.Builder { + ctor public PutDocumentsRequest.Builder(); + method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocument(@NonNull android.app.appsearch.GenericDocument...); + method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocument(@NonNull java.util.Collection<android.app.appsearch.GenericDocument>); + method @NonNull public android.app.appsearch.PutDocumentsRequest build(); + } + + public final class RemoveByUriRequest { + method @NonNull public String getNamespace(); + method @NonNull public java.util.Set<java.lang.String> getUris(); + } + + public static final class RemoveByUriRequest.Builder { + ctor public RemoveByUriRequest.Builder(); + method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUri(@NonNull java.lang.String...); + method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUri(@NonNull java.util.Collection<java.lang.String>); + method @NonNull public android.app.appsearch.RemoveByUriRequest build(); + method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder setNamespace(@NonNull String); + } + public final class SearchResult { method @NonNull public android.app.appsearch.GenericDocument getDocument(); method @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatches(); @@ -139,5 +197,18 @@ package android.app.appsearch { method @NonNull public android.app.appsearch.SearchSpec.Builder setTermMatch(int); } + public final class SetSchemaRequest { + method @NonNull public java.util.Set<android.app.appsearch.AppSearchSchema> getSchemas(); + method public boolean isForceOverride(); + } + + public static final class SetSchemaRequest.Builder { + ctor public SetSchemaRequest.Builder(); + method @NonNull public android.app.appsearch.SetSchemaRequest.Builder addSchema(@NonNull android.app.appsearch.AppSearchSchema...); + method @NonNull public android.app.appsearch.SetSchemaRequest.Builder addSchema(@NonNull java.util.Collection<android.app.appsearch.AppSearchSchema>); + method @NonNull public android.app.appsearch.SetSchemaRequest build(); + method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setForceOverride(boolean); + } + } diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java index 98daa66183a3..97cfe36fca80 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java @@ -33,7 +33,6 @@ import java.util.Map; * * @param <KeyType> The type of the keys for {@link #getSuccesses} and {@link #getFailures}. * @param <ValueType> The type of result objects associated with the keys. - * @hide */ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelable { @NonNull private final Map<KeyType, ValueType> mSuccesses; @@ -51,6 +50,7 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl mFailures = Collections.unmodifiableMap(in.readHashMap(/*loader=*/ null)); } + /** @hide */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeMap(mSuccesses); @@ -100,11 +100,14 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl return "{\n successes: " + mSuccesses + "\n failures: " + mFailures + "\n}"; } + /** @hide */ @Override public int describeContents() { return 0; } + /** @hide */ + @NonNull public static final Creator<AppSearchBatchResult> CREATOR = new Creator<AppSearchBatchResult>() { @NonNull diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java index 6e2ed70cca01..76225e40c56e 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java @@ -32,7 +32,6 @@ import java.util.Objects; * Information about the success or failure of an AppSearch call. * * @param <ValueType> The type of result object for successful calls. - * @hide */ public final class AppSearchResult<ValueType> implements Parcelable { /** @@ -107,6 +106,7 @@ public final class AppSearchResult<ValueType> implements Parcelable { mErrorMessage = in.readString(); } + /** @hide */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mResultCode); @@ -181,13 +181,15 @@ public final class AppSearchResult<ValueType> implements Parcelable { return "[FAILURE(" + mResultCode + ")]: " + mErrorMessage; } + /** @hide */ @Override public int describeContents() { return 0; } - public static final Creator<AppSearchResult> CREATOR = - new Creator<AppSearchResult>() { + /** @hide */ + @NonNull + public static final Creator<AppSearchResult> CREATOR = new Creator<AppSearchResult>() { @NonNull @Override public AppSearchResult createFromParcel(@NonNull Parcel in) { diff --git a/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java index 053d401d06dc..b1cf50484306 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java +++ b/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java @@ -29,8 +29,7 @@ import java.util.Set; /** * Encapsulates a request to retrieve documents by namespace and URI. * - * @see AppSearchSession#getByUri - * @hide + * @see AppSearchManager#getByUri */ public final class GetByUriRequest { private final String mNamespace; diff --git a/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java b/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java index 42f1ff2a716a..1e37277be55e 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java +++ b/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java @@ -29,10 +29,9 @@ import java.util.Collections; import java.util.List; /** - * Encapsulates a request to index a document into an {@link AppSearchSession} database. + * Encapsulates a request to index a document into an {@link AppSearchManager} database. * - * @see AppSearchSession#putDocuments - * @hide + * @see AppSearchManager#putDocuments */ public final class PutDocumentsRequest { private final List<GenericDocument> mDocuments; diff --git a/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java index 3d83c390fbad..1c17547574a6 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java +++ b/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java @@ -29,8 +29,7 @@ import java.util.Set; /** * Encapsulates a request to remove documents by namespace and URI. * - * @see AppSearchSession#removeByUri - * @hide + * @see AppSearchManager#removeByUri */ public final class RemoveByUriRequest { private final String mNamespace; diff --git a/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java index 3e472fd01939..081fb7202c2d 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java +++ b/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java @@ -30,10 +30,9 @@ import java.util.List; import java.util.Set; /** - * Encapsulates a request to update the schema of an {@link AppSearchSession} database. + * Encapsulates a request to update the schema of an {@link AppSearchManager} database. * - * @see AppSearchSession#setSchema - * @hide + * @see AppSearchManager#setSchema */ public final class SetSchemaRequest { private final Set<AppSearchSchema> mSchemas; @@ -82,9 +81,9 @@ public final class SetSchemaRequest { * follow the new schema. * * <p>By default, this is {@code false} and schema incompatibility causes the {@link - * AppSearchSession#setSchema} call to fail. + * AppSearchManager#setSchema} call to fail. * - * @see AppSearchSession#setSchema + * @see AppSearchManager#setSchema */ @NonNull public Builder setForceOverride(boolean forceOverride) { diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp index bf4323ddfb0b..e4299f52ff7d 100644 --- a/apex/statsd/framework/Android.bp +++ b/apex/statsd/framework/Android.bp @@ -45,6 +45,7 @@ filegroup { visibility: [ "//frameworks/base", // For the "global" stubs. "//frameworks/base/apex/statsd:__subpackages__", + "//packages/modules/StatsD/apex:__subpackages__", ], } java_sdk_library { @@ -72,7 +73,10 @@ java_sdk_library { hostdex: true, // for hiddenapi check - impl_library_visibility: ["//frameworks/base/apex/statsd/framework/test:__subpackages__"], + impl_library_visibility: [ + "//frameworks/base/apex/statsd/framework/test:__subpackages__", + "//packages/modules/StatsD/apex/framework/test:__subpackages__", + ], apex_available: [ "com.android.os.statsd", diff --git a/core/api/current.txt b/core/api/current.txt index f748c24e9181..15e985cddef7 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -2883,6 +2883,7 @@ package android.accessibilityservice { method protected void onServiceConnected(); method public void onSystemActionsChanged(); method public final boolean performGlobalAction(int); + method public void setAccessibilityFocusAppearance(int, @ColorInt int); method public void setGestureDetectionPassthroughRegion(int, @NonNull android.graphics.Region); method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo); method public void setTouchExplorationPassthroughRegion(int, @NonNull android.graphics.Region); @@ -7150,6 +7151,7 @@ package android.app.admin { field @RequiresPermission(android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY) public static final String EXTRA_PASSWORD_COMPLEXITY = "android.app.extra.PASSWORD_COMPLEXITY"; field public static final String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE = "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE"; field public static final String EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE = "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE"; + field public static final String EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES = "android.app.extra.PROVISIONING_ALLOWED_PROVISIONING_MODES"; field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME"; field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE = "android.app.extra.PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE"; field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM"; @@ -7254,6 +7256,7 @@ package android.app.admin { field public static final int PRIVATE_DNS_SET_NO_ERROR = 0; // 0x0 field public static final int PROVISIONING_MODE_FULLY_MANAGED_DEVICE = 1; // 0x1 field public static final int PROVISIONING_MODE_MANAGED_PROFILE = 2; // 0x2 + field public static final int PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE = 3; // 0x3 field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2 field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1 field public static final int SKIP_SETUP_WIZARD = 1; // 0x1 @@ -42460,6 +42463,7 @@ package android.service.notification { method public CharSequence getImportanceExplanation(); method public String getKey(); method public long getLastAudiblyAlertedMillis(); + method public int getLockscreenVisibilityOverride(); method public String getOverrideGroupKey(); method public int getRank(); method @NonNull public java.util.List<android.app.Notification.Action> getSmartActions(); @@ -42473,6 +42477,7 @@ package android.service.notification { field public static final int USER_SENTIMENT_NEGATIVE = -1; // 0xffffffff field public static final int USER_SENTIMENT_NEUTRAL = 0; // 0x0 field public static final int USER_SENTIMENT_POSITIVE = 1; // 0x1 + field public static final int VISIBILITY_NO_OVERRIDE = -1000; // 0xfffffc18 } public static class NotificationListenerService.RankingMap implements android.os.Parcelable { @@ -44860,14 +44865,19 @@ package android.telecom { field public static final String EXTRA_DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME = "android.telecom.extra.DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME"; field public static final String EXTRA_DISCONNECT_CAUSE = "android.telecom.extra.DISCONNECT_CAUSE"; field public static final String EXTRA_HANDLE = "android.telecom.extra.HANDLE"; + field public static final String EXTRA_HAS_PICTURE = "android.telecom.extra.HAS_PICTURE"; field public static final String EXTRA_INCOMING_CALL_ADDRESS = "android.telecom.extra.INCOMING_CALL_ADDRESS"; field public static final String EXTRA_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS"; + field public static final String EXTRA_INCOMING_PICTURE = "android.telecom.extra.INCOMING_PICTURE"; field public static final String EXTRA_INCOMING_VIDEO_STATE = "android.telecom.extra.INCOMING_VIDEO_STATE"; field public static final String EXTRA_IS_DEFAULT_CALL_SCREENING_APP = "android.telecom.extra.IS_DEFAULT_CALL_SCREENING_APP"; + field public static final String EXTRA_LOCATION = "android.telecom.extra.LOCATION"; field public static final String EXTRA_NOTIFICATION_COUNT = "android.telecom.extra.NOTIFICATION_COUNT"; field public static final String EXTRA_NOTIFICATION_PHONE_NUMBER = "android.telecom.extra.NOTIFICATION_PHONE_NUMBER"; field public static final String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS"; + field public static final String EXTRA_OUTGOING_PICTURE = "android.telecom.extra.OUTGOING_PICTURE"; field public static final String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE"; + field public static final String EXTRA_PRIORITY = "android.telecom.extra.PRIORITY"; field public static final String EXTRA_START_CALL_WITH_RTT = "android.telecom.extra.START_CALL_WITH_RTT"; field public static final String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE"; field public static final String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE"; @@ -44883,6 +44893,8 @@ package android.telecom { field public static final int PRESENTATION_PAYPHONE = 4; // 0x4 field public static final int PRESENTATION_RESTRICTED = 2; // 0x2 field public static final int PRESENTATION_UNKNOWN = 3; // 0x3 + field public static final int PRIORITY_NORMAL = 0; // 0x0 + field public static final int PRIORITY_URGENT = 1; // 0x1 } public class VideoProfile implements android.os.Parcelable { @@ -54611,6 +54623,8 @@ package android.view.accessibility { method public void addAccessibilityStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener, @Nullable android.os.Handler); method public boolean addTouchExplorationStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener); method public void addTouchExplorationStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener, @Nullable android.os.Handler); + method @ColorInt public int getAccessibilityFocusColor(); + method public int getAccessibilityFocusStrokeWidth(); method @Deprecated public java.util.List<android.content.pm.ServiceInfo> getAccessibilityServiceList(); method public java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int); method public java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getInstalledAccessibilityServiceList(); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index a49f71f6530c..3a769d5d5a71 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -878,11 +878,13 @@ package android.app.admin { field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI"; field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL"; field public static final String EXTRA_PROVISIONING_ORGANIZATION_NAME = "android.app.extra.PROVISIONING_ORGANIZATION_NAME"; + field public static final String EXTRA_PROVISIONING_SUPPORTED_MODES = "android.app.extra.PROVISIONING_SUPPORTED_MODES"; field public static final String EXTRA_PROVISIONING_SUPPORT_URL = "android.app.extra.PROVISIONING_SUPPORT_URL"; field public static final String EXTRA_PROVISIONING_TRIGGER = "android.app.extra.PROVISIONING_TRIGGER"; field public static final String EXTRA_RESTRICTION = "android.app.extra.RESTRICTION"; field public static final int PROVISIONING_TRIGGER_CLOUD_ENROLLMENT = 1; // 0x1 - field public static final int PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER = 3; // 0x3 + field public static final int PROVISIONING_TRIGGER_MANAGED_ACCOUNT = 4; // 0x4 + field @Deprecated public static final int PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER = 3; // 0x3 field public static final int PROVISIONING_TRIGGER_QR_CODE = 2; // 0x2 field public static final int PROVISIONING_TRIGGER_UNSPECIFIED = 0; // 0x0 field public static final int STATE_USER_PROFILE_COMPLETE = 4; // 0x4 @@ -891,6 +893,9 @@ package android.app.admin { field public static final int STATE_USER_SETUP_FINALIZED = 3; // 0x3 field public static final int STATE_USER_SETUP_INCOMPLETE = 1; // 0x1 field public static final int STATE_USER_UNMANAGED = 0; // 0x0 + field public static final int SUPPORTED_MODES_ORGANIZATION_AND_PERSONALLY_OWNED = 3; // 0x3 + field public static final int SUPPORTED_MODES_ORGANIZATION_OWNED = 1; // 0x1 + field public static final int SUPPORTED_MODES_PERSONALLY_OWNED = 2; // 0x2 } public final class SystemUpdatePolicy implements android.os.Parcelable { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 51edd03515e1..80a160a67fdd 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -987,9 +987,9 @@ package android.media { } public class AudioSystem { - method public static float getMasterBalance(); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS) public static float getMasterBalance(); method public static final int getNumStreamTypes(); - method public static int setMasterBalance(float); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS) public static int setMasterBalance(float); field public static final int DEVICE_ROLE_DISABLED = 2; // 0x2 field public static final int DEVICE_ROLE_NONE = 0; // 0x0 field public static final int DEVICE_ROLE_PREFERRED = 1; // 0x1 diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 0ad9e446dfc7..8e50184c96e0 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -18,6 +18,7 @@ package android.accessibilityservice; import android.accessibilityservice.GestureDescription.MotionEventGenerator; import android.annotation.CallbackExecutor; +import android.annotation.ColorInt; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -2118,6 +2119,27 @@ public abstract class AccessibilityService extends Service { } /** + * Sets the strokeWidth and color of the accessibility focus rectangle. + * + * @param strokeWidth The stroke width of the rectangle in pixels. + * Setting this value to zero results in no focus rectangle being drawn. + * @param color The color of the rectangle. + */ + public void setAccessibilityFocusAppearance(int strokeWidth, @ColorInt int color) { + IAccessibilityServiceConnection connection = + AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + if (connection != null) { + try { + connection.setFocusAppearance(strokeWidth, color); + } catch (RemoteException re) { + Log.w(LOG_TAG, "Error while setting the strokeWidth and color of the " + + "accessibility focus rectangle", re); + re.rethrowFromSystemServer(); + } + } + } + + /** * Implement to return the implementation of the internal accessibility * service interface. */ diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index 0b3b9b2ecae1..ab21dc9f14ad 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -115,4 +115,6 @@ interface IAccessibilityServiceConnection { void setGestureDetectionPassthroughRegion(int displayId, in Region region); void setTouchExplorationPassthroughRegion(int displayId, in Region region); + + void setFocusAppearance(int strokeWidth, int color); } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index e3048dff1bda..38a22d8ade94 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -77,6 +77,8 @@ import android.util.ArrayMap; import android.util.DisplayMetrics; import android.util.Singleton; import android.util.Size; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.view.Surface; import android.view.WindowInsetsController.Appearance; @@ -1502,57 +1504,54 @@ public class ActivityManager { } /** @hide */ - public void saveToXml(XmlSerializer out) throws IOException { + public void saveToXml(TypedXmlSerializer out) throws IOException { if (mLabel != null) { out.attribute(null, ATTR_TASKDESCRIPTIONLABEL, mLabel); } if (mColorPrimary != 0) { - out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR_PRIMARY, - Integer.toHexString(mColorPrimary)); + out.attributeIntHex(null, ATTR_TASKDESCRIPTIONCOLOR_PRIMARY, mColorPrimary); } if (mColorBackground != 0) { - out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND, - Integer.toHexString(mColorBackground)); + out.attributeIntHex(null, ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND, mColorBackground); } if (mIconFilename != null) { out.attribute(null, ATTR_TASKDESCRIPTIONICON_FILENAME, mIconFilename); } if (mIcon != null && mIcon.getType() == Icon.TYPE_RESOURCE) { - out.attribute(null, ATTR_TASKDESCRIPTIONICON_RESOURCE, - Integer.toString(mIcon.getResId())); + out.attributeInt(null, ATTR_TASKDESCRIPTIONICON_RESOURCE, mIcon.getResId()); out.attribute(null, ATTR_TASKDESCRIPTIONICON_RESOURCE_PACKAGE, mIcon.getResPackage()); } } /** @hide */ - public void restoreFromXml(XmlPullParser in) { + public void restoreFromXml(TypedXmlPullParser in) { final String label = in.getAttributeValue(null, ATTR_TASKDESCRIPTIONLABEL); if (label != null) { setLabel(label); } - final String colorPrimary = in.getAttributeValue(null, - ATTR_TASKDESCRIPTIONCOLOR_PRIMARY); - if (colorPrimary != null) { - setPrimaryColor((int) Long.parseLong(colorPrimary, 16)); + final int colorPrimary = in.getAttributeIntHex(null, + ATTR_TASKDESCRIPTIONCOLOR_PRIMARY, 0); + if (colorPrimary != 0) { + setPrimaryColor(colorPrimary); } - final String colorBackground = in.getAttributeValue(null, - ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND); - if (colorBackground != null) { - setBackgroundColor((int) Long.parseLong(colorBackground, 16)); + final int colorBackground = in.getAttributeIntHex(null, + ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND, 0); + if (colorBackground != 0) { + setBackgroundColor(colorBackground); } final String iconFilename = in.getAttributeValue(null, ATTR_TASKDESCRIPTIONICON_FILENAME); if (iconFilename != null) { setIconFilename(iconFilename); } - final String iconResourceId = in.getAttributeValue(null, - ATTR_TASKDESCRIPTIONICON_RESOURCE); + final int iconResourceId = in.getAttributeInt(null, + ATTR_TASKDESCRIPTIONICON_RESOURCE, Resources.ID_NULL); final String iconResourcePackage = in.getAttributeValue(null, ATTR_TASKDESCRIPTIONICON_RESOURCE_PACKAGE); - if (iconResourceId != null && iconResourcePackage != null) { + if (iconResourceId != Resources.ID_NULL && iconResourcePackage != null) { setIcon(Icon.createWithResource(iconResourcePackage, - Integer.parseInt(iconResourceId, 10))); + iconResourceId)); } } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 433182b38efd..ed6dea815e4c 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -85,6 +85,7 @@ import android.database.sqlite.SQLiteDebug.DbStats; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.HardwareRenderer; +import android.graphics.Rect; import android.graphics.Typeface; import android.hardware.display.DisplayManagerGlobal; import android.inputmethodservice.InputMethodService; @@ -5592,10 +5593,13 @@ public final class ActivityThread extends ClientTransactionHandler { // If the new config is the same as the config this Activity is already running with and // the override config also didn't change, then don't bother calling // onConfigurationChanged. + // TODO(b/173090263): Use diff instead after the improvement of AssetManager and + // ResourcesImpl constructions. final int diff = activity.mCurrentConfig.diffPublicOnly(newConfig); - if (diff == 0 && !movedToDifferentDisplay - && mResourcesManager.isSameResourcesOverrideConfig(activityToken, - amOverrideConfig)) { + + if (diff == 0 && !shouldUpdateWindowMetricsBounds(activity.mCurrentConfig, newConfig) + && !movedToDifferentDisplay && mResourcesManager.isSameResourcesOverrideConfig( + activityToken, amOverrideConfig)) { // Nothing significant, don't proceed with updating and reporting. return null; } else if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0) { @@ -5647,6 +5651,26 @@ public final class ActivityThread extends ClientTransactionHandler { return configToReport; } + // TODO(b/173090263): Remove this method after the improvement of AssetManager and ResourcesImpl + // constructions. + /** + * Returns {@code true} if the metrics reported by {@link android.view.WindowMetrics} APIs + * should be updated. + * + * @see WindowManager#getCurrentWindowMetrics() + * @see WindowManager#getMaximumWindowMetrics() + */ + private static boolean shouldUpdateWindowMetricsBounds(@NonNull Configuration currentConfig, + @NonNull Configuration newConfig) { + final Rect currentBounds = currentConfig.windowConfiguration.getBounds(); + final Rect newBounds = newConfig.windowConfiguration.getBounds(); + + final Rect currentMaxBounds = currentConfig.windowConfiguration.getMaxBounds(); + final Rect newMaxBounds = newConfig.windowConfiguration.getMaxBounds(); + + return !currentBounds.equals(newBounds) || !currentMaxBounds.equals(newMaxBounds); + } + public final void applyConfigurationToResources(Configuration config) { synchronized (mResourcesManager) { mResourcesManager.applyConfigurationToResourcesLocked(config, null); @@ -5881,7 +5905,7 @@ public final class ActivityThread extends ClientTransactionHandler { /** * Sets the supplied {@code overrideConfig} as pending for the {@code activityToken}. Calling * this method prevents any calls to - * {@link #handleActivityConfigurationChanged(IBinder, Configuration, int, boolean)} from + * {@link #handleActivityConfigurationChanged(ActivityClientRecord, Configuration, int)} from * processing any configurations older than {@code overrideConfig}. */ @Override @@ -5903,8 +5927,8 @@ public final class ActivityThread extends ClientTransactionHandler { /** * Handle new activity configuration and/or move to a different display. This method is a noop - * if {@link #updatePendingActivityConfiguration(IBinder, Configuration)} has been called with - * a newer config than {@code overrideConfig}. + * if {@link #updatePendingActivityConfiguration(ActivityClientRecord, Configuration)} has been + * called with a newer config than {@code overrideConfig}. * * @param r Target activity record. * @param overrideConfig Activity override config. @@ -5969,7 +5993,7 @@ public final class ActivityThread extends ClientTransactionHandler { /** * Checks if the display id of activity is different from the given one. Note that - * {@link #INVALID_DISPLAY} means no difference. + * {@link Display#INVALID_DISPLAY} means no difference. */ private static boolean isDifferentDisplay(@NonNull Activity activity, int displayId) { return displayId != INVALID_DISPLAY && displayId != activity.getDisplayId(); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 34437afb614a..3642d318e820 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -30,6 +30,7 @@ import android.annotation.Nullable; import android.annotation.StringRes; import android.annotation.UserIdInt; import android.annotation.XmlRes; +import android.app.role.RoleManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.ContentResolver; @@ -2306,20 +2307,14 @@ public class ApplicationPackageManager extends PackageManager { @Override public String getDefaultBrowserPackageNameAsUser(int userId) { - try { - return mPermissionManager.getDefaultBrowser(userId); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + RoleManager roleManager = mContext.getSystemService(RoleManager.class); + return roleManager.getBrowserRoleHolder(userId); } @Override public boolean setDefaultBrowserPackageNameAsUser(String packageName, int userId) { - try { - return mPermissionManager.setDefaultBrowser(packageName, userId); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + RoleManager roleManager = mContext.getSystemService(RoleManager.class); + return roleManager.setBrowserRoleHolder(packageName, userId); } @Override diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 6535387acdf3..99785e1e73f4 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -1284,7 +1284,7 @@ public final class LoadedApk { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); throw new RuntimeException( "Unable to instantiate application " + appClass - + ": " + e.toString(), e); + + " package " + mPackageName + ": " + e.toString(), e); } } mActivityThread.mAllApplications.add(app); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index a1135809fd4c..82f61a4c615a 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -83,6 +83,7 @@ import android.util.Log; import android.util.Pair; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; +import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; @@ -6120,8 +6121,11 @@ public class Notification implements Parcelable int defaultColor = mInNightMode ? Color.BLACK : Color.WHITE; Resources.Theme theme = mContext.getTheme(); if (theme == null) { + // Running unit tests with mocked context return defaultColor; } + theme = new ContextThemeWrapper(mContext, R.style.Theme_DeviceDefault_DayNight) + .getTheme(); TypedArray ta = theme.obtainStyledAttributes(new int[]{R.attr.colorBackground}); if (ta == null) { return defaultColor; diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index 080aac9a9e6a..b1a8f9b0ba33 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -32,9 +32,12 @@ import android.os.Parcelable; import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.text.TextUtils; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; import com.android.internal.util.Preconditions; +import com.android.internal.util.XmlUtils; import org.json.JSONException; import org.json.JSONObject; @@ -869,7 +872,7 @@ public final class NotificationChannel implements Parcelable { * @hide */ public void populateFromXmlForRestore(XmlPullParser parser, Context context) { - populateFromXml(parser, true, context); + populateFromXml(XmlUtils.makeTyped(parser), true, context); } /** @@ -877,13 +880,13 @@ public final class NotificationChannel implements Parcelable { */ @SystemApi public void populateFromXml(XmlPullParser parser) { - populateFromXml(parser, false, null); + populateFromXml(XmlUtils.makeTyped(parser), false, null); } /** * If {@param forRestore} is true, {@param Context} MUST be non-null. */ - private void populateFromXml(XmlPullParser parser, boolean forRestore, + private void populateFromXml(TypedXmlPullParser parser, boolean forRestore, @Nullable Context context) { Preconditions.checkArgument(!forRestore || context != null, "forRestore is true but got null context"); @@ -941,14 +944,14 @@ public final class NotificationChannel implements Parcelable { */ @SystemApi public void writeXml(XmlSerializer out) throws IOException { - writeXml(out, false, null); + writeXml(XmlUtils.makeTyped(out), false, null); } /** * @hide */ public void writeXmlForBackup(XmlSerializer out, Context context) throws IOException { - writeXml(out, true, context); + writeXml(XmlUtils.makeTyped(out), true, context); } private Uri getSoundForBackup(Context context) { @@ -967,7 +970,7 @@ public final class NotificationChannel implements Parcelable { /** * If {@param forBackup} is true, {@param Context} MUST be non-null. */ - private void writeXml(XmlSerializer out, boolean forBackup, @Nullable Context context) + private void writeXml(TypedXmlSerializer out, boolean forBackup, @Nullable Context context) throws IOException { Preconditions.checkArgument(!forBackup || context != null, "forBackup is true but got null context"); @@ -980,62 +983,58 @@ public final class NotificationChannel implements Parcelable { out.attribute(null, ATT_DESC, getDescription()); } if (getImportance() != DEFAULT_IMPORTANCE) { - out.attribute( - null, ATT_IMPORTANCE, Integer.toString(getImportance())); + out.attributeInt(null, ATT_IMPORTANCE, getImportance()); } if (canBypassDnd()) { - out.attribute( - null, ATT_PRIORITY, Integer.toString(Notification.PRIORITY_MAX)); + out.attributeInt(null, ATT_PRIORITY, Notification.PRIORITY_MAX); } if (getLockscreenVisibility() != DEFAULT_VISIBILITY) { - out.attribute(null, ATT_VISIBILITY, - Integer.toString(getLockscreenVisibility())); + out.attributeInt(null, ATT_VISIBILITY, getLockscreenVisibility()); } Uri sound = forBackup ? getSoundForBackup(context) : getSound(); if (sound != null) { out.attribute(null, ATT_SOUND, sound.toString()); } if (getAudioAttributes() != null) { - out.attribute(null, ATT_USAGE, Integer.toString(getAudioAttributes().getUsage())); - out.attribute(null, ATT_CONTENT_TYPE, - Integer.toString(getAudioAttributes().getContentType())); - out.attribute(null, ATT_FLAGS, Integer.toString(getAudioAttributes().getFlags())); + out.attributeInt(null, ATT_USAGE, getAudioAttributes().getUsage()); + out.attributeInt(null, ATT_CONTENT_TYPE, getAudioAttributes().getContentType()); + out.attributeInt(null, ATT_FLAGS, getAudioAttributes().getFlags()); } if (shouldShowLights()) { - out.attribute(null, ATT_LIGHTS, Boolean.toString(shouldShowLights())); + out.attributeBoolean(null, ATT_LIGHTS, shouldShowLights()); } if (getLightColor() != DEFAULT_LIGHT_COLOR) { - out.attribute(null, ATT_LIGHT_COLOR, Integer.toString(getLightColor())); + out.attributeInt(null, ATT_LIGHT_COLOR, getLightColor()); } if (shouldVibrate()) { - out.attribute(null, ATT_VIBRATION_ENABLED, Boolean.toString(shouldVibrate())); + out.attributeBoolean(null, ATT_VIBRATION_ENABLED, shouldVibrate()); } if (getVibrationPattern() != null) { out.attribute(null, ATT_VIBRATION, longArrayToString(getVibrationPattern())); } if (getUserLockedFields() != 0) { - out.attribute(null, ATT_USER_LOCKED, Integer.toString(getUserLockedFields())); + out.attributeInt(null, ATT_USER_LOCKED, getUserLockedFields()); } if (isFgServiceShown()) { - out.attribute(null, ATT_FG_SERVICE_SHOWN, Boolean.toString(isFgServiceShown())); + out.attributeBoolean(null, ATT_FG_SERVICE_SHOWN, isFgServiceShown()); } if (canShowBadge()) { - out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(canShowBadge())); + out.attributeBoolean(null, ATT_SHOW_BADGE, canShowBadge()); } if (isDeleted()) { - out.attribute(null, ATT_DELETED, Boolean.toString(isDeleted())); + out.attributeBoolean(null, ATT_DELETED, isDeleted()); } if (getGroup() != null) { out.attribute(null, ATT_GROUP, getGroup()); } if (isBlockable()) { - out.attribute(null, ATT_BLOCKABLE_SYSTEM, Boolean.toString(isBlockable())); + out.attributeBoolean(null, ATT_BLOCKABLE_SYSTEM, isBlockable()); } if (getAllowBubbles() != DEFAULT_ALLOW_BUBBLE) { - out.attribute(null, ATT_ALLOW_BUBBLE, Integer.toString(getAllowBubbles())); + out.attributeInt(null, ATT_ALLOW_BUBBLE, getAllowBubbles()); } if (getOriginalImportance() != DEFAULT_IMPORTANCE) { - out.attribute(null, ATT_ORIG_IMP, Integer.toString(getOriginalImportance())); + out.attributeInt(null, ATT_ORIG_IMP, getOriginalImportance()); } if (getParentChannelId() != null) { out.attribute(null, ATT_PARENT_CHANNEL, getParentChannelId()); @@ -1044,10 +1043,10 @@ public final class NotificationChannel implements Parcelable { out.attribute(null, ATT_CONVERSATION_ID, getConversationId()); } if (isDemoted()) { - out.attribute(null, ATT_DEMOTE, Boolean.toString(isDemoted())); + out.attributeBoolean(null, ATT_DEMOTE, isDemoted()); } if (isImportantConversation()) { - out.attribute(null, ATT_IMP_CONVERSATION, Boolean.toString(isImportantConversation())); + out.attributeBoolean(null, ATT_IMP_CONVERSATION, isImportantConversation()); } // mImportanceLockedDefaultApp and mImportanceLockedByOEM have a different source of @@ -1099,7 +1098,7 @@ public final class NotificationChannel implements Parcelable { return record; } - private static AudioAttributes safeAudioAttributes(XmlPullParser parser) { + private static AudioAttributes safeAudioAttributes(TypedXmlPullParser parser) { int usage = safeInt(parser, ATT_USAGE, AudioAttributes.USAGE_NOTIFICATION); int contentType = safeInt(parser, ATT_CONTENT_TYPE, AudioAttributes.CONTENT_TYPE_SONIFICATION); @@ -1111,32 +1110,20 @@ public final class NotificationChannel implements Parcelable { .build(); } - private static Uri safeUri(XmlPullParser parser, String att) { + private static Uri safeUri(TypedXmlPullParser parser, String att) { final String val = parser.getAttributeValue(null, att); return val == null ? null : Uri.parse(val); } - private static int safeInt(XmlPullParser parser, String att, int defValue) { - final String val = parser.getAttributeValue(null, att); - return tryParseInt(val, defValue); - } - - private static int tryParseInt(String value, int defValue) { - if (TextUtils.isEmpty(value)) return defValue; - try { - return Integer.parseInt(value); - } catch (NumberFormatException e) { - return defValue; - } + private static int safeInt(TypedXmlPullParser parser, String att, int defValue) { + return parser.getAttributeInt(null, att, defValue); } - private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) { - final String value = parser.getAttributeValue(null, att); - if (TextUtils.isEmpty(value)) return defValue; - return Boolean.parseBoolean(value); + private static boolean safeBool(TypedXmlPullParser parser, String att, boolean defValue) { + return parser.getAttributeBoolean(null, att, defValue); } - private static long[] safeLongArray(XmlPullParser parser, String att, long[] defValue) { + private static long[] safeLongArray(TypedXmlPullParser parser, String att, long[] defValue) { final String attributeValue = parser.getAttributeValue(null, att); if (TextUtils.isEmpty(attributeValue)) return defValue; String[] values = attributeValue.split(DELIMITER); diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java index ec7fa332b23f..cd6df0b231d9 100644 --- a/core/java/android/app/NotificationChannelGroup.java +++ b/core/java/android/app/NotificationChannelGroup.java @@ -23,12 +23,12 @@ import android.content.Intent; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; import org.json.JSONException; import org.json.JSONObject; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; @@ -228,22 +228,16 @@ public final class NotificationChannelGroup implements Parcelable { /** * @hide */ - public void populateFromXml(XmlPullParser parser) { + public void populateFromXml(TypedXmlPullParser parser) { // Name, id, and importance are set in the constructor. setDescription(parser.getAttributeValue(null, ATT_DESC)); - setBlocked(safeBool(parser, ATT_BLOCKED, false)); - } - - private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) { - final String value = parser.getAttributeValue(null, att); - if (TextUtils.isEmpty(value)) return defValue; - return Boolean.parseBoolean(value); + setBlocked(parser.getAttributeBoolean(null, ATT_BLOCKED, false)); } /** * @hide */ - public void writeXml(XmlSerializer out) throws IOException { + public void writeXml(TypedXmlSerializer out) throws IOException { out.startTag(null, TAG_GROUP); out.attribute(null, ATT_ID, getId()); @@ -253,8 +247,8 @@ public final class NotificationChannelGroup implements Parcelable { if (getDescription() != null) { out.attribute(null, ATT_DESC, getDescription().toString()); } - out.attribute(null, ATT_BLOCKED, Boolean.toString(isBlocked())); - out.attribute(null, ATT_USER_LOCKED, Integer.toString(mUserLockedFields)); + out.attributeBoolean(null, ATT_BLOCKED, isBlocked()); + out.attributeInt(null, ATT_USER_LOCKED, mUserLockedFields); out.endTag(null, TAG_GROUP); } diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 36d5b5eb9fdf..772833cc6d2d 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -1178,6 +1178,8 @@ public class ResourcesManager { continue; } + // TODO(b/173090263): Improve the performance of AssetManager & ResourcesImpl + // constructions. final ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(newKey); if (resourcesImpl != null && resourcesImpl != resources.getImpl()) { diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java index 1ee8e4fce58b..41256d09d0ed 100644 --- a/core/java/android/app/admin/DeviceAdminInfo.java +++ b/core/java/android/app/admin/DeviceAdminInfo.java @@ -37,11 +37,12 @@ import android.util.AttributeSet; import android.util.Log; import android.util.Printer; import android.util.SparseArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; @@ -472,16 +473,15 @@ public final class DeviceAdminInfo implements Parcelable { } /** @hide */ - public void writePoliciesToXml(XmlSerializer out) + public void writePoliciesToXml(TypedXmlSerializer out) throws IllegalArgumentException, IllegalStateException, IOException { - out.attribute(null, "flags", Integer.toString(mUsesPolicies)); + out.attributeInt(null, "flags", mUsesPolicies); } /** @hide */ - public void readPoliciesFromXml(XmlPullParser parser) + public void readPoliciesFromXml(TypedXmlPullParser parser) throws XmlPullParserException, IOException { - mUsesPolicies = Integer.parseInt( - parser.getAttributeValue(null, "flags")); + mUsesPolicies = parser.getAttributeInt(null, "flags"); } public void dump(Printer pw, String prefix) { diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 5eb1922a163c..da1219bdd19c 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1206,7 +1206,7 @@ public class DevicePolicyManager { * <ul> * <li>By the admin app when performing the admin-integrated * provisioning flow as a result of the {@link #ACTION_GET_PROVISIONING_MODE} activity</li> - * <li>With intent action {@link #ACTION_PROVISION_MANAGED_DEVICE}</li> + * <li>For managed account enrollment</li> * </ul> * * <p>If the education screens are skipped, it is the admin application's responsibility @@ -1229,9 +1229,37 @@ public class DevicePolicyManager { "android.app.extra.PROVISIONING_USE_MOBILE_DATA"; /** + * Possible values for {@link #EXTRA_PROVISIONING_TRIGGER}. + * + * @hide + */ + @IntDef(prefix = { "PROVISIONING_TRIGGER_" }, value = { + PROVISIONING_TRIGGER_UNSPECIFIED, + PROVISIONING_TRIGGER_CLOUD_ENROLLMENT, + PROVISIONING_TRIGGER_QR_CODE, + PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER, + PROVISIONING_TRIGGER_MANAGED_ACCOUNT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ProvisioningTrigger {} + + /** + * Possible values for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES}. + * + * @hide + */ + @IntDef(prefix = { "SUPPORTED_MODES_" }, value = { + SUPPORTED_MODES_ORGANIZATION_OWNED, + SUPPORTED_MODES_PERSONALLY_OWNED, + SUPPORTED_MODES_ORGANIZATION_AND_PERSONALLY_OWNED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ProvisioningConfiguration {} + + /** * A String extra holding the provisioning trigger. It could be one of * {@link #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT}, {@link #PROVISIONING_TRIGGER_QR_CODE}, - * {@link #PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER} or {@link + * {@link #PROVISIONING_TRIGGER_MANAGED_ACCOUNT} or {@link * #PROVISIONING_TRIGGER_UNSPECIFIED}. * * <p>Use in an intent with action {@link @@ -1247,7 +1275,7 @@ public class DevicePolicyManager { * trigger has not been specified. * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT * @see #PROVISIONING_TRIGGER_QR_CODE - * @see #PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER + * @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT * @hide */ @SystemApi @@ -1257,7 +1285,7 @@ public class DevicePolicyManager { * A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning * trigger is cloud enrollment. * @see #PROVISIONING_TRIGGER_QR_CODE - * @see #PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER + * @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT * @see #PROVISIONING_TRIGGER_UNSPECIFIED * @hide */ @@ -1268,7 +1296,7 @@ public class DevicePolicyManager { * A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning * trigger is the QR code scanner. * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT - * @see #PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER + * @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT * @see #PROVISIONING_TRIGGER_UNSPECIFIED * @hide */ @@ -1278,15 +1306,78 @@ public class DevicePolicyManager { /** * A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning * trigger is persistent device owner enrollment. + * @deprecated Use the broader {@link #PROVISIONING_TRIGGER_MANAGED_ACCOUNT} instead * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT * @see #PROVISIONING_TRIGGER_QR_CODE * @see #PROVISIONING_TRIGGER_UNSPECIFIED * @hide */ @SystemApi + @Deprecated public static final int PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER = 3; /** + * A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning + * trigger is managed account enrollment. + * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT + * @see #PROVISIONING_TRIGGER_QR_CODE + * @see #PROVISIONING_TRIGGER_UNSPECIFIED + * @hide + */ + @SystemApi + public static final int PROVISIONING_TRIGGER_MANAGED_ACCOUNT = 4; + + /** + * A value for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES} indicating that provisioning is + * organization-owned. + * + * <p>Using this value will cause the admin app's {@link #ACTION_GET_PROVISIONING_MODE} + * activity to have the {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra + * contain {@link #PROVISIONING_MODE_MANAGED_PROFILE} and {@link + * #PROVISIONING_MODE_FULLY_MANAGED_DEVICE}. + * + * <p>Also, if this value is set, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity + * will not receive the {@link #EXTRA_PROVISIONING_IMEI} and {@link + * #EXTRA_PROVISIONING_SERIAL_NUMBER} extras. + * + * @hide + */ + @SystemApi + public static final int SUPPORTED_MODES_ORGANIZATION_OWNED = 1; + + /** + * A value for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES} indicating that provisioning is + * personally-owned. + * + * <p>Using this value will cause the admin app's {@link #ACTION_GET_PROVISIONING_MODE} + * activity to have the {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra + * contain only {@link #PROVISIONING_MODE_MANAGED_PROFILE}. + * + * @hide + */ + @SystemApi + public static final int SUPPORTED_MODES_PERSONALLY_OWNED = 2; + + /** + * A value for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES} indicating that provisioning could + * be organization-owned or personally-owned. + * + * <p>Using this value will cause the admin app's {@link #ACTION_GET_PROVISIONING_MODE} + * activity to have the {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra + * contain {@link + * #PROVISIONING_MODE_MANAGED_PROFILE}, {@link #PROVISIONING_MODE_FULLY_MANAGED_DEVICE} and + * {@link #PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE}. + * + * <p>Also, if this value is set, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity + * will not receive the {@link #EXTRA_PROVISIONING_IMEI} and {@link + * #EXTRA_PROVISIONING_SERIAL_NUMBER} extras. + * + * @hide + */ + @SystemApi + public static final int SUPPORTED_MODES_ORGANIZATION_AND_PERSONALLY_OWNED = 3; + + /** * This MIME type is used for starting the device owner provisioning. * * <p>During device owner provisioning a device admin app is set as the owner of the device. @@ -2379,16 +2470,57 @@ public class DevicePolicyManager { /** * An intent extra holding the provisioning mode returned by the administrator. - * The value for this extra should be one of the following: - * <ul> - * <li>{@link #PROVISIONING_MODE_FULLY_MANAGED_DEVICE}</li> - * <li>{@link #PROVISIONING_MODE_MANAGED_PROFILE}</li> - * </ul> + * The value of this extra must be one of the values provided in {@link + * #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES}, which is provided as an intent extra to + * the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity. + * + * @see #PROVISIONING_MODE_FULLY_MANAGED_DEVICE + * @see #PROVISIONING_MODE_MANAGED_PROFILE */ public static final String EXTRA_PROVISIONING_MODE = "android.app.extra.PROVISIONING_MODE"; /** + * An integer extra indication what provisioning modes should be available for the admin app + * to pick. + * + * <p>The default value is {@link #SUPPORTED_MODES_ORGANIZATION_OWNED}. + * + * <p>The value of this extra will determine the contents of the {@link + * #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array that is passed to the admin app as an + * extra to its {@link #ACTION_GET_PROVISIONING_MODE} activity. + * + * <p>If one of the possible admin app choices is a personally-owned work profile, then the + * IMEI and serial number will not be passed to the admin app's {@link + * #ACTION_GET_PROVISIONING_MODE} activity via the {@link #EXTRA_PROVISIONING_IMEI} and {@link + * #EXTRA_PROVISIONING_SERIAL_NUMBER} respectively. + * + * <p>This extra is only respected when provided alongside the {@link + * #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent action. + * + * @see #SUPPORTED_MODES_ORGANIZATION_OWNED + * @see #SUPPORTED_MODES_PERSONALLY_OWNED + * @see #SUPPORTED_MODES_ORGANIZATION_AND_PERSONALLY_OWNED + * @hide + */ + @SystemApi + public static final String EXTRA_PROVISIONING_SUPPORTED_MODES = + "android.app.extra.PROVISIONING_SUPPORTED_MODES"; + + /** + * An {@link ArrayList} of {@link Integer} extra specifying the allowed provisioning modes. + * <p>This extra will be passed to the admin app's {@link #ACTION_GET_PROVISIONING_MODE} + * activity, whose result intent must contain {@link #EXTRA_PROVISIONING_MODE} set to one of + * the values in this array. + * <p>If the value set to {@link #EXTRA_PROVISIONING_MODE} is not in the array, + * provisioning will fail. + * @see #PROVISIONING_MODE_MANAGED_PROFILE + * @see #PROVISIONING_MODE_FULLY_MANAGED_DEVICE + */ + public static final String EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES = + "android.app.extra.PROVISIONING_ALLOWED_PROVISIONING_MODES"; + + /** * The provisioning mode for fully managed device. */ public static final int PROVISIONING_MODE_FULLY_MANAGED_DEVICE = 1; @@ -2399,6 +2531,11 @@ public class DevicePolicyManager { public static final int PROVISIONING_MODE_MANAGED_PROFILE = 2; /** + * The provisioning mode for a work profile on a personal device. + */ + public static final int PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE = 3; + + /** * Activity action: Starts the administrator to show policy compliance for the provisioning. * This action is used any time that the administrator has an opportunity to show policy * compliance before the end of setup wizard. This could happen as part of the admin-integrated diff --git a/core/java/android/app/admin/FactoryResetProtectionPolicy.java b/core/java/android/app/admin/FactoryResetProtectionPolicy.java index aa94e817c152..40ae1f0c11ea 100644 --- a/core/java/android/app/admin/FactoryResetProtectionPolicy.java +++ b/core/java/android/app/admin/FactoryResetProtectionPolicy.java @@ -26,10 +26,10 @@ import android.content.ComponentName; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; @@ -201,10 +201,10 @@ public final class FactoryResetProtectionPolicy implements Parcelable { * @hide */ @Nullable - public static FactoryResetProtectionPolicy readFromXml(@NonNull XmlPullParser parser) { + public static FactoryResetProtectionPolicy readFromXml(@NonNull TypedXmlPullParser parser) { try { - boolean factoryResetProtectionEnabled = Boolean.parseBoolean( - parser.getAttributeValue(null, KEY_FACTORY_RESET_PROTECTION_ENABLED)); + boolean factoryResetProtectionEnabled = parser.getAttributeBoolean(null, + KEY_FACTORY_RESET_PROTECTION_ENABLED, false); List<String> factoryResetProtectionAccounts = new ArrayList<>(); int outerDepth = parser.getDepth(); @@ -232,9 +232,9 @@ public final class FactoryResetProtectionPolicy implements Parcelable { /** * @hide */ - public void writeToXml(@NonNull XmlSerializer out) throws IOException { - out.attribute(null, KEY_FACTORY_RESET_PROTECTION_ENABLED, - Boolean.toString(mFactoryResetProtectionEnabled)); + public void writeToXml(@NonNull TypedXmlSerializer out) throws IOException { + out.attributeBoolean(null, KEY_FACTORY_RESET_PROTECTION_ENABLED, + mFactoryResetProtectionEnabled); for (String account : mFactoryResetProtectionAccounts) { out.startTag(null, KEY_FACTORY_RESET_PROTECTION_ACCOUNT); out.attribute(null, ATTR_VALUE, account); diff --git a/core/java/android/app/admin/SystemUpdateInfo.java b/core/java/android/app/admin/SystemUpdateInfo.java index 53b238633fec..b88bf76c96ca 100644 --- a/core/java/android/app/admin/SystemUpdateInfo.java +++ b/core/java/android/app/admin/SystemUpdateInfo.java @@ -21,9 +21,11 @@ import android.annotation.Nullable; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import android.util.Log; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlSerializer; +import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.lang.annotation.Retention; @@ -34,6 +36,7 @@ import java.util.Objects; * A class containing information about a pending system update. */ public final class SystemUpdateInfo implements Parcelable { + private static final String TAG = "SystemUpdateInfo"; /** * Represents it is unknown whether the system update is a security patch. @@ -125,27 +128,32 @@ public final class SystemUpdateInfo implements Parcelable { }; /** @hide */ - public void writeToXml(XmlSerializer out, String tag) throws IOException { + public void writeToXml(TypedXmlSerializer out, String tag) throws IOException { out.startTag(null, tag); - out.attribute(null, ATTR_RECEIVED_TIME, String.valueOf(mReceivedTime)); - out.attribute(null, ATTR_SECURITY_PATCH_STATE, String.valueOf(mSecurityPatchState)); + out.attributeLong(null, ATTR_RECEIVED_TIME, mReceivedTime); + out.attributeInt(null, ATTR_SECURITY_PATCH_STATE, mSecurityPatchState); out.attribute(null, ATTR_ORIGINAL_BUILD , Build.FINGERPRINT); out.endTag(null, tag); } /** @hide */ @Nullable - public static SystemUpdateInfo readFromXml(XmlPullParser parser) { + public static SystemUpdateInfo readFromXml(TypedXmlPullParser parser) { // If an OTA has been applied (build fingerprint has changed), discard stale info. final String buildFingerprint = parser.getAttributeValue(null, ATTR_ORIGINAL_BUILD ); if (!Build.FINGERPRINT.equals(buildFingerprint)) { return null; } - final long receivedTime = - Long.parseLong(parser.getAttributeValue(null, ATTR_RECEIVED_TIME)); - final int securityPatchState = - Integer.parseInt(parser.getAttributeValue(null, ATTR_SECURITY_PATCH_STATE)); - return new SystemUpdateInfo(receivedTime, securityPatchState); + try { + final long receivedTime = + parser.getAttributeLong(null, ATTR_RECEIVED_TIME); + final int securityPatchState = + parser.getAttributeInt(null, ATTR_SECURITY_PATCH_STATE); + return new SystemUpdateInfo(receivedTime, securityPatchState); + } catch (XmlPullParserException e) { + Log.w(TAG, "Load xml failed", e); + return null; + } } @Override diff --git a/core/java/android/app/admin/SystemUpdatePolicy.java b/core/java/android/app/admin/SystemUpdatePolicy.java index 2ba2c0425b2f..68ac4ccf210a 100644 --- a/core/java/android/app/admin/SystemUpdatePolicy.java +++ b/core/java/android/app/admin/SystemUpdatePolicy.java @@ -26,10 +26,10 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.Log; import android.util.Pair; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.lang.annotation.Retention; @@ -741,38 +741,31 @@ public final class SystemUpdatePolicy implements Parcelable { * system server from a validated policy object previously. * @hide */ - public static SystemUpdatePolicy restoreFromXml(XmlPullParser parser) { + public static SystemUpdatePolicy restoreFromXml(TypedXmlPullParser parser) { try { SystemUpdatePolicy policy = new SystemUpdatePolicy(); - String value = parser.getAttributeValue(null, KEY_POLICY_TYPE); - if (value != null) { - policy.mPolicyType = Integer.parseInt(value); - - value = parser.getAttributeValue(null, KEY_INSTALL_WINDOW_START); - if (value != null) { - policy.mMaintenanceWindowStart = Integer.parseInt(value); - } - value = parser.getAttributeValue(null, KEY_INSTALL_WINDOW_END); - if (value != null) { - policy.mMaintenanceWindowEnd = Integer.parseInt(value); + policy.mPolicyType = + parser.getAttributeInt(null, KEY_POLICY_TYPE, TYPE_UNKNOWN); + policy.mMaintenanceWindowStart = + parser.getAttributeInt(null, KEY_INSTALL_WINDOW_START, 0); + policy.mMaintenanceWindowEnd = + parser.getAttributeInt(null, KEY_INSTALL_WINDOW_END, 0); + + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != END_DOCUMENT + && (type != END_TAG || parser.getDepth() > outerDepth)) { + if (type == END_TAG || type == TEXT) { + continue; } - - int outerDepth = parser.getDepth(); - int type; - while ((type = parser.next()) != END_DOCUMENT - && (type != END_TAG || parser.getDepth() > outerDepth)) { - if (type == END_TAG || type == TEXT) { - continue; - } - if (!parser.getName().equals(KEY_FREEZE_TAG)) { - continue; - } - policy.mFreezePeriods.add(new FreezePeriod( - MonthDay.parse(parser.getAttributeValue(null, KEY_FREEZE_START)), - MonthDay.parse(parser.getAttributeValue(null, KEY_FREEZE_END)))); + if (!parser.getName().equals(KEY_FREEZE_TAG)) { + continue; } - return policy; + policy.mFreezePeriods.add(new FreezePeriod( + MonthDay.parse(parser.getAttributeValue(null, KEY_FREEZE_START)), + MonthDay.parse(parser.getAttributeValue(null, KEY_FREEZE_END)))); } + return policy; } catch (NumberFormatException | XmlPullParserException | IOException e) { // Fail through Log.w(TAG, "Load xml failed", e); @@ -783,10 +776,10 @@ public final class SystemUpdatePolicy implements Parcelable { /** * @hide */ - public void saveToXml(XmlSerializer out) throws IOException { - out.attribute(null, KEY_POLICY_TYPE, Integer.toString(mPolicyType)); - out.attribute(null, KEY_INSTALL_WINDOW_START, Integer.toString(mMaintenanceWindowStart)); - out.attribute(null, KEY_INSTALL_WINDOW_END, Integer.toString(mMaintenanceWindowEnd)); + public void saveToXml(TypedXmlSerializer out) throws IOException { + out.attributeInt(null, KEY_POLICY_TYPE, mPolicyType); + out.attributeInt(null, KEY_INSTALL_WINDOW_START, mMaintenanceWindowStart); + out.attributeInt(null, KEY_INSTALL_WINDOW_END, mMaintenanceWindowEnd); for (int i = 0; i < mFreezePeriods.size(); i++) { FreezePeriod interval = mFreezePeriods.get(i); out.startTag(null, KEY_FREEZE_TAG); diff --git a/core/java/android/app/role/IRoleManager.aidl b/core/java/android/app/role/IRoleManager.aidl index 6d790b381ace..5fc25f0422e2 100644 --- a/core/java/android/app/role/IRoleManager.aidl +++ b/core/java/android/app/role/IRoleManager.aidl @@ -53,5 +53,9 @@ interface IRoleManager { List<String> getHeldRolesFromController(in String packageName); - String getDefaultSmsPackage(int userId); + String getBrowserRoleHolder(int userId); + + boolean setBrowserRoleHolder(String packageName, int userId); + + String getSmsRoleHolder(int userId); } diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java index 408ce0f2ab1a..8b2e07b09701 100644 --- a/core/java/android/app/role/RoleManager.java +++ b/core/java/android/app/role/RoleManager.java @@ -613,12 +613,56 @@ public final class RoleManager { } /** - * Allows getting the role holder for {@link #ROLE_SMS} without - * {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}, as required by - * {@link android.provider.Telephony.Sms#getDefaultSmsPackage(Context)} + * Get the role holder of {@link #ROLE_BROWSER} without requiring + * {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}, as in + * {@link android.content.pm.PackageManager#getDefaultBrowserPackageNameAsUser(int)} + * + * @param userId the user ID + * @return the package name of the default browser, or {@code null} if none + * + * @hide + */ + @Nullable + //@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public String getBrowserRoleHolder(@UserIdInt int userId) { + try { + return mService.getBrowserRoleHolder(userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Set the role holder of {@link #ROLE_BROWSER} requiring + * {@link Manifest.permission.SET_PREFERRED_APPLICATIONS} instead of + * {@link Manifest.permission#MANAGE_ROLE_HOLDERS}, as in + * {@link android.content.pm.PackageManager#setDefaultBrowserPackageNameAsUser(String, int)} + * + * @param packageName the package name of the default browser, or {@code null} if none + * @param userId the user ID + * @return whether the default browser was set successfully + * + * @hide + */ + @Nullable + @RequiresPermission(Manifest.permission.SET_PREFERRED_APPLICATIONS) + //@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public boolean setBrowserRoleHolder(@Nullable String packageName, @UserIdInt int userId) { + try { + return mService.setBrowserRoleHolder(packageName, userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Allows getting the role holder for {@link #ROLE_SMS} without requiring + * {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}, as in + * {@link android.provider.Telephony.Sms#getDefaultSmsPackage(Context)}. + * + * @param userId the user ID to get the default SMS package for + * @return the package name of the default SMS app, or {@code null} if none * - * @param userId The user ID to get the default SMS package for. - * @return the package name of the default SMS app, or {@code null} if not configured. * @hide */ @Nullable @@ -626,7 +670,7 @@ public final class RoleManager { @TestApi public String getSmsRoleHolder(@UserIdInt int userId) { try { - return mService.getDefaultSmsPackage(userId); + return mService.getSmsRoleHolder(userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java index 130a20dfb07b..c4333ac44e34 100644 --- a/core/java/android/appwidget/AppWidgetProviderInfo.java +++ b/core/java/android/appwidget/AppWidgetProviderInfo.java @@ -68,6 +68,9 @@ public class AppWidgetProviderInfo implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface ResizeModeFlags {} + /** {@hide} */ + public static final int WIDGET_CATEGORY_UNKNOWN = -1; + /** * Indicates that the widget can be displayed on the home screen. This is the default value. */ diff --git a/core/java/android/content/SyncAdaptersCache.java b/core/java/android/content/SyncAdaptersCache.java index 58445a7f9242..495f94f98f95 100644 --- a/core/java/android/content/SyncAdaptersCache.java +++ b/core/java/android/content/SyncAdaptersCache.java @@ -25,12 +25,12 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.AttributeSet; import android.util.SparseArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.annotations.GuardedBy; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; @@ -142,12 +142,12 @@ public class SyncAdaptersCache extends RegisteredServicesCache<SyncAdapterType> } static class MySerializer implements XmlSerializerAndParser<SyncAdapterType> { - public void writeAsXml(SyncAdapterType item, XmlSerializer out) throws IOException { + public void writeAsXml(SyncAdapterType item, TypedXmlSerializer out) throws IOException { out.attribute(null, "authority", item.authority); out.attribute(null, "accountType", item.accountType); } - public SyncAdapterType createFromXml(XmlPullParser parser) + public SyncAdapterType createFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final String authority = parser.getAttributeValue(null, "authority"); final String accountType = parser.getAttributeValue(null, "accountType"); diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl index 6ccbc36e26f6..9a73be9d44a4 100644 --- a/core/java/android/content/pm/IPackageInstallerSession.aidl +++ b/core/java/android/content/pm/IPackageInstallerSession.aidl @@ -33,6 +33,7 @@ interface IPackageInstallerSession { ParcelFileDescriptor openRead(String name); void write(String name, long offsetBytes, long lengthBytes, in ParcelFileDescriptor fd); + void stageViaHardLink(String target); void addChecksums(String name, in Checksum[] checksums); diff --git a/core/java/android/content/pm/IntentFilterVerificationInfo.java b/core/java/android/content/pm/IntentFilterVerificationInfo.java index 0a913acba9f5..56b8bd80a06a 100644 --- a/core/java/android/content/pm/IntentFilterVerificationInfo.java +++ b/core/java/android/content/pm/IntentFilterVerificationInfo.java @@ -16,11 +16,11 @@ package android.content.pm; -import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; -import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK; +import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER; +import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; import android.annotation.SystemApi; import android.os.Parcel; @@ -28,12 +28,13 @@ import android.os.Parcelable; import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; @@ -72,7 +73,7 @@ public final class IntentFilterVerificationInfo implements Parcelable { } /** @hide */ - public IntentFilterVerificationInfo(XmlPullParser parser) + public IntentFilterVerificationInfo(TypedXmlPullParser parser) throws IOException, XmlPullParserException { readFromXml(parser); } @@ -121,7 +122,7 @@ public final class IntentFilterVerificationInfo implements Parcelable { return sb.toString(); } - String getStringFromXml(XmlPullParser parser, String attribute, String defaultValue) { + String getStringFromXml(TypedXmlPullParser parser, String attribute, String defaultValue) { String value = parser.getAttributeValue(null, attribute); if (value == null) { String msg = "Missing element under " + TAG +": " + attribute + " at " + @@ -133,20 +134,12 @@ public final class IntentFilterVerificationInfo implements Parcelable { } } - int getIntFromXml(XmlPullParser parser, String attribute, int defaultValue) { - String value = parser.getAttributeValue(null, attribute); - if (TextUtils.isEmpty(value)) { - String msg = "Missing element under " + TAG +": " + attribute + " at " + - parser.getPositionDescription(); - Log.w(TAG, msg); - return defaultValue; - } else { - return Integer.parseInt(value); - } + int getIntFromXml(TypedXmlPullParser parser, String attribute, int defaultValue) { + return parser.getAttributeInt(null, attribute, defaultValue); } /** @hide */ - public void readFromXml(XmlPullParser parser) throws XmlPullParserException, + public void readFromXml(TypedXmlPullParser parser) throws XmlPullParserException, IOException { mPackageName = getStringFromXml(parser, ATTR_PACKAGE_NAME, null); if (mPackageName == null) { @@ -182,9 +175,9 @@ public final class IntentFilterVerificationInfo implements Parcelable { } /** @hide */ - public void writeToXml(XmlSerializer serializer) throws IOException { + public void writeToXml(TypedXmlSerializer serializer) throws IOException { serializer.attribute(null, ATTR_PACKAGE_NAME, mPackageName); - serializer.attribute(null, ATTR_STATUS, String.valueOf(mStatus)); + serializer.attributeInt(null, ATTR_STATUS, mStatus); for (String str : mDomains) { serializer.startTag(null, TAG_DOMAIN); serializer.attribute(null, ATTR_DOMAIN_NAME, str); diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 0dcfd38294b9..d4a98f82a4f7 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1049,6 +1049,31 @@ public class PackageInstaller { } /** + * Populate an APK file by creating a hard link to avoid the need to copy. + * <p> + * Note this API is used by RollbackManager only and can only be called from system_server. + * {@code target} will be relabeled if link is created successfully. RollbackManager has + * to delete {@code target} when the session is committed successfully to avoid SELinux + * label conflicts. + * <p> + * Note No more bytes should be written to the file once the link is created successfully. + * + * @param target the path of the link target + * + * @hide + */ + public void stageViaHardLink(String target) throws IOException { + try { + mSession.stageViaHardLink(target); + } catch (RuntimeException e) { + ExceptionUtils.maybeUnwrapIOException(e); + throw e; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Ensure that any outstanding data for given stream has been committed * to disk. This is only valid for streams returned from * {@link #openWrite(String, long, long)}. diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index 2fca980e764c..99258712030c 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -41,6 +41,8 @@ import android.util.ArraySet; import android.util.DebugUtils; import android.util.Pair; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; @@ -560,7 +562,7 @@ public class PackageUserState { * @param out the {@link XmlSerializer} object * @throws IOException */ - public void saveToXml(XmlSerializer out) throws IOException { + public void saveToXml(TypedXmlSerializer out) throws IOException { if (dialogInfo != null) { out.startTag(null, TAG_DIALOG_INFO); dialogInfo.saveToXml(out); @@ -594,7 +596,7 @@ public class PackageUserState { * @param in the reader * @return */ - public static SuspendParams restoreFromXml(XmlPullParser in) throws IOException { + public static SuspendParams restoreFromXml(TypedXmlPullParser in) throws IOException { SuspendDialogInfo readDialogInfo = null; PersistableBundle readAppExtras = null; PersistableBundle readLauncherExtras = null; diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index 192470e964e0..7ecb11248d23 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -36,21 +36,21 @@ import android.util.IntArray; import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; -import com.android.internal.util.FastXmlSerializer; + +import libcore.io.IoUtils; import com.google.android.collect.Lists; import com.google.android.collect.Maps; -import libcore.io.IoUtils; - import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileDescriptor; @@ -58,7 +58,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -672,8 +671,7 @@ public abstract class RegisteredServicesCache<V> { */ private void readPersistentServicesLocked(InputStream is) throws XmlPullParserException, IOException { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(is, StandardCharsets.UTF_8.name()); + TypedXmlPullParser parser = Xml.resolvePullParser(is); int eventType = parser.getEventType(); while (eventType != XmlPullParser.START_TAG && eventType != XmlPullParser.END_DOCUMENT) { @@ -690,8 +688,7 @@ public abstract class RegisteredServicesCache<V> { if (service == null) { break; } - String uidString = parser.getAttributeValue(null, "uid"); - final int uid = Integer.parseInt(uidString); + final int uid = parser.getAttributeInt(null, "uid"); final int userId = UserHandle.getUserId(uid); final UserServices<V> user = findOrCreateUserLocked(userId, false /*loadFromFileIfNew*/) ; @@ -762,14 +759,13 @@ public abstract class RegisteredServicesCache<V> { FileOutputStream fos = null; try { fos = atomicFile.startWrite(); - XmlSerializer out = new FastXmlSerializer(); - out.setOutput(fos, StandardCharsets.UTF_8.name()); + TypedXmlSerializer out = Xml.resolveSerializer(fos); out.startDocument(null, true); out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); out.startTag(null, "services"); for (Map.Entry<V, Integer> service : user.persistentServices.entrySet()) { out.startTag(null, "service"); - out.attribute(null, "uid", Integer.toString(service.getValue())); + out.attributeInt(null, "uid", service.getValue()); mSerializerAndParser.writeAsXml(service.getKey(), out); out.endTag(null, "service"); } diff --git a/core/java/android/content/pm/SuspendDialogInfo.java b/core/java/android/content/pm/SuspendDialogInfo.java index 851a08116f56..60f321883e98 100644 --- a/core/java/android/content/pm/SuspendDialogInfo.java +++ b/core/java/android/content/pm/SuspendDialogInfo.java @@ -29,13 +29,12 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlSerializer; - import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -169,38 +168,38 @@ public final class SuspendDialogInfo implements Parcelable { /** * @hide */ - public void saveToXml(XmlSerializer out) throws IOException { + public void saveToXml(TypedXmlSerializer out) throws IOException { if (mIconResId != ID_NULL) { - XmlUtils.writeIntAttribute(out, XML_ATTR_ICON_RES_ID, mIconResId); + out.attributeInt(null, XML_ATTR_ICON_RES_ID, mIconResId); } if (mTitleResId != ID_NULL) { - XmlUtils.writeIntAttribute(out, XML_ATTR_TITLE_RES_ID, mTitleResId); + out.attributeInt(null, XML_ATTR_TITLE_RES_ID, mTitleResId); } if (mDialogMessageResId != ID_NULL) { - XmlUtils.writeIntAttribute(out, XML_ATTR_DIALOG_MESSAGE_RES_ID, mDialogMessageResId); + out.attributeInt(null, XML_ATTR_DIALOG_MESSAGE_RES_ID, mDialogMessageResId); } else { XmlUtils.writeStringAttribute(out, XML_ATTR_DIALOG_MESSAGE, mDialogMessage); } if (mNeutralButtonTextResId != ID_NULL) { - XmlUtils.writeIntAttribute(out, XML_ATTR_BUTTON_TEXT_RES_ID, mNeutralButtonTextResId); + out.attributeInt(null, XML_ATTR_BUTTON_TEXT_RES_ID, mNeutralButtonTextResId); } - XmlUtils.writeIntAttribute(out, XML_ATTR_BUTTON_ACTION, mNeutralButtonAction); + out.attributeInt(null, XML_ATTR_BUTTON_ACTION, mNeutralButtonAction); } /** * @hide */ - public static SuspendDialogInfo restoreFromXml(XmlPullParser in) { + public static SuspendDialogInfo restoreFromXml(TypedXmlPullParser in) { final SuspendDialogInfo.Builder dialogInfoBuilder = new SuspendDialogInfo.Builder(); try { - final int iconId = XmlUtils.readIntAttribute(in, XML_ATTR_ICON_RES_ID, ID_NULL); - final int titleId = XmlUtils.readIntAttribute(in, XML_ATTR_TITLE_RES_ID, ID_NULL); - final int buttonTextId = XmlUtils.readIntAttribute(in, XML_ATTR_BUTTON_TEXT_RES_ID, - ID_NULL); - final int buttonAction = XmlUtils.readIntAttribute(in, XML_ATTR_BUTTON_ACTION, - BUTTON_ACTION_MORE_DETAILS); - final int dialogMessageResId = XmlUtils.readIntAttribute( - in, XML_ATTR_DIALOG_MESSAGE_RES_ID, ID_NULL); + final int iconId = in.getAttributeInt(null, XML_ATTR_ICON_RES_ID, ID_NULL); + final int titleId = in.getAttributeInt(null, XML_ATTR_TITLE_RES_ID, ID_NULL); + final int buttonTextId = + in.getAttributeInt(null, XML_ATTR_BUTTON_TEXT_RES_ID, ID_NULL); + final int buttonAction = + in.getAttributeInt(null, XML_ATTR_BUTTON_ACTION, BUTTON_ACTION_MORE_DETAILS); + final int dialogMessageResId = + in.getAttributeInt(null, XML_ATTR_DIALOG_MESSAGE_RES_ID, ID_NULL); final String dialogMessage = XmlUtils.readStringAttribute(in, XML_ATTR_DIALOG_MESSAGE); if (iconId != ID_NULL) { diff --git a/core/java/android/content/pm/XmlSerializerAndParser.java b/core/java/android/content/pm/XmlSerializerAndParser.java index 5dce83902f78..51cd6ca60f59 100644 --- a/core/java/android/content/pm/XmlSerializerAndParser.java +++ b/core/java/android/content/pm/XmlSerializerAndParser.java @@ -17,6 +17,10 @@ package android.content.pm; import android.compat.annotation.UnsupportedAppUsage; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; + +import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -26,8 +30,16 @@ import java.io.IOException; /** @hide */ public interface XmlSerializerAndParser<T> { + void writeAsXml(T item, TypedXmlSerializer out) throws IOException; + T createFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException; + @UnsupportedAppUsage - void writeAsXml(T item, XmlSerializer out) throws IOException; + default void writeAsXml(T item, XmlSerializer out) throws IOException { + writeAsXml(item, XmlUtils.makeTyped(out)); + } + @UnsupportedAppUsage - T createFromXml(XmlPullParser parser) throws IOException, XmlPullParserException; + default T createFromXml(XmlPullParser parser) throws IOException, XmlPullParserException { + return createFromXml(XmlUtils.makeTyped(parser)); + } } diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java index d9c1063cd39d..366734e9bf11 100644 --- a/core/java/android/hardware/display/BrightnessConfiguration.java +++ b/core/java/android/hardware/display/BrightnessConfiguration.java @@ -24,13 +24,13 @@ import android.content.pm.ApplicationInfo; import android.os.Parcel; import android.os.Parcelable; import android.util.Pair; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; @@ -343,15 +343,15 @@ public final class BrightnessConfiguration implements Parcelable { * * @hide */ - public void saveToXml(@NonNull XmlSerializer serializer) throws IOException { + public void saveToXml(@NonNull TypedXmlSerializer serializer) throws IOException { serializer.startTag(null, TAG_BRIGHTNESS_CURVE); if (mDescription != null) { serializer.attribute(null, ATTR_DESCRIPTION, mDescription); } for (int i = 0; i < mLux.length; i++) { serializer.startTag(null, TAG_BRIGHTNESS_POINT); - serializer.attribute(null, ATTR_LUX, Float.toString(mLux[i])); - serializer.attribute(null, ATTR_NITS, Float.toString(mNits[i])); + serializer.attributeFloat(null, ATTR_LUX, mLux[i]); + serializer.attributeFloat(null, ATTR_NITS, mNits[i]); serializer.endTag(null, TAG_BRIGHTNESS_POINT); } serializer.endTag(null, TAG_BRIGHTNESS_CURVE); @@ -370,7 +370,7 @@ public final class BrightnessConfiguration implements Parcelable { final int category = entry.getKey(); final BrightnessCorrection correction = entry.getValue(); serializer.startTag(null, TAG_BRIGHTNESS_CORRECTION); - serializer.attribute(null, ATTR_CATEGORY, Integer.toString(category)); + serializer.attributeInt(null, ATTR_CATEGORY, category); correction.saveToXml(serializer); serializer.endTag(null, TAG_BRIGHTNESS_CORRECTION); } @@ -378,19 +378,18 @@ public final class BrightnessConfiguration implements Parcelable { serializer.startTag(null, TAG_BRIGHTNESS_PARAMS); if (mShouldCollectColorSamples) { - serializer.attribute(null, ATTR_COLLECT_COLOR, Boolean.toString(true)); + serializer.attributeBoolean(null, ATTR_COLLECT_COLOR, true); } if (mShortTermModelTimeout >= 0) { - serializer.attribute(null, ATTR_MODEL_TIMEOUT, - Long.toString(mShortTermModelTimeout)); + serializer.attributeLong(null, ATTR_MODEL_TIMEOUT, mShortTermModelTimeout); } if (!Float.isNaN(mShortTermModelLowerLuxMultiplier)) { - serializer.attribute(null, ATTR_MODEL_LOWER_BOUND, - Float.toString(mShortTermModelLowerLuxMultiplier)); + serializer.attributeFloat(null, ATTR_MODEL_LOWER_BOUND, + mShortTermModelLowerLuxMultiplier); } if (!Float.isNaN(mShortTermModelUpperLuxMultiplier)) { - serializer.attribute(null, ATTR_MODEL_UPPER_BOUND, - Float.toString(mShortTermModelUpperLuxMultiplier)); + serializer.attributeFloat(null, ATTR_MODEL_UPPER_BOUND, + mShortTermModelUpperLuxMultiplier); } serializer.endTag(null, TAG_BRIGHTNESS_PARAMS); } @@ -408,7 +407,7 @@ public final class BrightnessConfiguration implements Parcelable { * * @hide */ - public static BrightnessConfiguration loadFromXml(@NonNull XmlPullParser parser) + public static BrightnessConfiguration loadFromXml(@NonNull TypedXmlPullParser parser) throws IOException, XmlPullParserException { String description = null; List<Float> luxList = new ArrayList<>(); @@ -440,22 +439,17 @@ public final class BrightnessConfiguration implements Parcelable { continue; } final String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME); - final String categoryText = parser.getAttributeValue(null, ATTR_CATEGORY); + final int category = parser.getAttributeInt(null, ATTR_CATEGORY, -1); BrightnessCorrection correction = BrightnessCorrection.loadFromXml(parser); if (packageName != null) { correctionsByPackageName.put(packageName, correction); - } else if (categoryText != null) { - try { - final int category = Integer.parseInt(categoryText); - correctionsByCategory.put(category, correction); - } catch (NullPointerException | NumberFormatException e) { - continue; - } + } else if (category != -1) { + correctionsByCategory.put(category, correction); } } } else if (TAG_BRIGHTNESS_PARAMS.equals(parser.getName())) { shouldCollectColorSamples = - Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_COLLECT_COLOR)); + parser.getAttributeBoolean(null, ATTR_COLLECT_COLOR, false); Long timeout = loadLongFromXml(parser, ATTR_MODEL_TIMEOUT); if (timeout != null) { shortTermModelTimeout = timeout; @@ -491,23 +485,16 @@ public final class BrightnessConfiguration implements Parcelable { return builder.build(); } - private static float loadFloatFromXml(XmlPullParser parser, String attribute) { - final String string = parser.getAttributeValue(null, attribute); - try { - return Float.parseFloat(string); - } catch (NullPointerException | NumberFormatException e) { - return Float.NaN; - } + private static float loadFloatFromXml(TypedXmlPullParser parser, String attribute) { + return parser.getAttributeFloat(null, attribute, Float.NaN); } - private static Long loadLongFromXml(XmlPullParser parser, String attribute) { - final String string = parser.getAttributeValue(null, attribute); + private static Long loadLongFromXml(TypedXmlPullParser parser, String attribute) { try { - return Long.parseLong(string); - } catch (NullPointerException | NumberFormatException e) { - // Ignoring + return parser.getAttributeLong(null, attribute); + } catch (Exception e) { + return null; } - return null; } /** diff --git a/core/java/android/hardware/display/BrightnessCorrection.java b/core/java/android/hardware/display/BrightnessCorrection.java index bbfc45edb89a..2919ec3fbf08 100644 --- a/core/java/android/hardware/display/BrightnessCorrection.java +++ b/core/java/android/hardware/display/BrightnessCorrection.java @@ -23,12 +23,12 @@ import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.util.MathUtils; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.util.XmlUtils; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; @@ -153,7 +153,7 @@ public final class BrightnessCorrection implements Parcelable { * * @hide */ - public void saveToXml(XmlSerializer serializer) throws IOException { + public void saveToXml(TypedXmlSerializer serializer) throws IOException { mImplementation.saveToXml(serializer); } @@ -170,7 +170,7 @@ public final class BrightnessCorrection implements Parcelable { * * @hide */ - public static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException, + public static BrightnessCorrection loadFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final int depth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, depth)) { @@ -181,20 +181,15 @@ public final class BrightnessCorrection implements Parcelable { return null; } - private static float loadFloatFromXml(XmlPullParser parser, String attribute) { - final String string = parser.getAttributeValue(null, attribute); - try { - return Float.parseFloat(string); - } catch (NullPointerException | NumberFormatException e) { - return Float.NaN; - } + private static float loadFloatFromXml(TypedXmlPullParser parser, String attribute) { + return parser.getAttributeFloat(null, attribute, Float.NaN); } private interface BrightnessCorrectionImplementation { float apply(float brightness); String toString(); void writeToParcel(Parcel dest); - void saveToXml(XmlSerializer serializer) throws IOException; + void saveToXml(TypedXmlSerializer serializer) throws IOException; // Package-private static methods: // static BrightnessCorrection readFromParcel(Parcel in); // static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException, @@ -263,10 +258,10 @@ public final class BrightnessCorrection implements Parcelable { } @Override - public void saveToXml(XmlSerializer serializer) throws IOException { + public void saveToXml(TypedXmlSerializer serializer) throws IOException { serializer.startTag(null, TAG_SCALE_AND_TRANSLATE_LOG); - serializer.attribute(null, ATTR_SCALE, Float.toString(mScale)); - serializer.attribute(null, ATTR_TRANSLATE, Float.toString(mTranslate)); + serializer.attributeFloat(null, ATTR_SCALE, mScale); + serializer.attributeFloat(null, ATTR_TRANSLATE, mTranslate); serializer.endTag(null, TAG_SCALE_AND_TRANSLATE_LOG); } @@ -276,7 +271,7 @@ public final class BrightnessCorrection implements Parcelable { return BrightnessCorrection.createScaleAndTranslateLog(scale, translate); } - static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException, + static BrightnessCorrection loadFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final float scale = loadFloatFromXml(parser, ATTR_SCALE); final float translate = loadFloatFromXml(parser, ATTR_TRANSLATE); diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java index 9876fc650f21..183f500572bd 100644 --- a/core/java/android/net/Ikev2VpnProfile.java +++ b/core/java/android/net/Ikev2VpnProfile.java @@ -16,12 +16,9 @@ package android.net; -import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_PSK; -import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_RSA; -import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_USER_PASS; - import static com.android.internal.annotations.VisibleForTesting.Visibility; import static com.android.internal.util.Preconditions.checkStringNotEmpty; +import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU; import android.annotation.NonNull; import android.annotation.Nullable; @@ -158,9 +155,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { // IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6 // networks, the VPN must provide a link fulfilling the stricter of the two conditions // (at least that of the IPv6 MTU). - if (mMaxMtu < LinkProperties.MIN_MTU_V6) { - throw new IllegalArgumentException( - "Max MTU must be at least" + LinkProperties.MIN_MTU_V6); + if (mMaxMtu < IPV6_MIN_MTU) { + throw new IllegalArgumentException("Max MTU must be at least" + IPV6_MIN_MTU); } switch (mType) { @@ -811,9 +807,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { // IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6 // networks, the VPN must provide a link fulfilling the stricter of the two conditions // (at least that of the IPv6 MTU). - if (mtu < LinkProperties.MIN_MTU_V6) { - throw new IllegalArgumentException( - "Max MTU must be at least " + LinkProperties.MIN_MTU_V6); + if (mtu < IPV6_MIN_MTU) { + throw new IllegalArgumentException("Max MTU must be at least " + IPV6_MIN_MTU); } mMaxMtu = mtu; return this; diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java index fa1497dcbc43..b48c1fdaf1b2 100644 --- a/core/java/android/net/IpSecTransform.java +++ b/core/java/android/net/IpSecTransform.java @@ -17,8 +17,6 @@ package android.net; import static android.net.IpSecManager.INVALID_RESOURCE_ID; -import static com.android.internal.util.Preconditions.checkNotNull; - import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -28,7 +26,6 @@ import android.annotation.SystemApi; import android.content.Context; import android.content.pm.PackageManager; import android.os.Binder; -import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; @@ -182,7 +179,6 @@ public final class IpSecTransform implements AutoCloseable { try { IIpSecService svc = getIpSecService(); svc.deleteTransform(mResourceId); - stopNattKeepalive(); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } catch (Exception e) { @@ -213,36 +209,6 @@ public final class IpSecTransform implements AutoCloseable { private int mResourceId; private final Context mContext; private final CloseGuard mCloseGuard = CloseGuard.get(); - private ConnectivityManager.PacketKeepalive mKeepalive; - private Handler mCallbackHandler; - private final ConnectivityManager.PacketKeepaliveCallback mKeepaliveCallback = - new ConnectivityManager.PacketKeepaliveCallback() { - - @Override - public void onStarted() { - synchronized (this) { - mCallbackHandler.post(() -> mUserKeepaliveCallback.onStarted()); - } - } - - @Override - public void onStopped() { - synchronized (this) { - mKeepalive = null; - mCallbackHandler.post(() -> mUserKeepaliveCallback.onStopped()); - } - } - - @Override - public void onError(int error) { - synchronized (this) { - mKeepalive = null; - mCallbackHandler.post(() -> mUserKeepaliveCallback.onError(error)); - } - } - }; - - private NattKeepaliveCallback mUserKeepaliveCallback; /** @hide */ @VisibleForTesting @@ -274,76 +240,6 @@ public final class IpSecTransform implements AutoCloseable { public void onError(int error) {} } - /** - * Start a NAT-T keepalive session for the current transform. - * - * For a transform that is using UDP encapsulated IPv4, NAT-T offloading provides - * a power efficient mechanism of sending NAT-T packets at a specified interval. - * - * @param userCallback a {@link #NattKeepaliveCallback} to receive asynchronous status - * information about the requested NAT-T keepalive session. - * @param intervalSeconds the interval between NAT-T keepalives being sent. The - * the allowed range is between 20 and 3600 seconds. - * @param handler a handler on which to post callbacks when received. - * - * @hide - */ - @RequiresPermission(anyOf = { - android.Manifest.permission.MANAGE_IPSEC_TUNNELS, - android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD - }) - public void startNattKeepalive(@NonNull NattKeepaliveCallback userCallback, - int intervalSeconds, @NonNull Handler handler) throws IOException { - checkNotNull(userCallback); - if (intervalSeconds < 20 || intervalSeconds > 3600) { - throw new IllegalArgumentException("Invalid NAT-T keepalive interval"); - } - checkNotNull(handler); - if (mResourceId == INVALID_RESOURCE_ID) { - throw new IllegalStateException( - "Packet keepalive cannot be started for an inactive transform"); - } - - synchronized (mKeepaliveCallback) { - if (mKeepaliveCallback != null) { - throw new IllegalStateException("Keepalive already active"); - } - - mUserKeepaliveCallback = userCallback; - ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService( - Context.CONNECTIVITY_SERVICE); - mKeepalive = cm.startNattKeepalive( - mConfig.getNetwork(), intervalSeconds, mKeepaliveCallback, - NetworkUtils.numericToInetAddress(mConfig.getSourceAddress()), - 4500, // FIXME urgently, we need to get the port number from the Encap socket - NetworkUtils.numericToInetAddress(mConfig.getDestinationAddress())); - mCallbackHandler = handler; - } - } - - /** - * Stop an ongoing NAT-T keepalive session. - * - * Calling this API will request that an ongoing NAT-T keepalive session be terminated. - * If this API is not called when a Transform is closed, the underlying NAT-T session will - * be terminated automatically. - * - * @hide - */ - @RequiresPermission(anyOf = { - android.Manifest.permission.MANAGE_IPSEC_TUNNELS, - android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD - }) - public void stopNattKeepalive() { - synchronized (mKeepaliveCallback) { - if (mKeepalive == null) { - Log.e(TAG, "No active keepalive to stop"); - return; - } - mKeepalive.stop(); - } - } - /** This class is used to build {@link IpSecTransform} objects. */ public static class Builder { private Context mContext; diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 81e6e788734b..e41ed72b259c 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -82,8 +82,7 @@ public final class LinkProperties implements Parcelable { private static final int MIN_MTU = 68; - /** @hide */ - public static final int MIN_MTU_V6 = 1280; + private static final int MIN_MTU_V6 = 1280; private static final int MAX_MTU = 10000; diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java index 74a336d36b57..e65c27c2f4bb 100644 --- a/core/java/android/net/NetworkIdentity.java +++ b/core/java/android/net/NetworkIdentity.java @@ -17,7 +17,6 @@ package android.net; import static android.net.ConnectivityManager.TYPE_WIFI; -import static android.net.ConnectivityManager.isNetworkTypeMobile; import android.annotation.Nullable; import android.content.Context; @@ -26,7 +25,6 @@ import android.net.wifi.WifiManager; import android.os.Build; import android.service.NetworkIdentityProto; import android.telephony.Annotation.NetworkType; -import android.util.Slog; import android.util.proto.ProtoOutputStream; import java.util.Objects; @@ -194,18 +192,9 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { boolean metered = !state.networkCapabilities.hasCapability( NetworkCapabilities.NET_CAPABILITY_NOT_METERED); - if (isNetworkTypeMobile(type)) { - if (state.subscriberId == null) { - if (state.networkInfo.getState() != NetworkInfo.State.DISCONNECTED && - state.networkInfo.getState() != NetworkInfo.State.UNKNOWN) { - Slog.w(TAG, "Active mobile network without subscriber! ni = " - + state.networkInfo); - } - } - - subscriberId = state.subscriberId; + subscriberId = state.subscriberId; - } else if (type == TYPE_WIFI) { + if (type == TYPE_WIFI) { if (state.networkId != null) { networkId = state.networkId; } else { diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index a4af0dbed0bd..e2e1bbe8487f 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -1946,7 +1946,13 @@ public final class Debug */ public static final int MEMINFO_KRECLAIMABLE = 15; /** @hide */ - public static final int MEMINFO_COUNT = 16; + public static final int MEMINFO_ACTIVE = 16; + /** @hide */ + public static final int MEMINFO_INACTIVE = 17; + /** @hide */ + public static final int MEMINFO_UNEVICTABLE = 18; + /** @hide */ + public static final int MEMINFO_COUNT = 19; /** * Retrieves /proc/meminfo. outSizes is filled with fields diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl index 100166814975..9855a9a98cfb 100644 --- a/core/java/android/permission/IPermissionManager.aidl +++ b/core/java/android/permission/IPermissionManager.aidl @@ -73,10 +73,6 @@ interface IPermissionManager { void resetRuntimePermissions(); - boolean setDefaultBrowser(String packageName, int userId); - - String getDefaultBrowser(int userId); - void grantDefaultPermissionsToEnabledCarrierApps(in String[] packageNames, int userId); void grantDefaultPermissionsToEnabledImsServices(in String[] packageNames, int userId); diff --git a/core/java/android/permission/PermissionManagerInternal.java b/core/java/android/permission/PermissionManagerInternal.java index 3134ec06fe50..71674311965c 100644 --- a/core/java/android/permission/PermissionManagerInternal.java +++ b/core/java/android/permission/PermissionManagerInternal.java @@ -21,10 +21,6 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.os.UserHandle; -import com.android.internal.util.function.TriFunction; - -import java.util.function.BiFunction; - /** * Internal interfaces to be used by other components within the system server. * @@ -50,33 +46,6 @@ public abstract class PermissionManagerInternal { @UserIdInt int userId); } - /** Interface to override permission checks via composition */ - public interface CheckPermissionDelegate { - /** - * Checks whether the given package has been granted the specified permission. - * - * @return If the package has the permission, PERMISSION_GRANTED is - * returned. If it does not have the permission, PERMISSION_DENIED - * is returned. - * - * @see android.content.pm.PackageManager#checkPermission(String, String) - */ - int checkPermission(String permName, String pkgName, int userId, - TriFunction<String, String, Integer, Integer> superImpl); - - /** - /** - * Checks whether the given uid has been granted the specified permission. - * - * @return If the package has the permission, PERMISSION_GRANTED is - * returned. If it does not have the permission, PERMISSION_DENIED - * is returned. - * - */ - int checkUidPermission(String permName, int uid, - BiFunction<String, Integer, Integer> superImpl); - } - /** * Get the state of the runtime permissions as xml file. * diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index aa9e289345db..440eeb1619f0 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -1517,8 +1517,10 @@ public abstract class NotificationListenerService extends Service { */ public static class Ranking { - /** Value signifying that the user has not expressed a per-app visibility override value. - * @hide */ + /** + * Value signifying that the user and device policy manager have not expressed a lockscreen + * visibility override for a notification. + */ public static final int VISIBILITY_NO_OVERRIDE = NotificationManager.VISIBILITY_NO_OVERRIDE; /** @@ -1695,14 +1697,13 @@ public abstract class NotificationListenerService extends Service { } /** - * Returns the user specified visibility for the package that posted - * this notification, or + * Returns the user or device policy manager specified visibility (see + * {@link Notification#VISIBILITY_PRIVATE}, {@link Notification#VISIBILITY_PUBLIC}, + * {@link Notification#VISIBILITY_SECRET}) for this notification, or * {@link NotificationListenerService.Ranking#VISIBILITY_NO_OVERRIDE} if * no such preference has been expressed. - * @hide */ - @UnsupportedAppUsage - public int getVisibilityOverride() { + public int getLockscreenVisibilityOverride() { return mVisibilityOverride; } diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 787a81bac3c0..12d905588e1e 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -45,13 +45,14 @@ import android.text.format.DateFormat; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; import com.android.internal.R; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; @@ -515,7 +516,7 @@ public class ZenModeConfig implements Parcelable { } } - public static ZenModeConfig readXml(XmlPullParser parser) + public static ZenModeConfig readXml(TypedXmlPullParser parser) throws XmlPullParserException, IOException { int type = parser.getEventType(); if (type != XmlPullParser.START_TAG) return null; @@ -611,28 +612,28 @@ public class ZenModeConfig implements Parcelable { * @param version uses XML_VERSION if version is null * @throws IOException */ - public void writeXml(XmlSerializer out, Integer version) throws IOException { + public void writeXml(TypedXmlSerializer out, Integer version) throws IOException { out.startTag(null, ZEN_TAG); out.attribute(null, ZEN_ATT_VERSION, version == null ? Integer.toString(XML_VERSION) : Integer.toString(version)); - out.attribute(null, ZEN_ATT_USER, Integer.toString(user)); + out.attributeInt(null, ZEN_ATT_USER, user); out.startTag(null, ALLOW_TAG); - out.attribute(null, ALLOW_ATT_CALLS, Boolean.toString(allowCalls)); - out.attribute(null, ALLOW_ATT_REPEAT_CALLERS, Boolean.toString(allowRepeatCallers)); - out.attribute(null, ALLOW_ATT_MESSAGES, Boolean.toString(allowMessages)); - out.attribute(null, ALLOW_ATT_REMINDERS, Boolean.toString(allowReminders)); - out.attribute(null, ALLOW_ATT_EVENTS, Boolean.toString(allowEvents)); - out.attribute(null, ALLOW_ATT_CALLS_FROM, Integer.toString(allowCallsFrom)); - out.attribute(null, ALLOW_ATT_MESSAGES_FROM, Integer.toString(allowMessagesFrom)); - out.attribute(null, ALLOW_ATT_ALARMS, Boolean.toString(allowAlarms)); - out.attribute(null, ALLOW_ATT_MEDIA, Boolean.toString(allowMedia)); - out.attribute(null, ALLOW_ATT_SYSTEM, Boolean.toString(allowSystem)); - out.attribute(null, ALLOW_ATT_CONV, Boolean.toString(allowConversations)); - out.attribute(null, ALLOW_ATT_CONV_FROM, Integer.toString(allowConversationsFrom)); + out.attributeBoolean(null, ALLOW_ATT_CALLS, allowCalls); + out.attributeBoolean(null, ALLOW_ATT_REPEAT_CALLERS, allowRepeatCallers); + out.attributeBoolean(null, ALLOW_ATT_MESSAGES, allowMessages); + out.attributeBoolean(null, ALLOW_ATT_REMINDERS, allowReminders); + out.attributeBoolean(null, ALLOW_ATT_EVENTS, allowEvents); + out.attributeInt(null, ALLOW_ATT_CALLS_FROM, allowCallsFrom); + out.attributeInt(null, ALLOW_ATT_MESSAGES_FROM, allowMessagesFrom); + out.attributeBoolean(null, ALLOW_ATT_ALARMS, allowAlarms); + out.attributeBoolean(null, ALLOW_ATT_MEDIA, allowMedia); + out.attributeBoolean(null, ALLOW_ATT_SYSTEM, allowSystem); + out.attributeBoolean(null, ALLOW_ATT_CONV, allowConversations); + out.attributeInt(null, ALLOW_ATT_CONV_FROM, allowConversationsFrom); out.endTag(null, ALLOW_TAG); out.startTag(null, DISALLOW_TAG); - out.attribute(null, DISALLOW_ATT_VISUAL_EFFECTS, Integer.toString(suppressedVisualEffects)); + out.attributeInt(null, DISALLOW_ATT_VISUAL_EFFECTS, suppressedVisualEffects); out.endTag(null, DISALLOW_TAG); if (manualRule != null) { @@ -651,14 +652,13 @@ public class ZenModeConfig implements Parcelable { } out.startTag(null, STATE_TAG); - out.attribute(null, STATE_ATT_CHANNELS_BYPASSING_DND, - Boolean.toString(areChannelsBypassingDnd)); + out.attributeBoolean(null, STATE_ATT_CHANNELS_BYPASSING_DND, areChannelsBypassingDnd); out.endTag(null, STATE_TAG); out.endTag(null, ZEN_TAG); } - public static ZenRule readRuleXml(XmlPullParser parser) { + public static ZenRule readRuleXml(TypedXmlPullParser parser) { final ZenRule rt = new ZenRule(); rt.enabled = safeBoolean(parser, RULE_ATT_ENABLED, true); rt.name = parser.getAttributeValue(null, RULE_ATT_NAME); @@ -691,12 +691,12 @@ public class ZenModeConfig implements Parcelable { return rt; } - public static void writeRuleXml(ZenRule rule, XmlSerializer out) throws IOException { - out.attribute(null, RULE_ATT_ENABLED, Boolean.toString(rule.enabled)); + public static void writeRuleXml(ZenRule rule, TypedXmlSerializer out) throws IOException { + out.attributeBoolean(null, RULE_ATT_ENABLED, rule.enabled); if (rule.name != null) { out.attribute(null, RULE_ATT_NAME, rule.name); } - out.attribute(null, RULE_ATT_ZEN, Integer.toString(rule.zenMode)); + out.attributeInt(null, RULE_ATT_ZEN, rule.zenMode); if (rule.component != null) { out.attribute(null, RULE_ATT_COMPONENT, rule.component.flattenToString()); } @@ -707,7 +707,7 @@ public class ZenModeConfig implements Parcelable { if (rule.conditionId != null) { out.attribute(null, RULE_ATT_CONDITION_ID, rule.conditionId.toString()); } - out.attribute(null, RULE_ATT_CREATION_TIME, Long.toString(rule.creationTime)); + out.attributeLong(null, RULE_ATT_CREATION_TIME, rule.creationTime); if (rule.enabler != null) { out.attribute(null, RULE_ATT_ENABLER, rule.enabler); } @@ -717,10 +717,10 @@ public class ZenModeConfig implements Parcelable { if (rule.zenPolicy != null) { writeZenPolicyXml(rule.zenPolicy, out); } - out.attribute(null, RULE_ATT_MODIFIED, Boolean.toString(rule.modified)); + out.attributeBoolean(null, RULE_ATT_MODIFIED, rule.modified); } - public static Condition readConditionXml(XmlPullParser parser) { + public static Condition readConditionXml(TypedXmlPullParser parser) { final Uri id = safeUri(parser, CONDITION_ATT_ID); if (id == null) return null; final String summary = parser.getAttributeValue(null, CONDITION_ATT_SUMMARY); @@ -737,21 +737,21 @@ public class ZenModeConfig implements Parcelable { } } - public static void writeConditionXml(Condition c, XmlSerializer out) throws IOException { + public static void writeConditionXml(Condition c, TypedXmlSerializer out) throws IOException { out.attribute(null, CONDITION_ATT_ID, c.id.toString()); out.attribute(null, CONDITION_ATT_SUMMARY, c.summary); out.attribute(null, CONDITION_ATT_LINE1, c.line1); out.attribute(null, CONDITION_ATT_LINE2, c.line2); - out.attribute(null, CONDITION_ATT_ICON, Integer.toString(c.icon)); - out.attribute(null, CONDITION_ATT_STATE, Integer.toString(c.state)); - out.attribute(null, CONDITION_ATT_FLAGS, Integer.toString(c.flags)); + out.attributeInt(null, CONDITION_ATT_ICON, c.icon); + out.attributeInt(null, CONDITION_ATT_STATE, c.state); + out.attributeInt(null, CONDITION_ATT_FLAGS, c.flags); } /** * Read the zen policy from xml * Returns null if no zen policy exists */ - public static ZenPolicy readZenPolicyXml(XmlPullParser parser) { + public static ZenPolicy readZenPolicyXml(TypedXmlPullParser parser) { boolean policySet = false; ZenPolicy.Builder builder = new ZenPolicy.Builder(); @@ -845,7 +845,7 @@ public class ZenModeConfig implements Parcelable { /** * Writes zen policy to xml */ - public static void writeZenPolicyXml(ZenPolicy policy, XmlSerializer out) + public static void writeZenPolicyXml(ZenPolicy policy, TypedXmlSerializer out) throws IOException { writeZenPolicyState(ALLOW_ATT_CALLS_FROM, policy.getPriorityCallSenders(), out); writeZenPolicyState(ALLOW_ATT_MESSAGES_FROM, policy.getPriorityMessageSenders(), out); @@ -868,16 +868,16 @@ public class ZenModeConfig implements Parcelable { out); } - private static void writeZenPolicyState(String attr, int val, XmlSerializer out) + private static void writeZenPolicyState(String attr, int val, TypedXmlSerializer out) throws IOException { if (Objects.equals(attr, ALLOW_ATT_CALLS_FROM) || Objects.equals(attr, ALLOW_ATT_MESSAGES_FROM)) { if (val != ZenPolicy.PEOPLE_TYPE_UNSET) { - out.attribute(null, attr, Integer.toString(val)); + out.attributeInt(null, attr, val); } } else { if (val != ZenPolicy.STATE_UNSET) { - out.attribute(null, attr, Integer.toString(val)); + out.attributeInt(null, attr, val); } } } @@ -894,15 +894,16 @@ public class ZenModeConfig implements Parcelable { return source >= SOURCE_ANYONE && source <= MAX_SOURCE; } - private static Boolean unsafeBoolean(XmlPullParser parser, String att) { - final String val = parser.getAttributeValue(null, att); - if (TextUtils.isEmpty(val)) return null; - return Boolean.parseBoolean(val); + private static Boolean unsafeBoolean(TypedXmlPullParser parser, String att) { + try { + return parser.getAttributeBoolean(null, att); + } catch (Exception e) { + return null; + } } - private static boolean safeBoolean(XmlPullParser parser, String att, boolean defValue) { - final String val = parser.getAttributeValue(null, att); - return safeBoolean(val, defValue); + private static boolean safeBoolean(TypedXmlPullParser parser, String att, boolean defValue) { + return parser.getAttributeBoolean(null, att, defValue); } private static boolean safeBoolean(String val, boolean defValue) { @@ -910,24 +911,23 @@ public class ZenModeConfig implements Parcelable { return Boolean.parseBoolean(val); } - private static int safeInt(XmlPullParser parser, String att, int defValue) { - final String val = parser.getAttributeValue(null, att); - return tryParseInt(val, defValue); + private static int safeInt(TypedXmlPullParser parser, String att, int defValue) { + return parser.getAttributeInt(null, att, defValue); } - private static ComponentName safeComponentName(XmlPullParser parser, String att) { + private static ComponentName safeComponentName(TypedXmlPullParser parser, String att) { final String val = parser.getAttributeValue(null, att); if (TextUtils.isEmpty(val)) return null; return ComponentName.unflattenFromString(val); } - private static Uri safeUri(XmlPullParser parser, String att) { + private static Uri safeUri(TypedXmlPullParser parser, String att) { final String val = parser.getAttributeValue(null, att); if (TextUtils.isEmpty(val)) return null; return Uri.parse(val); } - private static long safeLong(XmlPullParser parser, String att, long defValue) { + private static long safeLong(TypedXmlPullParser parser, String att, long defValue) { final String val = parser.getAttributeValue(null, att); return tryParseLong(val, defValue); } diff --git a/core/java/android/util/Xml.java b/core/java/android/util/Xml.java index cc6ed2e4539e..c8193b3b0dbb 100644 --- a/core/java/android/util/Xml.java +++ b/core/java/android/util/Xml.java @@ -18,6 +18,8 @@ package android.util; import android.annotation.NonNull; import android.annotation.Nullable; +import android.system.ErrnoException; +import android.system.Os; import com.android.internal.util.BinaryXmlPullParser; import com.android.internal.util.BinaryXmlSerializer; @@ -35,7 +37,7 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -43,6 +45,7 @@ import java.io.Reader; import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; +import java.util.Arrays; /** * XML utility methods. @@ -59,6 +62,12 @@ public class Xml { public static String FEATURE_RELAXED = "http://xmlpull.org/v1/doc/features.html#relaxed"; /** + * Feature flag: when set, {@link #resolveSerializer(OutputStream)} will + * emit binary XML by default. + */ + private static final boolean ENABLE_BINARY_DEFAULT = false; + + /** * Parses the given xml string and fires events on the given SAX handler. */ public static void parse(String xml, ContentHandler contentHandler) @@ -119,6 +128,7 @@ public class Xml { * * @hide */ + @SuppressWarnings("AndroidFrameworkEfficientXml") public static @NonNull TypedXmlPullParser newFastPullParser() { return XmlUtils.makeTyped(newPullParser()); } @@ -151,8 +161,28 @@ public class Xml { */ public static @NonNull TypedXmlPullParser resolvePullParser(@NonNull InputStream in) throws IOException { - // TODO: add support for binary format - final TypedXmlPullParser xml = newFastPullParser(); + final byte[] magic = new byte[4]; + if (in instanceof FileInputStream) { + try { + Os.pread(((FileInputStream) in).getFD(), magic, 0, magic.length, 0); + } catch (ErrnoException e) { + throw e.rethrowAsIOException(); + } + } else { + if (!in.markSupported()) { + in = new BufferedInputStream(in); + } + in.mark(4); + in.read(magic); + in.reset(); + } + + final TypedXmlPullParser xml; + if (Arrays.equals(magic, BinaryXmlSerializer.PROTOCOL_MAGIC_VERSION_0)) { + xml = newBinaryPullParser(); + } else { + xml = newFastPullParser(); + } try { xml.setInput(in, StandardCharsets.UTF_8.name()); } catch (XmlPullParserException e) { @@ -177,6 +207,7 @@ public class Xml { * * @hide */ + @SuppressWarnings("AndroidFrameworkEfficientXml") public static @NonNull TypedXmlSerializer newFastSerializer() { return XmlUtils.makeTyped(new FastXmlSerializer()); } @@ -209,8 +240,12 @@ public class Xml { */ public static @NonNull TypedXmlSerializer resolveSerializer(@NonNull OutputStream out) throws IOException { - // TODO: add support for binary format - final TypedXmlSerializer xml = newFastSerializer(); + final TypedXmlSerializer xml; + if (ENABLE_BINARY_DEFAULT) { + xml = newBinarySerializer(); + } else { + xml = newFastSerializer(); + } xml.setOutput(out, StandardCharsets.UTF_8.name()); return xml; } diff --git a/core/java/android/uwb/AdapterStateListener.java b/core/java/android/uwb/AdapterStateListener.java new file mode 100644 index 000000000000..8875af385238 --- /dev/null +++ b/core/java/android/uwb/AdapterStateListener.java @@ -0,0 +1,149 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.uwb; + +import android.annotation.NonNull; +import android.os.Binder; +import android.os.RemoteException; +import android.util.Log; +import android.uwb.UwbManager.AdapterStateCallback; +import android.uwb.UwbManager.AdapterStateCallback.StateChangedReason; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Executor; + +/** + * @hide + */ +public class AdapterStateListener extends IUwbAdapterStateCallbacks.Stub { + private static final String TAG = "Uwb.StateListener"; + + private final IUwbAdapter mAdapter; + private boolean mIsRegistered = false; + + private final Map<AdapterStateCallback, Executor> mCallbackMap = new HashMap<>(); + + @StateChangedReason + private int mAdapterStateChangeReason = AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN; + private boolean mAdapterEnabledState = false; + + public AdapterStateListener(@NonNull IUwbAdapter adapter) { + mAdapter = adapter; + } + + /** + * Register an {@link AdapterStateCallback} with this {@link AdapterStateListener} + * + * @param executor an {@link Executor} to execute given callback + * @param callback user implementation of the {@link AdapterStateCallback} + */ + public void register(@NonNull Executor executor, @NonNull AdapterStateCallback callback) { + synchronized (this) { + if (mCallbackMap.containsKey(callback)) { + return; + } + + mCallbackMap.put(callback, executor); + + if (!mIsRegistered) { + try { + mAdapter.registerAdapterStateCallbacks(this); + mIsRegistered = true; + } catch (RemoteException e) { + Log.w(TAG, "Failed to register adapter state callback"); + executor.execute(() -> callback.onStateChanged(false, + AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN)); + } + } else { + sendCurrentState(callback); + } + } + } + + /** + * Unregister the specified {@link AdapterStateCallback} + * + * @param callback user implementation of the {@link AdapterStateCallback} + */ + public void unregister(@NonNull AdapterStateCallback callback) { + synchronized (this) { + if (!mCallbackMap.containsKey(callback)) { + return; + } + + mCallbackMap.remove(callback); + + if (mCallbackMap.isEmpty() && mIsRegistered) { + try { + mAdapter.unregisterAdapterStateCallbacks(this); + } catch (RemoteException e) { + Log.w(TAG, "Failed to unregister AdapterStateCallback with service"); + } + mIsRegistered = false; + } + } + } + + private void sendCurrentState(@NonNull AdapterStateCallback callback) { + synchronized (this) { + Executor executor = mCallbackMap.get(callback); + + final long identity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> callback.onStateChanged( + mAdapterEnabledState, mAdapterStateChangeReason)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + } + + @Override + public void onAdapterStateChanged(boolean isEnabled, int reason) { + synchronized (this) { + @StateChangedReason int localReason = + convertToStateChangedReason(reason); + mAdapterEnabledState = isEnabled; + mAdapterStateChangeReason = localReason; + for (AdapterStateCallback cb : mCallbackMap.keySet()) { + sendCurrentState(cb); + } + } + } + + private static @StateChangedReason int convertToStateChangedReason( + @StateChangeReason int reason) { + switch (reason) { + case StateChangeReason.ALL_SESSIONS_CLOSED: + return AdapterStateCallback.STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED; + + case StateChangeReason.SESSION_STARTED: + return AdapterStateCallback.STATE_CHANGED_REASON_SESSION_STARTED; + + case StateChangeReason.SYSTEM_POLICY: + return AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_POLICY; + + case StateChangeReason.SYSTEM_BOOT: + return AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_BOOT; + + case StateChangeReason.UNKNOWN: + default: + return AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN; + } + } +} diff --git a/core/java/android/uwb/IUwbAdapter.aidl b/core/java/android/uwb/IUwbAdapter.aidl index d29ed34804f1..2c8b2e462510 100644 --- a/core/java/android/uwb/IUwbAdapter.aidl +++ b/core/java/android/uwb/IUwbAdapter.aidl @@ -98,11 +98,18 @@ interface IUwbAdapter { int getMaxSimultaneousSessions(); /** - * Get the maximum number of remote devices per session + * Get the maximum number of remote devices per session when local device is initiator * * @return the maximum number of remote devices supported in a single session */ - int getMaxRemoteDevicesPerSession(); + int getMaxRemoteDevicesPerInitiatorSession(); + + /** + * Get the maximum number of remote devices per session when local device is responder + * + * @return the maximum number of remote devices supported in a single session + */ + int getMaxRemoteDevicesPerResponderSession(); /** * Provides the capabilities and features of the device diff --git a/core/java/android/uwb/StateChangeReason.aidl b/core/java/android/uwb/StateChangeReason.aidl index 46a6e2edfa22..28eaf9f2b24e 100644 --- a/core/java/android/uwb/StateChangeReason.aidl +++ b/core/java/android/uwb/StateChangeReason.aidl @@ -41,5 +41,10 @@ enum StateChangeReason { * The adapter state changed because of a device system change. */ SYSTEM_POLICY, + + /** + * Used to signal the first adapter state message after boot + */ + SYSTEM_BOOT, } diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java index 6488490b8863..ed5cf3625525 100644 --- a/core/java/android/uwb/UwbManager.java +++ b/core/java/android/uwb/UwbManager.java @@ -16,6 +16,7 @@ package android.uwb; +import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SuppressLint; @@ -23,10 +24,13 @@ import android.annotation.SystemService; import android.content.Context; import android.os.IBinder; import android.os.PersistableBundle; +import android.os.RemoteException; import android.os.ServiceManager; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.Executor; @@ -45,6 +49,8 @@ public final class UwbManager { private IUwbAdapter mUwbAdapter; private static final String SERVICE_NAME = "uwb"; + private AdapterStateListener mAdapterStateListener; + /** * Interface for receiving UWB adapter state changes */ @@ -109,6 +115,7 @@ public final class UwbManager { */ private UwbManager(IUwbAdapter adapter) { mUwbAdapter = adapter; + mAdapterStateListener = new AdapterStateListener(adapter); } /** @@ -140,8 +147,9 @@ public final class UwbManager { * @param executor an {@link Executor} to execute given callback * @param callback user implementation of the {@link AdapterStateCallback} */ - public void registerAdapterStateCallback(Executor executor, AdapterStateCallback callback) { - throw new UnsupportedOperationException(); + public void registerAdapterStateCallback(@NonNull @CallbackExecutor Executor executor, + @NonNull AdapterStateCallback callback) { + mAdapterStateListener.register(executor, callback); } /** @@ -153,8 +161,8 @@ public final class UwbManager { * * @param callback user implementation of the {@link AdapterStateCallback} */ - public void unregisterAdapterStateCallback(AdapterStateCallback callback) { - throw new UnsupportedOperationException(); + public void unregisterAdapterStateCallback(@NonNull AdapterStateCallback callback) { + mAdapterStateListener.unregister(callback); } /** @@ -167,7 +175,11 @@ public final class UwbManager { */ @NonNull public PersistableBundle getSpecificationInfo() { - throw new UnsupportedOperationException(); + try { + return mUwbAdapter.getSpecificationInfo(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -176,7 +188,11 @@ public final class UwbManager { * @return true if ranging is supported */ public boolean isRangingSupported() { - throw new UnsupportedOperationException(); + try { + return mUwbAdapter.isRangingSupported(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } @Retention(RetentionPolicy.SOURCE) @@ -225,7 +241,24 @@ public final class UwbManager { */ @AngleOfArrivalSupportType public int getAngleOfArrivalSupport() { - throw new UnsupportedOperationException(); + try { + switch (mUwbAdapter.getAngleOfArrivalSupport()) { + case AngleOfArrivalSupport.TWO_DIMENSIONAL: + return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D; + + case AngleOfArrivalSupport.THREE_DIMENSIONAL_HEMISPHERICAL: + return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL; + + case AngleOfArrivalSupport.THREE_DIMENSIONAL_SPHERICAL: + return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL; + + case AngleOfArrivalSupport.NONE: + default: + return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE; + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -239,7 +272,15 @@ public final class UwbManager { */ @NonNull public List<Integer> getSupportedChannelNumbers() { - throw new UnsupportedOperationException(); + List<Integer> channels = new ArrayList<>(); + try { + for (int channel : mUwbAdapter.getSupportedChannels()) { + channels.add(channel); + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return channels; } /** @@ -250,7 +291,15 @@ public final class UwbManager { */ @NonNull public Set<Integer> getSupportedPreambleCodeIndices() { - throw new UnsupportedOperationException(); + Set<Integer> preambles = new HashSet<>(); + try { + for (int preamble : mUwbAdapter.getSupportedPreambleCodes()) { + preambles.add(preamble); + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return preambles; } /** @@ -262,7 +311,11 @@ public final class UwbManager { */ @SuppressLint("MethodNameUnits") public long elapsedRealtimeResolutionNanos() { - throw new UnsupportedOperationException(); + try { + return mUwbAdapter.getTimestampResolutionNanos(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -271,7 +324,11 @@ public final class UwbManager { * @return the maximum allowed number of simultaneously open {@link RangingSession} instances. */ public int getMaxSimultaneousSessions() { - throw new UnsupportedOperationException(); + try { + return mUwbAdapter.getMaxSimultaneousSessions(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -281,7 +338,11 @@ public final class UwbManager { * @return the maximum number of remote devices per {@link RangingSession} */ public int getMaxRemoteDevicesPerInitiatorSession() { - throw new UnsupportedOperationException(); + try { + return mUwbAdapter.getMaxRemoteDevicesPerInitiatorSession(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -291,7 +352,11 @@ public final class UwbManager { * @return the maximum number of remote devices per {@link RangingSession} */ public int getMaxRemoteDevicesPerResponderSession() { - throw new UnsupportedOperationException(); + try { + return mUwbAdapter.getMaxRemoteDevicesPerResponderSession(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** diff --git a/core/java/android/view/IPinnedStackController.aidl b/core/java/android/view/IPinnedStackController.aidl deleted file mode 100644 index 1cf83a3f1e79..000000000000 --- a/core/java/android/view/IPinnedStackController.aidl +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) 2016, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.view; - -import android.graphics.Rect; - -/** - * An interface to the PinnedStackController to update it of state changes, and to query - * information based on the current state. - * - * @hide - */ -interface IPinnedStackController { - /** - * @return what WM considers to be the current device rotation. - */ - int getDisplayRotation(); -} diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedStackListener.aidl index f49fee36ca7e..84dd8af5e342 100644 --- a/core/java/android/view/IPinnedStackListener.aidl +++ b/core/java/android/view/IPinnedStackListener.aidl @@ -21,7 +21,6 @@ import android.content.ComponentName; import android.content.pm.ParceledListSlice; import android.graphics.Rect; import android.view.DisplayInfo; -import android.view.IPinnedStackController; /** * Listener for changes to the pinned stack made by the WindowManager. @@ -31,12 +30,6 @@ import android.view.IPinnedStackController; oneway interface IPinnedStackListener { /** - * Called when the listener is registered and provides an interface to call back to the pinned - * stack controller to update the controller of the pinned stack state. - */ - void onListenerRegistered(IPinnedStackController controller); - - /** * Called when the window manager has detected a change that would cause the movement bounds * to be changed (ie. after configuration change, aspect ratio change, etc). */ diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index a23b7e193024..025a977d5420 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -780,4 +780,25 @@ interface IWindowManager * verified. */ boolean verifyImpressionToken(in ImpressionToken impressionToken); + + /** + * Registers a listener for a {@link android.app.WindowContext} to handle configuration changes + * from the server side. + * + * @param clientToken the window context's token + * @param type Window type of the window context + * @param displayId The display associated with the window context + * @param options A bundle used to pass window-related options and choose the right DisplayArea + * + * @return {@code true} if the listener was registered successfully. + */ + boolean registerWindowContextListener(IBinder clientToken, int type, int displayId, + in Bundle options); + + /** + * Unregisters a listener which registered with {@link #registerWindowContextListener()}. + * + * @param clientToken the window context's token + */ + void unregisterWindowContextListener(IBinder clientToken); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 349b84899f9d..2d633cbeb353 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -119,6 +119,7 @@ import android.graphics.Rect; import android.graphics.Region; import android.graphics.RenderNode; import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; import android.hardware.input.InputManager; @@ -4414,6 +4415,14 @@ public final class ViewRootImpl implements ViewParent, mView.mContext.getDrawable(value.resourceId); } } + // Sets the focus appearance data into the accessibility focus drawable. + if (mAttachInfo.mAccessibilityFocusDrawable instanceof GradientDrawable) { + final GradientDrawable drawable = + (GradientDrawable) mAttachInfo.mAccessibilityFocusDrawable; + drawable.setStroke(mAccessibilityManager.getAccessibilityFocusStrokeWidth(), + mAccessibilityManager.getAccessibilityFocusColor()); + } + return mAttachInfo.mAccessibilityFocusDrawable; } diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index a8534faf43e5..56dcd5951e5e 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -19,9 +19,11 @@ package android.view.accessibility; import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME; import android.Manifest; +import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.AccessibilityServiceInfo.FeedbackType; import android.accessibilityservice.AccessibilityShortcutInfo; +import android.annotation.ColorInt; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -59,6 +61,7 @@ import android.view.IWindow; import android.view.View; import android.view.accessibility.AccessibilityEvent.EventType; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IntPair; @@ -233,6 +236,11 @@ public final class AccessibilityManager { private int mPerformingAction = 0; + /** The stroke width of the focus rectangle in pixels */ + private int mFocusStrokeWidth; + /** The color of the focus rectangle */ + private int mFocusColor; + @UnsupportedAppUsage private final ArrayMap<AccessibilityStateChangeListener, Handler> mAccessibilityStateChangeListeners = new ArrayMap<>(); @@ -410,6 +418,13 @@ public final class AccessibilityManager { public void setRelevantEventTypes(int eventTypes) { mRelevantEventTypes = eventTypes; } + + @Override + public void setFocusAppearance(int strokeWidth, int color) { + synchronized (mLock) { + updateFocusAppearanceLocked(strokeWidth, color); + } + } }; /** @@ -457,6 +472,7 @@ public final class AccessibilityManager { mHandler = new Handler(context.getMainLooper(), mCallback); mUserId = userId; synchronized (mLock) { + initialFocusAppearanceLocked(context.getResources()); tryConnectToServiceLocked(service); } } @@ -464,18 +480,26 @@ public final class AccessibilityManager { /** * Create an instance. * + * @param context A {@link Context}. * @param handler The handler to use * @param service An interface to the backing service. * @param userId User id under which to run. + * @param serviceConnect {@code true} to connect the service or + * {@code false} not to connect the service. * * @hide */ - public AccessibilityManager(Handler handler, IAccessibilityManager service, int userId) { + @VisibleForTesting + public AccessibilityManager(Context context, Handler handler, IAccessibilityManager service, + int userId, boolean serviceConnect) { mCallback = new MyCallback(); mHandler = handler; mUserId = userId; synchronized (mLock) { - tryConnectToServiceLocked(service); + initialFocusAppearanceLocked(context.getResources()); + if (serviceConnect) { + tryConnectToServiceLocked(service); + } } } @@ -954,6 +978,32 @@ public final class AccessibilityManager { } /** + * Gets the strokeWidth of the focus rectangle. This value can be set by + * {@link AccessibilityService}. + * + * @return The strokeWidth of the focus rectangle in pixels. + * + */ + public int getAccessibilityFocusStrokeWidth() { + synchronized (mLock) { + return mFocusStrokeWidth; + } + } + + /** + * Gets the color of the focus rectangle. This value can be set by + * {@link AccessibilityService}. + * + * @return The color of the focus rectangle. + * + */ + public @ColorInt int getAccessibilityFocusColor() { + synchronized (mLock) { + return mFocusColor; + } + } + + /** * Get the preparers that are registered for an accessibility ID * * @param id The ID of interest @@ -1551,6 +1601,7 @@ public final class AccessibilityManager { setStateLocked(IntPair.first(userStateAndRelevantEvents)); mRelevantEventTypes = IntPair.second(userStateAndRelevantEvents); updateUiTimeout(service.getRecommendedTimeoutMillis()); + updateFocusAppearanceLocked(service.getFocusStrokeWidth(), service.getFocusColor()); mService = service; } catch (RemoteException re) { Log.e(LOG_TAG, "AccessibilityManagerService is dead", re); @@ -1635,6 +1686,40 @@ public final class AccessibilityManager { } /** + * Updates the stroke width and color of the focus rectangle. + * + * @param strokeWidth The strokeWidth of the focus rectangle. + * @param color The color of the focus rectangle. + */ + private void updateFocusAppearanceLocked(int strokeWidth, int color) { + if (mFocusStrokeWidth == strokeWidth && mFocusColor == color) { + return; + } + mFocusStrokeWidth = strokeWidth; + mFocusColor = color; + } + + /** + * Sets the stroke width and color of the focus rectangle to default value. + * + * @param resource The resources. + */ + private void initialFocusAppearanceLocked(Resources resource) { + try { + mFocusStrokeWidth = resource.getDimensionPixelSize( + R.dimen.accessibility_focus_highlight_stroke_width); + mFocusColor = resource.getColor(R.color.accessibility_focus_highlight_color); + } catch (Resources.NotFoundException re) { + // Sets the stroke width and color to default value by hardcoded for making + // the Talkback can work normally. + mFocusStrokeWidth = (int) (4 * resource.getDisplayMetrics().density); + mFocusColor = 0xbf39b500; + Log.e(LOG_TAG, "Error while initialing the focus appearance data then setting to" + + " default value by hardcoded", re); + } + } + + /** * Determines if the accessibility button within the system navigation area is supported. * * @return {@code true} if the accessibility button is supported on this device, diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl index 5d3c72015ee3..c71ea53c414d 100644 --- a/core/java/android/view/accessibility/IAccessibilityManager.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -92,4 +92,8 @@ interface IAccessibilityManager { void associateEmbeddedHierarchy(IBinder host, IBinder embedded); void disassociateEmbeddedHierarchy(IBinder token); + + int getFocusStrokeWidth(); + + int getFocusColor(); } diff --git a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl index 94b9ad1c3279..041399ccb8ec 100644 --- a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl @@ -29,4 +29,6 @@ oneway interface IAccessibilityManagerClient { void notifyServicesStateChanged(long updatedUiTimeout); void setRelevantEventTypes(int eventTypes); + + void setFocusAppearance(int strokeWidth, int color); } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index dcd74fd1ad3e..fb6eb97253e3 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -90,6 +90,8 @@ import android.util.SparseArray; import android.util.SparseIntArray; import android.util.SparseLongArray; import android.util.TimeUtils; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import android.view.Display; @@ -112,7 +114,6 @@ import libcore.util.EmptyArray; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.ByteArrayOutputStream; import java.io.File; @@ -10830,8 +10831,7 @@ public class BatteryStatsImpl extends BatteryStats { } final ByteArrayOutputStream memStream = new ByteArrayOutputStream(); try { - XmlSerializer out = new FastXmlSerializer(); - out.setOutput(memStream, StandardCharsets.UTF_8.name()); + TypedXmlSerializer out = Xml.resolveSerializer(memStream); writeDailyItemsLocked(out); final long initialTimeMs = SystemClock.uptimeMillis() - startTimeMs; BackgroundThread.getHandler().post(new Runnable() { @@ -10861,15 +10861,15 @@ public class BatteryStatsImpl extends BatteryStats { } } - private void writeDailyItemsLocked(XmlSerializer out) throws IOException { + private void writeDailyItemsLocked(TypedXmlSerializer out) throws IOException { StringBuilder sb = new StringBuilder(64); out.startDocument(null, true); out.startTag(null, "daily-items"); for (int i=0; i<mDailyItems.size(); i++) { final DailyItem dit = mDailyItems.get(i); out.startTag(null, "item"); - out.attribute(null, "start", Long.toString(dit.mStartTime)); - out.attribute(null, "end", Long.toString(dit.mEndTime)); + out.attributeLong(null, "start", dit.mStartTime); + out.attributeLong(null, "end", dit.mEndTime); writeDailyLevelSteps(out, "dis", dit.mDischargeSteps, sb); writeDailyLevelSteps(out, "chg", dit.mChargeSteps, sb); if (dit.mPackageChanges != null) { @@ -10878,7 +10878,7 @@ public class BatteryStatsImpl extends BatteryStats { if (pc.mUpdate) { out.startTag(null, "upd"); out.attribute(null, "pkg", pc.mPackageName); - out.attribute(null, "ver", Long.toString(pc.mVersionCode)); + out.attributeLong(null, "ver", pc.mVersionCode); out.endTag(null, "upd"); } else { out.startTag(null, "rem"); @@ -10893,11 +10893,11 @@ public class BatteryStatsImpl extends BatteryStats { out.endDocument(); } - private void writeDailyLevelSteps(XmlSerializer out, String tag, LevelStepTracker steps, + private void writeDailyLevelSteps(TypedXmlSerializer out, String tag, LevelStepTracker steps, StringBuilder tmpBuilder) throws IOException { if (steps != null) { out.startTag(null, tag); - out.attribute(null, "n", Integer.toString(steps.mNumStepDurations)); + out.attributeInt(null, "n", steps.mNumStepDurations); for (int i=0; i<steps.mNumStepDurations; i++) { out.startTag(null, "s"); tmpBuilder.setLength(0); @@ -10919,10 +10919,9 @@ public class BatteryStatsImpl extends BatteryStats { return; } try { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(stream, StandardCharsets.UTF_8.name()); + TypedXmlPullParser parser = Xml.resolvePullParser(stream); readDailyItemsLocked(parser); - } catch (XmlPullParserException e) { + } catch (IOException e) { } finally { try { stream.close(); @@ -10931,7 +10930,7 @@ public class BatteryStatsImpl extends BatteryStats { } } - private void readDailyItemsLocked(XmlPullParser parser) { + private void readDailyItemsLocked(TypedXmlPullParser parser) { try { int type; while ((type = parser.next()) != XmlPullParser.START_TAG @@ -10975,17 +10974,11 @@ public class BatteryStatsImpl extends BatteryStats { } } - void readDailyItemTagLocked(XmlPullParser parser) throws NumberFormatException, + void readDailyItemTagLocked(TypedXmlPullParser parser) throws NumberFormatException, XmlPullParserException, IOException { DailyItem dit = new DailyItem(); - String attr = parser.getAttributeValue(null, "start"); - if (attr != null) { - dit.mStartTime = Long.parseLong(attr); - } - attr = parser.getAttributeValue(null, "end"); - if (attr != null) { - dit.mEndTime = Long.parseLong(attr); - } + dit.mStartTime = parser.getAttributeLong(null, "start", 0); + dit.mEndTime = parser.getAttributeLong(null, "end", 0); int outerDepth = parser.getDepth(); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT @@ -11006,8 +10999,7 @@ public class BatteryStatsImpl extends BatteryStats { PackageChange pc = new PackageChange(); pc.mUpdate = true; pc.mPackageName = parser.getAttributeValue(null, "pkg"); - String verStr = parser.getAttributeValue(null, "ver"); - pc.mVersionCode = verStr != null ? Long.parseLong(verStr) : 0; + pc.mVersionCode = parser.getAttributeLong(null, "ver", 0); dit.mPackageChanges.add(pc); XmlUtils.skipCurrentTag(parser); } else if (tagName.equals("rem")) { @@ -11028,16 +11020,15 @@ public class BatteryStatsImpl extends BatteryStats { mDailyItems.add(dit); } - void readDailyItemTagDetailsLocked(XmlPullParser parser, DailyItem dit, boolean isCharge, + void readDailyItemTagDetailsLocked(TypedXmlPullParser parser, DailyItem dit, boolean isCharge, String tag) throws NumberFormatException, XmlPullParserException, IOException { - final String numAttr = parser.getAttributeValue(null, "n"); - if (numAttr == null) { + final int num = parser.getAttributeInt(null, "n", -1); + if (num == -1) { Slog.w(TAG, "Missing 'n' attribute at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); return; } - final int num = Integer.parseInt(numAttr); LevelStepTracker steps = new LevelStepTracker(num); if (isCharge) { dit.mChargeSteps = steps; diff --git a/core/java/com/android/internal/util/BinaryXmlSerializer.java b/core/java/com/android/internal/util/BinaryXmlSerializer.java index d3fcf71ba399..bc3b1f8bef57 100644 --- a/core/java/com/android/internal/util/BinaryXmlSerializer.java +++ b/core/java/com/android/internal/util/BinaryXmlSerializer.java @@ -69,7 +69,7 @@ public final class BinaryXmlSerializer implements TypedXmlSerializer { * {@code ABX_}, representing "Android Binary XML." The final byte is a * version number which may be incremented as the protocol changes. */ - static final byte[] PROTOCOL_MAGIC_VERSION_0 = new byte[] { 0x41, 0x42, 0x58, 0x00 }; + public static final byte[] PROTOCOL_MAGIC_VERSION_0 = new byte[] { 0x41, 0x42, 0x58, 0x00 }; /** * Internal token which represents an attribute associated with the most diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java index 8eb0e280350f..244efc596926 100644 --- a/core/java/com/android/internal/util/XmlUtils.java +++ b/core/java/com/android/internal/util/XmlUtils.java @@ -53,6 +53,7 @@ import java.util.Set; public class XmlUtils { private static final String STRING_ARRAY_SEPARATOR = ":"; + @SuppressWarnings("AndroidFrameworkEfficientXml") private static class ForcedTypedXmlSerializer extends XmlSerializerWrapper implements TypedXmlSerializer { public ForcedTypedXmlSerializer(XmlSerializer wrapped) { @@ -133,6 +134,7 @@ public class XmlUtils { } } + @SuppressWarnings("AndroidFrameworkEfficientXml") private static class ForcedTypedXmlPullParser extends XmlPullParserWrapper implements TypedXmlPullParser { public ForcedTypedXmlPullParser(XmlPullParser wrapped) { @@ -1715,6 +1717,7 @@ public class XmlUtils { } } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static int readIntAttribute(XmlPullParser in, String name, int defaultValue) { if (in instanceof TypedXmlPullParser) { return ((TypedXmlPullParser) in).getAttributeInt(null, name, defaultValue); @@ -1730,6 +1733,7 @@ public class XmlUtils { } } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static int readIntAttribute(XmlPullParser in, String name) throws IOException { if (in instanceof TypedXmlPullParser) { try { @@ -1746,6 +1750,7 @@ public class XmlUtils { } } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static void writeIntAttribute(XmlSerializer out, String name, int value) throws IOException { if (out instanceof TypedXmlSerializer) { @@ -1755,6 +1760,7 @@ public class XmlUtils { out.attribute(null, name, Integer.toString(value)); } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static long readLongAttribute(XmlPullParser in, String name, long defaultValue) { if (in instanceof TypedXmlPullParser) { return ((TypedXmlPullParser) in).getAttributeLong(null, name, defaultValue); @@ -1770,6 +1776,7 @@ public class XmlUtils { } } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static long readLongAttribute(XmlPullParser in, String name) throws IOException { if (in instanceof TypedXmlPullParser) { try { @@ -1786,6 +1793,7 @@ public class XmlUtils { } } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static void writeLongAttribute(XmlSerializer out, String name, long value) throws IOException { if (out instanceof TypedXmlSerializer) { @@ -1795,6 +1803,7 @@ public class XmlUtils { out.attribute(null, name, Long.toString(value)); } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static float readFloatAttribute(XmlPullParser in, String name) throws IOException { if (in instanceof TypedXmlPullParser) { try { @@ -1811,6 +1820,7 @@ public class XmlUtils { } } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static void writeFloatAttribute(XmlSerializer out, String name, float value) throws IOException { if (out instanceof TypedXmlSerializer) { @@ -1820,10 +1830,12 @@ public class XmlUtils { out.attribute(null, name, Float.toString(value)); } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static boolean readBooleanAttribute(XmlPullParser in, String name) { return readBooleanAttribute(in, name, false); } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static boolean readBooleanAttribute(XmlPullParser in, String name, boolean defaultValue) { if (in instanceof TypedXmlPullParser) { @@ -1837,6 +1849,7 @@ public class XmlUtils { } } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static void writeBooleanAttribute(XmlSerializer out, String name, boolean value) throws IOException { if (out instanceof TypedXmlSerializer) { @@ -1869,6 +1882,7 @@ public class XmlUtils { } } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static byte[] readByteArrayAttribute(XmlPullParser in, String name) { if (in instanceof TypedXmlPullParser) { try { @@ -1885,6 +1899,7 @@ public class XmlUtils { } } + @SuppressWarnings("AndroidFrameworkEfficientXml") public static void writeByteArrayAttribute(XmlSerializer out, String name, byte[] value) throws IOException { if (value != null) { diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java index ac2361dff560..b4d8e506c61a 100644 --- a/core/java/com/android/server/BootReceiver.java +++ b/core/java/com/android/server/BootReceiver.java @@ -35,24 +35,23 @@ import android.text.TextUtils; import android.util.AtomicFile; import android.util.EventLog; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Iterator; import java.util.regex.Matcher; @@ -712,8 +711,7 @@ public class BootReceiver extends BroadcastReceiver { HashMap<String, Long> timestamps = new HashMap<String, Long>(); boolean success = false; try (final FileInputStream stream = sFile.openRead()) { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(stream, StandardCharsets.UTF_8.name()); + TypedXmlPullParser parser = Xml.resolvePullParser(stream); int type; while ((type = parser.next()) != XmlPullParser.START_TAG @@ -735,8 +733,7 @@ public class BootReceiver extends BroadcastReceiver { String tagName = parser.getName(); if (tagName.equals("log")) { final String filename = parser.getAttributeValue(null, "filename"); - final long timestamp = Long.valueOf(parser.getAttributeValue( - null, "timestamp")); + final long timestamp = parser.getAttributeLong(null, "timestamp"); timestamps.put(filename, timestamp); } else { Slog.w(TAG, "Unknown tag: " + parser.getName()); @@ -775,8 +772,7 @@ public class BootReceiver extends BroadcastReceiver { } try { - XmlSerializer out = new FastXmlSerializer(); - out.setOutput(stream, StandardCharsets.UTF_8.name()); + TypedXmlSerializer out = Xml.resolveSerializer(stream); out.startDocument(null, true); out.startTag(null, "log-files"); @@ -785,7 +781,7 @@ public class BootReceiver extends BroadcastReceiver { String filename = itor.next(); out.startTag(null, "log"); out.attribute(null, "filename", filename); - out.attribute(null, "timestamp", timestamps.get(filename).toString()); + out.attributeLong(null, "timestamp", timestamps.get(filename)); out.endTag(null, "log"); } diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 5a859aabce7b..bf79a4472fa5 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -571,6 +571,9 @@ enum { MEMINFO_PAGE_TABLES, MEMINFO_KERNEL_STACK, MEMINFO_KERNEL_RECLAIMABLE, + MEMINFO_ACTIVE, + MEMINFO_INACTIVE, + MEMINFO_UNEVICTABLE, MEMINFO_COUNT }; diff --git a/core/res/res/drawable/view_accessibility_focused.xml b/core/res/res/drawable/view_accessibility_focused.xml index 025916b35f40..aa3031e5cf26 100644 --- a/core/res/res/drawable/view_accessibility_focused.xml +++ b/core/res/res/drawable/view_accessibility_focused.xml @@ -17,8 +17,8 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android" > <stroke - android:width="4dp" - android:color="@color/accessibility_focus_highlight" /> + android:width="@dimen/accessibility_focus_highlight_stroke_width" + android:color="@color/accessibility_focus_highlight_color" /> <corners android:radius="2dp" /> diff --git a/core/res/res/layout/notification_top_line_views.xml b/core/res/res/layout/notification_top_line_views.xml index c71e8863502c..60507eddba22 100644 --- a/core/res/res/layout/notification_top_line_views.xml +++ b/core/res/res/layout/notification_top_line_views.xml @@ -88,7 +88,7 @@ <DateTimeView android:id="@+id/time" - android:textAppearance="@style/TextAppearance.Material.Notification.Time" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="@dimen/notification_header_separating_margin" diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 0079d8cd0276..110e77a8aa05 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -159,7 +159,7 @@ <color name="keyguard_avatar_nick_color">#ffffffff</color> <color name="keyguard_avatar_frame_pressed_color">#ff35b5e5</color> - <color name="accessibility_focus_highlight">#bf39b500</color> + <color name="accessibility_focus_highlight_color">#bf39b500</color> <color name="autofilled_highlight">#4dffeb3b</color> <color name="system_notification_accent_color">#ff607D8B</color> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 84e6f12c0b51..40e11cb92d3e 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1613,21 +1613,28 @@ <!-- Whether the geolocation time zone detection feature is enabled. --> <bool name="config_enableGeolocationTimeZoneDetection" translatable="false">true</bool> - <!-- Whether to enable primary location time zone provider overlay which allows the primary - location time zone provider to be replaced by an app at run-time. When disabled, only the + <!-- Whether the primary LocationTimeZoneProvider is enabled device. + Ignored if config_enableGeolocationTimeZoneDetection is false --> + <bool name="config_enablePrimaryLocationTimeZoneProvider" translatable="false">false</bool> + <!-- Used when enablePrimaryLocationTimeZoneProvider is true. Controls whether to enable primary + location time zone provider overlay which allows the primary location time zone provider to + be replaced by an app at run-time. When disabled, only the config_primaryLocationTimeZoneProviderPackageName package will be searched for the primary location time zone provider, otherwise any system package is eligible. Anyone who wants to - disable the overlay mechanism can set it to false. --> + disable the runtime overlay mechanism can set it to false. --> <bool name="config_enablePrimaryLocationTimeZoneOverlay" translatable="false">false</bool> <!-- Package name providing the primary location time zone provider. Used only when config_enablePrimaryLocationTimeZoneOverlay is false. --> <string name="config_primaryLocationTimeZoneProviderPackageName" translatable="false">@null</string> - <!-- Whether to enable secondary location time zone provider overlay which allows the secondary - location time zone provider to be replaced by an app at run-time. When disabled, only the - config_secondaryLocationTimeZoneProviderPackageName package will be searched for the - secondary location time zone provider, otherwise any system package is eligible. Anyone who - wants to disable the overlay mechanism can set it to false. --> + <!-- Whether the secondary LocationTimeZoneProvider is enabled device. + Ignored if config_enableGeolocationTimeZoneDetection is false --> + <bool name="config_enableSecondaryLocationTimeZoneProvider" translatable="false">true</bool> + <!-- Used when enableSecondaryLocationTimeZoneProvider is true. Controls whether to enable + secondary location time zone provider overlay which allows the primary location time zone + provider to config_secondaryLocationTimeZoneProviderPackageName package will be searched + for the secondary location time zone provider, otherwise any system package is eligible. + Anyone who wants to disable the runtime overlay mechanism can set it to false. --> <bool name="config_enableSecondaryLocationTimeZoneOverlay" translatable="false">false</bool> <!-- Package name providing the secondary location time zone provider. Used only when config_enableSecondaryLocationTimeZoneOverlay is false. @@ -4515,7 +4522,7 @@ <integer name="config_pdp_reject_retry_delay_ms">-1</integer> <!-- Whether or not to enable the binder heavy hitter watcher by default --> - <bool name="config_defaultBinderHeavyHitterWatcherEnabled">true</bool> + <bool name="config_defaultBinderHeavyHitterWatcherEnabled">false</bool> <!-- The default batch size for the binder heavy hitter watcher --> <integer name="config_defaultBinderHeavyHitterWatcherBatchSize">2000</integer> @@ -4526,7 +4533,7 @@ </item> <!-- Whether or not to enable the binder heavy hitter auto sampler by default --> - <bool name="config_defaultBinderHeavyHitterAutoSamplerEnabled">true</bool> + <bool name="config_defaultBinderHeavyHitterAutoSamplerEnabled">false</bool> <!-- The default batch size for the binder heavy hitter auto sampler --> <integer name="config_defaultBinderHeavyHitterAutoSamplerBatchSize">400</integer> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 4bcabff109ea..05db741643e8 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -553,6 +553,9 @@ <!-- Width of the outline stroke used by the accessibility screen magnification indicator --> <dimen name="accessibility_magnification_indicator_width">4dip</dimen> + <!-- Width of the outline stroke used by the accessibility focus rectangle --> + <dimen name="accessibility_focus_highlight_stroke_width">4dp</dimen> + <!-- Margin around the various security views --> <dimen name="keyguard_muliuser_selector_margin">8dp</dimen> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index cd00fbf204cf..e50eee6afa91 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2168,8 +2168,10 @@ <java-symbol type="string" name="config_deviceConfiguratorPackageName" /> <java-symbol type="array" name="config_autoTimeSourcesPriority" /> <java-symbol type="bool" name="config_enableGeolocationTimeZoneDetection" /> + <java-symbol type="bool" name="config_enablePrimaryLocationTimeZoneProvider" /> <java-symbol type="bool" name="config_enablePrimaryLocationTimeZoneOverlay" /> <java-symbol type="string" name="config_primaryLocationTimeZoneProviderPackageName" /> + <java-symbol type="bool" name="config_enableSecondaryLocationTimeZoneProvider" /> <java-symbol type="bool" name="config_enableSecondaryLocationTimeZoneOverlay" /> <java-symbol type="string" name="config_secondaryLocationTimeZoneProviderPackageName" /> @@ -4115,4 +4117,9 @@ <!-- Component with platform query permissions for AppSearch --> <java-symbol type="string" name="config_defaultAppSearchPlatformQuerierComponent" /> + + <!-- Color used by the accessibility focus rectangle --> + <java-symbol type="color" name="accessibility_focus_highlight_color" /> + <!-- Width of the outline stroke used by the accessibility focus rectangle --> + <java-symbol type="dimen" name="accessibility_focus_highlight_stroke_width" /> </resources> diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java index 365e97ded928..e04d9034d2c1 100644 --- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java +++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java @@ -24,12 +24,12 @@ import android.os.UserHandle; import android.test.AndroidTestCase; import android.util.AttributeSet; import android.util.SparseArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import androidx.test.filters.LargeTest; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.ByteArrayInputStream; import java.io.File; @@ -308,12 +308,12 @@ public class RegisteredServicesCacheTest extends AndroidTestCase { static class TestSerializer implements XmlSerializerAndParser<TestServiceType> { - public void writeAsXml(TestServiceType item, XmlSerializer out) throws IOException { + public void writeAsXml(TestServiceType item, TypedXmlSerializer out) throws IOException { out.attribute(null, "type", item.type); out.attribute(null, "value", item.value); } - public TestServiceType createFromXml(XmlPullParser parser) + public TestServiceType createFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final String type = parser.getAttributeValue(null, "type"); final String value = parser.getAttributeValue(null, "value"); diff --git a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java index 4370462279b2..e750454a01ff 100644 --- a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java +++ b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java @@ -23,18 +23,16 @@ import static org.junit.Assert.fail; import android.os.Parcel; import android.util.Pair; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.util.FastXmlSerializer; - import org.junit.Test; import org.junit.runner.RunWith; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -163,7 +161,7 @@ public class BrightnessConfigurationTest { BrightnessConfiguration config = builder.build(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); - XmlSerializer out = new FastXmlSerializer(); + TypedXmlSerializer out = Xml.newFastSerializer(); out.setOutput(baos, StandardCharsets.UTF_8.name()); out.startDocument(null, true); config.saveToXml(out); @@ -171,7 +169,7 @@ public class BrightnessConfigurationTest { baos.flush(); ByteArrayInputStream input = new ByteArrayInputStream(baos.toByteArray()); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(input, StandardCharsets.UTF_8.name()); BrightnessConfiguration loadedConfig = BrightnessConfiguration.loadFromXml(parser); diff --git a/core/tests/coretests/src/android/util/BinaryXmlTest.java b/core/tests/coretests/src/android/util/BinaryXmlTest.java index be63a0ecc65f..6fa5a2343361 100644 --- a/core/tests/coretests/src/android/util/BinaryXmlTest.java +++ b/core/tests/coretests/src/android/util/BinaryXmlTest.java @@ -20,6 +20,8 @@ import static android.util.XmlTest.assertNext; import static android.util.XmlTest.buildPersistableBundle; import static android.util.XmlTest.doPersistableBundleRead; import static android.util.XmlTest.doPersistableBundleWrite; +import static android.util.XmlTest.doVerifyRead; +import static android.util.XmlTest.doVerifyWrite; import static org.junit.Assert.assertEquals; import static org.xmlpull.v1.XmlPullParser.START_TAG; @@ -33,6 +35,11 @@ import org.junit.runner.RunWith; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; import java.nio.charset.StandardCharsets; @RunWith(AndroidJUnit4.class) @@ -96,4 +103,58 @@ public class BinaryXmlTest { final PersistableBundle actual = doPersistableBundleRead(secondIn, os.toByteArray()); assertEquals(expected.toString(), actual.toString()); } + + @Test + public void testResolve_File() throws Exception { + { + final File file = File.createTempFile("fast", ".xml"); + try (OutputStream os = new FileOutputStream(file)) { + TypedXmlSerializer xml = Xml.newFastSerializer(); + xml.setOutput(os, StandardCharsets.UTF_8.name()); + doVerifyWrite(xml); + } + try (InputStream is = new FileInputStream(file)) { + doVerifyRead(Xml.resolvePullParser(is)); + } + } + { + final File file = File.createTempFile("binary", ".xml"); + try (OutputStream os = new FileOutputStream(file)) { + TypedXmlSerializer xml = Xml.newBinarySerializer(); + xml.setOutput(os, StandardCharsets.UTF_8.name()); + doVerifyWrite(xml); + } + try (InputStream is = new FileInputStream(file)) { + doVerifyRead(Xml.resolvePullParser(is)); + } + } + } + + @Test + public void testResolve_Memory() throws Exception { + { + final byte[] data; + try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { + TypedXmlSerializer xml = Xml.newFastSerializer(); + xml.setOutput(os, StandardCharsets.UTF_8.name()); + doVerifyWrite(xml); + data = os.toByteArray(); + } + try (InputStream is = new ByteArrayInputStream(data)) { + doVerifyRead(Xml.resolvePullParser(is)); + } + } + { + final byte[] data; + try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { + TypedXmlSerializer xml = Xml.newBinarySerializer(); + xml.setOutput(os, StandardCharsets.UTF_8.name()); + doVerifyWrite(xml); + data = os.toByteArray(); + } + try (InputStream is = new ByteArrayInputStream(data)) { + doVerifyRead(Xml.resolvePullParser(is)); + } + } + } } diff --git a/core/tests/coretests/src/android/util/XmlTest.java b/core/tests/coretests/src/android/util/XmlTest.java index 5ae17c46944f..a8fc6f933605 100644 --- a/core/tests/coretests/src/android/util/XmlTest.java +++ b/core/tests/coretests/src/android/util/XmlTest.java @@ -205,7 +205,7 @@ public class XmlTest { private static final byte[] TEST_BYTES = new byte[] { 0, 1, 2, 3, 4, 3, 2, 1, 0 }; private static final byte[] TEST_BYTES_EMPTY = new byte[0]; - private static void doVerifyWrite(TypedXmlSerializer out) throws Exception { + static void doVerifyWrite(TypedXmlSerializer out) throws Exception { out.startDocument(StandardCharsets.UTF_8.name(), true); out.startTag(null, "one"); { @@ -244,7 +244,7 @@ public class XmlTest { out.endDocument(); } - private static void doVerifyRead(TypedXmlPullParser in) throws Exception { + static void doVerifyRead(TypedXmlPullParser in) throws Exception { assertEquals(START_DOCUMENT, in.getEventType()); assertNext(in, START_TAG, "one", 1); { diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java index 8e2490789a6f..75116d8c6df2 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java @@ -38,6 +38,7 @@ import android.os.UserHandle; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.R; import com.android.internal.util.IntPair; import com.android.server.accessibility.test.MessageCapturingHandler; @@ -73,12 +74,18 @@ public class AccessibilityManagerTest { @Mock private IAccessibilityManager mMockService; private MessageCapturingHandler mHandler; private Instrumentation mInstrumentation; + private int mFocusStrokeWidthDefaultValue; + private int mFocusColorDefaultValue; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mHandler = new MessageCapturingHandler(null); mInstrumentation = InstrumentationRegistry.getInstrumentation(); + mFocusStrokeWidthDefaultValue = mInstrumentation.getContext().getResources() + .getDimensionPixelSize(R.dimen.accessibility_focus_highlight_stroke_width); + mFocusColorDefaultValue = mInstrumentation.getContext().getResources().getColor( + R.color.accessibility_focus_highlight_color); } @After @@ -94,8 +101,12 @@ public class AccessibilityManagerTest { when(mMockService.addClient(any(IAccessibilityManagerClient.class), anyInt())) .thenReturn(serviceReturnValue); + when(mMockService.getFocusStrokeWidth()).thenReturn(mFocusStrokeWidthDefaultValue); + when(mMockService.getFocusColor()).thenReturn(mFocusColorDefaultValue); + AccessibilityManager manager = - new AccessibilityManager(mHandler, mMockService, UserHandle.USER_CURRENT); + new AccessibilityManager(mInstrumentation.getContext(), mHandler, mMockService, + UserHandle.USER_CURRENT, true); verify(mMockService).addClient(any(IAccessibilityManagerClient.class), anyInt()); mHandler.setCallback(manager.getCallback()); @@ -205,4 +216,16 @@ public class AccessibilityManagerTest { verify(mMockService).setWindowMagnificationConnection(connection); } + + @Test + public void testGetDefaultValueOfFocusAppearanceData() { + AccessibilityManager manager = + new AccessibilityManager(mInstrumentation.getContext(), mHandler, null, + UserHandle.USER_CURRENT, false); + + assertEquals(mFocusStrokeWidthDefaultValue, + manager.getAccessibilityFocusStrokeWidth()); + assertEquals(mFocusColorDefaultValue, + manager.getAccessibilityFocusColor()); + } } diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java index 75a7504cac2f..cfdb2b769a08 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java @@ -161,4 +161,6 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon public void setTouchExplorationPassthroughRegion(int displayId, Region region) {} public void setGestureDetectionPassthroughRegion(int displayId, Region region) {} + + public void setFocusAppearance(int strokeWidth, int color) {} } diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java index c17c36eba2dc..6baf3056be32 100644 --- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java +++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java @@ -68,6 +68,7 @@ import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IAccessibilityManager; import android.widget.Toast; +import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.internal.R; @@ -148,7 +149,8 @@ public class AccessibilityShortcutControllerTest { // Use the extra level of indirection in the object to mock framework objects AccessibilityManager accessibilityManager = - new AccessibilityManager(mHandler, mAccessibilityManagerService, 0); + new AccessibilityManager(InstrumentationRegistry.getContext(), mHandler, + mAccessibilityManagerService, 0, true); when(mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext)) .thenReturn(accessibilityManager); when(mContext.getSystemService(Context.ACCESSIBILITY_SERVICE)) diff --git a/core/tests/uwbtests/Android.bp b/core/tests/uwbtests/Android.bp index c41c346b131a..8ee86f470c9e 100644 --- a/core/tests/uwbtests/Android.bp +++ b/core/tests/uwbtests/Android.bp @@ -17,6 +17,7 @@ android_test { static_libs: [ "androidx.test.ext.junit", "androidx.test.rules", + "mockito-target-minus-junit4", ], libs: [ "android.test.runner", diff --git a/core/tests/uwbtests/src/android/uwb/AdapterStateListenerTest.java b/core/tests/uwbtests/src/android/uwb/AdapterStateListenerTest.java new file mode 100644 index 000000000000..ce67ef7868e8 --- /dev/null +++ b/core/tests/uwbtests/src/android/uwb/AdapterStateListenerTest.java @@ -0,0 +1,311 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.uwb; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + + +import android.os.RemoteException; +import android.uwb.UwbManager.AdapterStateCallback; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executor; + +/** + * Test of {@link AdapterStateListener}. + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class AdapterStateListenerTest { + + IUwbAdapter mUwbAdapter = mock(IUwbAdapter.class); + + Answer mRegisterSuccessAnswer = new Answer() { + public Object answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + IUwbAdapterStateCallbacks cb = (IUwbAdapterStateCallbacks) args[0]; + try { + cb.onAdapterStateChanged(false, StateChangeReason.UNKNOWN); + } catch (RemoteException e) { + // Nothing to do + } + return new Object(); + } + }; + + Throwable mThrowRemoteException = new RemoteException("RemoteException"); + + private static Executor getExecutor() { + return new Executor() { + @Override + public void execute(Runnable command) { + command.run(); + } + }; + } + + private static void verifyCallbackStateChangedInvoked( + AdapterStateCallback callback, int numTimes) { + verify(callback, times(numTimes)).onStateChanged(anyBoolean(), anyInt()); + } + + @Test + public void testRegister_RegisterUnregister() throws RemoteException { + doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any()); + + AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter); + AdapterStateCallback callback1 = mock(AdapterStateCallback.class); + AdapterStateCallback callback2 = mock(AdapterStateCallback.class); + + // Verify that the adapter state listener registered with the UWB Adapter + adapterStateListener.register(getExecutor(), callback1); + verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any()); + verifyCallbackStateChangedInvoked(callback1, 1); + verifyCallbackStateChangedInvoked(callback2, 0); + + // Register a second client and no new call to UWB Adapter + adapterStateListener.register(getExecutor(), callback2); + verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any()); + verifyCallbackStateChangedInvoked(callback1, 1); + verifyCallbackStateChangedInvoked(callback2, 1); + + // Unregister first callback + adapterStateListener.unregister(callback1); + verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any()); + verify(mUwbAdapter, times(0)).unregisterAdapterStateCallbacks(any()); + verifyCallbackStateChangedInvoked(callback1, 1); + verifyCallbackStateChangedInvoked(callback2, 1); + + // Unregister second callback + adapterStateListener.unregister(callback2); + verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any()); + verify(mUwbAdapter, times(1)).unregisterAdapterStateCallbacks(any()); + verifyCallbackStateChangedInvoked(callback1, 1); + verifyCallbackStateChangedInvoked(callback2, 1); + } + + @Test + public void testRegister_FirstRegisterFails() throws RemoteException { + AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter); + AdapterStateCallback callback1 = mock(AdapterStateCallback.class); + AdapterStateCallback callback2 = mock(AdapterStateCallback.class); + + // Throw a remote exception whenever first registering + doThrow(mThrowRemoteException).when(mUwbAdapter).registerAdapterStateCallbacks(any()); + + adapterStateListener.register(getExecutor(), callback1); + verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any()); + + // No longer throw an exception, instead succeed + doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any()); + + // Register a different callback + adapterStateListener.register(getExecutor(), callback2); + verify(mUwbAdapter, times(2)).registerAdapterStateCallbacks(any()); + + // Ensure first callback was invoked again + verifyCallbackStateChangedInvoked(callback1, 2); + verifyCallbackStateChangedInvoked(callback2, 1); + } + + @Test + public void testRegister_RegisterSameCallbackTwice() throws RemoteException { + AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter); + AdapterStateCallback callback = mock(AdapterStateCallback.class); + doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any()); + + adapterStateListener.register(getExecutor(), callback); + verifyCallbackStateChangedInvoked(callback, 1); + + adapterStateListener.register(getExecutor(), callback); + verifyCallbackStateChangedInvoked(callback, 1); + + // Invoke a state change and ensure the callback is only called once + adapterStateListener.onAdapterStateChanged(false, StateChangeReason.UNKNOWN); + verifyCallbackStateChangedInvoked(callback, 2); + } + + @Test + public void testCallback_RunViaExecutor_Success() throws RemoteException { + // Verify that the callbacks are invoked on the executor when successful + doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any()); + runViaExecutor(); + } + + @Test + public void testCallback_RunViaExecutor_Failure() throws RemoteException { + // Verify that the callbacks are invoked on the executor when there is a remote exception + doThrow(mThrowRemoteException).when(mUwbAdapter).registerAdapterStateCallbacks(any()); + runViaExecutor(); + } + + private void runViaExecutor() { + AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter); + AdapterStateCallback callback = mock(AdapterStateCallback.class); + + Executor executor = mock(Executor.class); + + // Do not run commands received and ensure that the callback is not invoked + doAnswer(new ExecutorAnswer(false)).when(executor).execute(any()); + adapterStateListener.register(executor, callback); + verify(executor, times(1)).execute(any()); + verifyCallbackStateChangedInvoked(callback, 0); + + // Manually invoke the callback and ensure callback is not invoked + adapterStateListener.onAdapterStateChanged(false, StateChangeReason.UNKNOWN); + verify(executor, times(2)).execute(any()); + verifyCallbackStateChangedInvoked(callback, 0); + + // Run the command that the executor receives + doAnswer(new ExecutorAnswer(true)).when(executor).execute(any()); + adapterStateListener.onAdapterStateChanged(false, StateChangeReason.UNKNOWN); + verify(executor, times(3)).execute(any()); + verifyCallbackStateChangedInvoked(callback, 1); + } + + class ExecutorAnswer implements Answer { + + final boolean mShouldRun; + ExecutorAnswer(boolean shouldRun) { + mShouldRun = shouldRun; + } + + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + if (mShouldRun) { + ((Runnable) invocation.getArgument(0)).run(); + } + return null; + } + } + + @Test + public void testNotify_AllCallbacksNotified() throws RemoteException { + doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any()); + + AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter); + List<AdapterStateCallback> callbacks = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + AdapterStateCallback callback = mock(AdapterStateCallback.class); + adapterStateListener.register(getExecutor(), callback); + callbacks.add(callback); + } + + // Ensure every callback got the initial state + for (AdapterStateCallback callback : callbacks) { + verifyCallbackStateChangedInvoked(callback, 1); + } + + // Invoke a state change and ensure all callbacks are invoked + adapterStateListener.onAdapterStateChanged(true, StateChangeReason.ALL_SESSIONS_CLOSED); + for (AdapterStateCallback callback : callbacks) { + verifyCallbackStateChangedInvoked(callback, 2); + } + } + + @Test + public void testStateChange_CorrectValue() { + AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter); + + AdapterStateCallback callback = mock(AdapterStateCallback.class); + + adapterStateListener.register(getExecutor(), callback); + + runStateChangeValue(StateChangeReason.ALL_SESSIONS_CLOSED, + AdapterStateCallback.STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED); + + runStateChangeValue(StateChangeReason.SESSION_STARTED, + AdapterStateCallback.STATE_CHANGED_REASON_SESSION_STARTED); + + runStateChangeValue(StateChangeReason.SYSTEM_BOOT, + AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_BOOT); + + runStateChangeValue(StateChangeReason.SYSTEM_POLICY, + AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_POLICY); + + runStateChangeValue(StateChangeReason.UNKNOWN, + AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN); + } + + private void runStateChangeValue(@StateChangeReason int reasonIn, + @AdapterStateCallback.StateChangedReason int reasonOut) { + AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter); + AdapterStateCallback callback = mock(AdapterStateCallback.class); + adapterStateListener.register(getExecutor(), callback); + + adapterStateListener.onAdapterStateChanged(false, reasonIn); + verify(callback, times(1)).onStateChanged(false, reasonOut); + + adapterStateListener.onAdapterStateChanged(true, reasonIn); + verify(callback, times(1)).onStateChanged(true, reasonOut); + } + + @Test + public void testStateChange_FirstRegisterGetsCorrectState() throws RemoteException { + AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter); + AdapterStateCallback callback = mock(AdapterStateCallback.class); + + Answer registerAnswer = new Answer() { + public Object answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + IUwbAdapterStateCallbacks cb = (IUwbAdapterStateCallbacks) args[0]; + try { + cb.onAdapterStateChanged(true, StateChangeReason.SESSION_STARTED); + } catch (RemoteException e) { + // Nothing to do + } + return new Object(); + } + }; + + doAnswer(registerAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any()); + + adapterStateListener.register(getExecutor(), callback); + verify(callback).onStateChanged(true, + AdapterStateCallback.STATE_CHANGED_REASON_SESSION_STARTED); + } + + @Test + public void testStateChange_SecondRegisterGetsCorrectState() { + AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter); + AdapterStateCallback callback1 = mock(AdapterStateCallback.class); + AdapterStateCallback callback2 = mock(AdapterStateCallback.class); + + adapterStateListener.register(getExecutor(), callback1); + adapterStateListener.onAdapterStateChanged(true, StateChangeReason.SYSTEM_BOOT); + + adapterStateListener.register(getExecutor(), callback2); + verify(callback2).onStateChanged(true, + AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_BOOT); + } +} diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 1dace8125ae1..a5667b2f0a8a 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -85,6 +85,12 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, + "-2014162875": { + "message": "Could not register window container listener token=%s, container=%s", + "level": "ERROR", + "group": "WM_ERROR", + "at": "com\/android\/server\/wm\/WindowContextListenerController.java" + }, "-2012562539": { "message": "startAnimation(): Notify animation start:", "level": "DEBUG", @@ -811,6 +817,12 @@ "group": "WM_DEBUG_FOCUS", "at": "com\/android\/server\/wm\/DisplayContent.java" }, + "-1136467585": { + "message": "The listener does not exist.", + "level": "INFO", + "group": "WM_DEBUG_ADD_REMOVE", + "at": "com\/android\/server\/wm\/WindowContextListenerController.java" + }, "-1136139407": { "message": "no-history finish of %s", "level": "DEBUG", @@ -1819,6 +1831,18 @@ "group": "WM_DEBUG_TASKS", "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" }, + "90764070": { + "message": "Could not report token removal to the window token client.", + "level": "WARN", + "group": "WM_ERROR", + "at": "com\/android\/server\/wm\/WindowContextListenerController.java" + }, + "91350919": { + "message": "Attempted to set IME flag to a display that does not exist: %d", + "level": "WARN", + "group": "WM_ERROR", + "at": "com\/android\/server\/wm\/WindowManagerService.java" + }, "94402792": { "message": "Moving to RESUMED: %s (in existing)", "level": "VERBOSE", @@ -1963,6 +1987,12 @@ "group": "WM_DEBUG_WINDOW_ORGANIZER", "at": "com\/android\/server\/wm\/TaskOrganizerController.java" }, + "236210101": { + "message": "registerWindowContextListener: trying to add listener to a non-existing display:%d", + "level": "WARN", + "group": "WM_ERROR", + "at": "com\/android\/server\/wm\/WindowManagerService.java" + }, "241961619": { "message": "Adding %s to %s", "level": "VERBOSE", @@ -3403,6 +3433,12 @@ "group": "WM_DEBUG_TASKS", "at": "com\/android\/server\/wm\/RootWindowContainer.java" }, + "1948483534": { + "message": "Could not report config changes to the window token client.", + "level": "WARN", + "group": "WM_ERROR", + "at": "com\/android\/server\/wm\/WindowContextListenerController.java" + }, "1964565370": { "message": "Starting remote animation", "level": "INFO", diff --git a/errorprone/refaster/EfficientXml.java b/errorprone/refaster/EfficientXml.java index bd1ddfc92e91..ae797c46b77e 100644 --- a/errorprone/refaster/EfficientXml.java +++ b/errorprone/refaster/EfficientXml.java @@ -292,6 +292,30 @@ public class EfficientXml { } } + class BooleanToStringTrue { + @BeforeTemplate + void before(TypedXmlSerializer out, String n) throws Exception { + out.attribute(null, n, "true"); + } + + @AfterTemplate + void after(TypedXmlSerializer out, String n) throws Exception { + out.attributeBoolean(null, n, true); + } + } + + class BooleanToStringFalse { + @BeforeTemplate + void before(TypedXmlSerializer out, String n) throws Exception { + out.attribute(null, n, "false"); + } + + @AfterTemplate + void after(TypedXmlSerializer out, String n) throws Exception { + out.attributeBoolean(null, n, false); + } + } + class BooleanFromString { @BeforeTemplate boolean beforeParse(TypedXmlPullParser in, String n) throws Exception { diff --git a/errorprone/refaster/EfficientXml.java.refaster b/errorprone/refaster/EfficientXml.java.refaster Binary files differindex f2974fffb83b..750c2dbe98c0 100644 --- a/errorprone/refaster/EfficientXml.java.refaster +++ b/errorprone/refaster/EfficientXml.java.refaster diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java index 28943baf9503..fb6ea99be7ab 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -88,31 +88,19 @@ public final class SystemFonts { } private static @NonNull Set<Font> collectAllFonts() { - Map<String, Typeface> map = Typeface.getSystemFontMap(); - HashSet<NativeFont.Font> seenFonts = new HashSet<>(); - HashSet<Font> result = new HashSet<>(); - for (Typeface typeface : map.values()) { - List<NativeFont.Family> families = NativeFont.readTypeface(typeface); - for (NativeFont.Family family : families) { - for (NativeFont.Font font : family.getFonts()) { - if (seenFonts.contains(font)) { - continue; - } - seenFonts.add(font); - try { - result.add(new Font.Builder(font.getFile(), family.getLocale()) - .setFontVariationSettings(font.getAxes()) - .setTtcIndex(font.getIndex()) - .setWeight(font.getStyle().getWeight()) - .setSlant(font.getStyle().getSlant()) - .build()); - } catch (IOException e) { - Log.w(TAG, "Failed to load " + font.getFile(), e); - } + final FontCustomizationParser.Result oemCustomization = + readFontCustomization("/product/etc/fonts_customization.xml", "/product/fonts/"); + Map<String, FontFamily[]> map = new ArrayMap<>(); + buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", oemCustomization, map); + Set<Font> res = new HashSet<>(); + for (FontFamily[] families : map.values()) { + for (FontFamily family : families) { + for (int i = 0; i < family.getSize(); ++i) { + res.add(family.getFont(i)); } } } - return result; + return res; } private static @Nullable ByteBuffer mmap(@NonNull String fullPath) { diff --git a/libs/WindowManager/Shell/res/layout/split_divider.xml b/libs/WindowManager/Shell/res/layout/split_divider.xml index b86f36a14d17..341fe617b2d0 100644 --- a/libs/WindowManager/Shell/res/layout/split_divider.xml +++ b/libs/WindowManager/Shell/res/layout/split_divider.xml @@ -14,7 +14,7 @@ ~ limitations under the License. --> -<com.android.wm.shell.apppairs.DividerView +<com.android.wm.shell.common.split.DividerView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="match_parent" android:layout_width="match_parent"> @@ -24,4 +24,4 @@ android:id="@+id/docked_divider_background" android:background="@color/docked_divider_background"/> -</com.android.wm.shell.apppairs.DividerView> +</com.android.wm.shell.common.split.DividerView> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java index d3032f83fc1c..cfbf8452ddae 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java @@ -29,11 +29,13 @@ import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.common.split.SplitLayout; import java.io.PrintWriter; @@ -42,7 +44,7 @@ import java.io.PrintWriter; * {@link #mTaskInfo1} and {@link #mTaskInfo2} in the pair. * Also includes all UI for managing the pair like the divider. */ -class AppPair implements ShellTaskOrganizer.TaskListener { +class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChangeListener { private static final String TAG = AppPair.class.getSimpleName(); private ActivityManager.RunningTaskInfo mRootTaskInfo; @@ -55,7 +57,7 @@ class AppPair implements ShellTaskOrganizer.TaskListener { private final AppPairsController mController; private final SyncTransactionQueue mSyncQueue; private final DisplayController mDisplayController; - private AppPairLayout mAppPairLayout; + private SplitLayout mSplitLayout; AppPair(AppPairsController controller) { mController = controller; @@ -92,11 +94,9 @@ class AppPair implements ShellTaskOrganizer.TaskListener { mTaskInfo1 = task1; mTaskInfo2 = task2; - mAppPairLayout = new AppPairLayout( + mSplitLayout = new SplitLayout( mDisplayController.getDisplayContext(mRootTaskInfo.displayId), - mDisplayController.getDisplay(mRootTaskInfo.displayId), - mRootTaskInfo.configuration, - mRootTaskLeash); + mRootTaskInfo.configuration, this, mRootTaskLeash); final WindowContainerToken token1 = task1.token; final WindowContainerToken token2 = task2.token; @@ -107,8 +107,8 @@ class AppPair implements ShellTaskOrganizer.TaskListener { .reparent(token2, mRootTaskInfo.token, true /* onTop */) .setWindowingMode(token1, WINDOWING_MODE_MULTI_WINDOW) .setWindowingMode(token2, WINDOWING_MODE_MULTI_WINDOW) - .setBounds(token1, mAppPairLayout.getBounds1()) - .setBounds(token2, mAppPairLayout.getBounds2()) + .setBounds(token1, mSplitLayout.getBounds1()) + .setBounds(token2, mSplitLayout.getBounds2()) // Moving the root task to top after the child tasks were repareted , or the root // task cannot be visible and focused. .reorder(mRootTaskInfo.token, true); @@ -117,6 +117,10 @@ class AppPair implements ShellTaskOrganizer.TaskListener { } void unpair() { + unpair(null /* toTopToken */); + } + + private void unpair(@Nullable WindowContainerToken toTopToken) { final WindowContainerToken token1 = mTaskInfo1.token; final WindowContainerToken token2 = mTaskInfo2.token; final WindowContainerTransaction wct = new WindowContainerTransaction(); @@ -124,16 +128,16 @@ class AppPair implements ShellTaskOrganizer.TaskListener { // Reparent out of this container and reset windowing mode. wct.setHidden(mRootTaskInfo.token, true) .reorder(mRootTaskInfo.token, false) - .reparent(token1, null, false /* onTop */) - .reparent(token2, null, false /* onTop */) + .reparent(token1, null, token1 == toTopToken /* onTop */) + .reparent(token2, null, token2 == toTopToken /* onTop */) .setWindowingMode(token1, WINDOWING_MODE_UNDEFINED) .setWindowingMode(token2, WINDOWING_MODE_UNDEFINED); mController.getTaskOrganizer().applyTransaction(wct); mTaskInfo1 = null; mTaskInfo2 = null; - mAppPairLayout.release(); - mAppPairLayout = null; + mSplitLayout.release(); + mSplitLayout = null; } @Override @@ -153,17 +157,17 @@ class AppPair implements ShellTaskOrganizer.TaskListener { if (mTaskLeash1 == null || mTaskLeash2 == null) return; - mAppPairLayout.init(); - final SurfaceControl dividerLeash = mAppPairLayout.getDividerLeash(); - final Rect dividerBounds = mAppPairLayout.getDividerBounds(); + mSplitLayout.init(); + final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash(); + final Rect dividerBounds = mSplitLayout.getDividerBounds(); // TODO: Is there more we need to do here? mSyncQueue.runInSync(t -> { - t.setPosition(mTaskLeash1, mTaskInfo1.positionInParent.x, - mTaskInfo1.positionInParent.y) + t.setLayer(dividerLeash, Integer.MAX_VALUE) + .setPosition(mTaskLeash1, mTaskInfo1.positionInParent.x, + mTaskInfo1.positionInParent.y) .setPosition(mTaskLeash2, mTaskInfo2.positionInParent.x, mTaskInfo2.positionInParent.y) - .setLayer(dividerLeash, Integer.MAX_VALUE) .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top) .show(mRootTaskLeash) .show(mTaskLeash1) @@ -185,14 +189,14 @@ class AppPair implements ShellTaskOrganizer.TaskListener { } mRootTaskInfo = taskInfo; - if (mAppPairLayout != null - && mAppPairLayout.updateConfiguration(mRootTaskInfo.configuration)) { + if (mSplitLayout != null + && mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) { // Update bounds when root bounds or its orientation changed. final WindowContainerTransaction wct = new WindowContainerTransaction(); - final SurfaceControl dividerLeash = mAppPairLayout.getDividerLeash(); - final Rect dividerBounds = mAppPairLayout.getDividerBounds(); - final Rect bounds1 = mAppPairLayout.getBounds1(); - final Rect bounds2 = mAppPairLayout.getBounds2(); + final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash(); + final Rect dividerBounds = mSplitLayout.getDividerBounds(); + final Rect bounds1 = mSplitLayout.getBounds1(); + final Rect bounds2 = mSplitLayout.getBounds2(); wct.setBounds(mTaskInfo1.token, bounds1) .setBounds(mTaskInfo2.token, bounds2); @@ -200,7 +204,9 @@ class AppPair implements ShellTaskOrganizer.TaskListener { mSyncQueue.runInSync(t -> t .setPosition(mTaskLeash1, bounds1.left, bounds1.top) .setPosition(mTaskLeash2, bounds2.left, bounds2.top) - .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)); + .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top) + // Resets layer to divider bar to make sure it is always on top. + .setLayer(dividerLeash, Integer.MAX_VALUE)); } } else if (taskInfo.taskId == getTaskId1()) { mTaskInfo1 = taskInfo; @@ -242,4 +248,39 @@ class AppPair implements ShellTaskOrganizer.TaskListener { public String toString() { return TAG + "#" + getRootTaskId(); } + + @Override + public void onSnappedToDismiss(boolean snappedToEnd) { + unpair(snappedToEnd ? mTaskInfo1.token : mTaskInfo2.token /* toTopToken */); + } + + @Override + public void onBoundsChanging(SplitLayout layout) { + final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash(); + if (dividerLeash == null) return; + final Rect dividerBounds = layout.getDividerBounds(); + final Rect bounds1 = layout.getBounds1(); + final Rect bounds2 = layout.getBounds2(); + mSyncQueue.runInSync(t -> t + .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top) + .setPosition(mTaskLeash1, bounds1.left, bounds1.top) + .setPosition(mTaskLeash2, bounds2.left, bounds2.top)); + } + + @Override + public void onBoundsChanged(SplitLayout layout) { + final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash(); + if (dividerLeash == null) return; + final Rect dividerBounds = layout.getDividerBounds(); + final Rect bounds1 = layout.getBounds1(); + final Rect bounds2 = layout.getBounds2(); + final WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.setBounds(mTaskInfo1.token, bounds1) + .setBounds(mTaskInfo2.token, bounds2); + mController.getTaskOrganizer().applyTransaction(wct); + mSyncQueue.runInSync(t -> t + .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top) + .setPosition(mTaskLeash1, bounds1.left, bounds1.top) + .setPosition(mTaskLeash2, bounds2.left, bounds2.top)); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java deleted file mode 100644 index 8c8655e1ff1f..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.apppairs; - -import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; -import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; -import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; -import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; -import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; - -import android.content.Context; -import android.content.res.Configuration; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.graphics.Region; -import android.os.Binder; -import android.os.IBinder; -import android.view.Display; -import android.view.IWindow; -import android.view.LayoutInflater; -import android.view.SurfaceControl; -import android.view.SurfaceControlViewHost; -import android.view.WindowManager; -import android.view.WindowlessWindowManager; - -import com.android.wm.shell.R; - -/** - * Records and handles layout of a pair of apps. - */ -final class AppPairLayout { - private static final String DIVIDER_WINDOW_TITLE = "AppPairDivider"; - private final Display mDisplay; - private final int mDividerWindowWidth; - private final int mDividerWindowInsets; - private final AppPairWindowManager mAppPairWindowManager; - - private Context mContext; - private Rect mRootBounds; - private DIVIDE_POLICY mDividePolicy; - - private SurfaceControlViewHost mViewHost; - private SurfaceControl mDividerLeash; - - AppPairLayout( - Context context, - Display display, - Configuration configuration, - SurfaceControl rootLeash) { - mContext = context.createConfigurationContext(configuration); - mDisplay = display; - mRootBounds = configuration.windowConfiguration.getBounds(); - mDividerWindowWidth = mContext.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.docked_stack_divider_thickness); - mDividerWindowInsets = mContext.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.docked_stack_divider_insets); - - mAppPairWindowManager = new AppPairWindowManager(configuration, rootLeash); - mDividePolicy = DIVIDE_POLICY.MIDDLE; - mDividePolicy.update(mRootBounds, mDividerWindowWidth, mDividerWindowInsets); - } - - boolean updateConfiguration(Configuration configuration) { - mAppPairWindowManager.setConfiguration(configuration); - final Rect rootBounds = configuration.windowConfiguration.getBounds(); - if (isIdenticalBounds(mRootBounds, rootBounds)) { - return false; - } - - mContext = mContext.createConfigurationContext(configuration); - mRootBounds = rootBounds; - mDividePolicy.update(mRootBounds, mDividerWindowWidth, mDividerWindowInsets); - release(); - init(); - return true; - } - - Rect getBounds1() { - return mDividePolicy.mBounds1; - } - - Rect getBounds2() { - return mDividePolicy.mBounds2; - } - - Rect getDividerBounds() { - return mDividePolicy.mDividerBounds; - } - - SurfaceControl getDividerLeash() { - return mDividerLeash; - } - - void release() { - if (mViewHost == null) { - return; - } - mViewHost.release(); - mDividerLeash = null; - mViewHost = null; - } - - void init() { - if (mViewHost == null) { - mViewHost = new SurfaceControlViewHost(mContext, mDisplay, mAppPairWindowManager); - } - - final DividerView dividerView = (DividerView) LayoutInflater.from(mContext) - .inflate(R.layout.split_divider, null); - - WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - mDividePolicy.mDividerBounds.width(), - mDividePolicy.mDividerBounds.height(), - TYPE_DOCK_DIVIDER, - FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL | FLAG_WATCH_OUTSIDE_TOUCH - | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY, - PixelFormat.TRANSLUCENT); - lp.token = new Binder(); - lp.setTitle(DIVIDER_WINDOW_TITLE); - lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION; - - mViewHost.setView(dividerView, lp); - mDividerLeash = mAppPairWindowManager.getSurfaceControl(mViewHost.getWindowToken()); - } - - private static boolean isIdenticalBounds(Rect bounds1, Rect bounds2) { - return bounds1.left == bounds2.left && bounds1.top == bounds2.top - && bounds1.right == bounds2.right && bounds1.bottom == bounds2.bottom; - } - - /** - * Indicates the policy of placing divider bar and corresponding split-screens. - */ - // TODO(172704238): add more divide policy and provide snap to resize feature for divider bar. - enum DIVIDE_POLICY { - MIDDLE; - - void update(Rect rootBounds, int dividerWindowWidth, int dividerWindowInsets) { - final int dividerOffset = dividerWindowWidth / 2; - final int boundsOffset = dividerOffset - dividerWindowInsets; - - mDividerBounds = new Rect(rootBounds); - mBounds1 = new Rect(rootBounds); - mBounds2 = new Rect(rootBounds); - - switch (this) { - case MIDDLE: - default: - if (isLandscape(rootBounds)) { - mDividerBounds.left = rootBounds.width() / 2 - dividerOffset; - mDividerBounds.right = rootBounds.width() / 2 + dividerOffset; - mBounds1.left = rootBounds.width() / 2 + boundsOffset; - mBounds2.right = rootBounds.width() / 2 - boundsOffset; - } else { - mDividerBounds.top = rootBounds.height() / 2 - dividerOffset; - mDividerBounds.bottom = rootBounds.height() / 2 + dividerOffset; - mBounds1.bottom = rootBounds.height() / 2 - boundsOffset; - mBounds2.top = rootBounds.height() / 2 + boundsOffset; - } - } - } - - private boolean isLandscape(Rect bounds) { - return bounds.width() > bounds.height(); - } - - Rect mDividerBounds; - Rect mBounds1; - Rect mBounds2; - } - - /** - * WindowManger for app pair. Holds view hierarchy for the root task. - */ - private static final class AppPairWindowManager extends WindowlessWindowManager { - AppPairWindowManager(Configuration config, SurfaceControl rootSurface) { - super(config, rootSurface, null /* hostInputToken */); - } - - @Override - public void setTouchRegion(IBinder window, Region region) { - super.setTouchRegion(window, region); - } - - @Override - public SurfaceControl getSurfaceControl(IWindow window) { - return super.getSurfaceControl(window); - } - - @Override - public void setConfiguration(Configuration configuration) { - super.setConfiguration(configuration); - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/DividerView.java deleted file mode 100644 index 41b5e47e7fa9..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/DividerView.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.apppairs; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.widget.FrameLayout; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -/** - * Stack divider for app pair. - */ -public class DividerView extends FrameLayout { - public DividerView(@NonNull Context context) { - super(context); - } - - public DividerView(@NonNull Context context, - @Nullable AttributeSet attrs) { - super(context, attrs); - } - - public DividerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public DividerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, - int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - void show() { - post(() -> setVisibility(View.VISIBLE)); - } - - void hide() { - post(() -> setVisibility(View.INVISIBLE)); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java new file mode 100644 index 000000000000..50d9fe8629ac --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.common.split; + +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.SurfaceControlViewHost; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.WindowManager; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.internal.policy.DividerSnapAlgorithm; + +/** + * Stack divider for app pair. + */ +// TODO(b/172704238): add handle view to indicate touching status. +public class DividerView extends FrameLayout implements View.OnTouchListener { + private final int mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); + + private SplitLayout mSplitLayout; + private SurfaceControlViewHost mViewHost; + private DragListener mDragListener; + + private VelocityTracker mVelocityTracker; + private boolean mMoving; + private int mStartPos; + + public DividerView(@NonNull Context context) { + super(context); + } + + public DividerView(@NonNull Context context, + @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public DividerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public DividerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + /** Sets up essential dependencies of the divider bar. */ + public void setup( + SplitLayout layout, + SurfaceControlViewHost viewHost, + @Nullable DragListener dragListener) { + mSplitLayout = layout; + mViewHost = viewHost; + mDragListener = dragListener; + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + setOnTouchListener(this); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (mSplitLayout == null) { + return false; + } + + final int action = event.getAction() & MotionEvent.ACTION_MASK; + final boolean isLandscape = isLandscape(); + // Using raw xy to prevent lost track of motion events while moving divider bar. + final int touchPos = isLandscape ? (int) event.getRawX() : (int) event.getRawY(); + switch (action) { + case MotionEvent.ACTION_DOWN: + mVelocityTracker = VelocityTracker.obtain(); + mVelocityTracker.addMovement(event); + setSlippery(false); + mStartPos = touchPos; + mMoving = false; + break; + case MotionEvent.ACTION_MOVE: + mVelocityTracker.addMovement(event); + if (!mMoving && Math.abs(touchPos - mStartPos) > mTouchSlop) { + mStartPos = touchPos; + mMoving = true; + if (mDragListener != null) { + mDragListener.onDragStart(); + } + } + if (mMoving) { + final int position = mSplitLayout.getDividePosition() + touchPos - mStartPos; + mSplitLayout.updateDividePosition(position); + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mVelocityTracker.addMovement(event); + mVelocityTracker.computeCurrentVelocity(1000 /* units */); + final float velocity = isLandscape + ? mVelocityTracker.getXVelocity() + : mVelocityTracker.getYVelocity(); + setSlippery(true); + mMoving = false; + if (mDragListener != null) { + mDragListener.onDragEnd(); + } + + final int position = mSplitLayout.getDividePosition() + touchPos - mStartPos; + final DividerSnapAlgorithm.SnapTarget snapTarget = + mSplitLayout.findSnapTarget(position, velocity); + mSplitLayout.setSnapTarget(snapTarget); + break; + } + return true; + } + + private void setSlippery(boolean slippery) { + if (mViewHost == null) { + return; + } + + final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams(); + final boolean isSlippery = (lp.flags & FLAG_SLIPPERY) != 0; + if (isSlippery == slippery) { + return; + } + + if (slippery) { + lp.flags |= FLAG_SLIPPERY; + } else { + lp.flags &= ~FLAG_SLIPPERY; + } + mViewHost.relayout(lp); + } + + private boolean isLandscape() { + return getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE; + } + + /** Monitors dragging action of the divider bar. */ + // TODO(b/172704238): add listeners to deal with resizing state of the app windows. + public interface DragListener { + /** Called when start dragging. */ + void onDragStart(); + /** Called when stop dragging. */ + void onDragEnd(); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java new file mode 100644 index 000000000000..e11037f55cfa --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.common.split; + +import static android.view.WindowManager.DOCKED_LEFT; +import static android.view.WindowManager.DOCKED_TOP; + +import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END; +import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START; + +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Rect; +import android.view.SurfaceControl; + +import androidx.annotation.Nullable; + +import com.android.internal.policy.DividerSnapAlgorithm; + +/** + * Records and handles layout of splits. Helps to calculate proper bounds when configuration or + * divide position changes. + */ +public class SplitLayout { + private final int mDividerWindowWidth; + private final int mDividerInsets; + private final int mDividerSize; + + private final Rect mRootBounds = new Rect(); + private final Rect mDividerBounds = new Rect(); + private final Rect mBounds1 = new Rect(); + private final Rect mBounds2 = new Rect(); + private final LayoutChangeListener mLayoutChangeListener; + private final SplitWindowManager mSplitWindowManager; + + private Context mContext; + private DividerSnapAlgorithm mDividerSnapAlgorithm; + private int mDividePosition; + + public SplitLayout(Context context, Configuration configuration, + LayoutChangeListener layoutChangeListener, SurfaceControl rootLeash) { + mContext = context.createConfigurationContext(configuration); + mLayoutChangeListener = layoutChangeListener; + mSplitWindowManager = new SplitWindowManager(mContext, configuration, rootLeash); + + mDividerWindowWidth = context.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.docked_stack_divider_thickness); + mDividerInsets = context.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.docked_stack_divider_insets); + mDividerSize = mDividerWindowWidth - mDividerInsets * 2; + + mRootBounds.set(configuration.windowConfiguration.getBounds()); + mDividerSnapAlgorithm = getSnapAlgorithm(context.getResources(), mRootBounds); + mDividePosition = mDividerSnapAlgorithm.getMiddleTarget().position; + updateBounds(mDividePosition); + } + + /** Gets bounds of the primary split. */ + public Rect getBounds1() { + return mBounds1; + } + + /** Gets bounds of the secondary split. */ + public Rect getBounds2() { + return mBounds2; + } + + /** Gets bounds of divider window. */ + public Rect getDividerBounds() { + return mDividerBounds; + } + + /** Returns leash of the current divider bar. */ + @Nullable + public SurfaceControl getDividerLeash() { + return mSplitWindowManager == null ? null : mSplitWindowManager.getSurfaceControl(); + } + + int getDividePosition() { + return mDividePosition; + } + + /** Applies new configuration, returns {@code false} if there's no effect to the layout. */ + public boolean updateConfiguration(Configuration configuration) { + final Rect rootBounds = configuration.windowConfiguration.getBounds(); + if (mRootBounds.equals(rootBounds)) { + return false; + } + + mContext = mContext.createConfigurationContext(configuration); + mSplitWindowManager.setConfiguration(configuration); + mRootBounds.set(rootBounds); + mDividerSnapAlgorithm = getSnapAlgorithm(mContext.getResources(), mRootBounds); + mDividePosition = mDividerSnapAlgorithm.getMiddleTarget().position; + updateBounds(mDividePosition); + release(); + init(); + return true; + } + + /** Updates recording bounds of divider window and both of the splits. */ + private void updateBounds(int position) { + mDividerBounds.set(mRootBounds); + mBounds1.set(mRootBounds); + mBounds2.set(mRootBounds); + if (isLandscape(mRootBounds)) { + mDividerBounds.left = position - mDividerInsets; + mDividerBounds.right = mDividerBounds.left + mDividerWindowWidth; + mBounds1.right = mBounds1.left + position; + mBounds2.left = mBounds1.right + mDividerSize; + } else { + mDividerBounds.top = position - mDividerInsets; + mDividerBounds.bottom = mDividerBounds.top + mDividerWindowWidth; + mBounds1.bottom = mBounds1.top + position; + mBounds2.top = mBounds1.bottom + mDividerSize; + } + } + + /** Inflates {@link DividerView} on the root surface. */ + public void init() { + mSplitWindowManager.init(this); + } + + /** Releases the surface holding the current {@link DividerView}. */ + public void release() { + mSplitWindowManager.release(); + } + + /** + * Updates bounds with the passing position. Usually used to update recording bounds while + * performing animation or dragging divider bar to resize the splits. + */ + public void updateDividePosition(int position) { + updateBounds(position); + mLayoutChangeListener.onBoundsChanging(this); + } + + /** + * Sets new divide position and updates bounds correspondingly. Notifies listener if the new + * target indicates dismissing split. + */ + public void setSnapTarget(DividerSnapAlgorithm.SnapTarget snapTarget) { + switch(snapTarget.flag) { + case FLAG_DISMISS_START: + mLayoutChangeListener.onSnappedToDismiss(false /* snappedToEnd */); + break; + case FLAG_DISMISS_END: + mLayoutChangeListener.onSnappedToDismiss(true /* snappedToEnd */); + break; + default: + mDividePosition = snapTarget.position; + updateBounds(mDividePosition); + mLayoutChangeListener.onBoundsChanged(this); + break; + } + } + + /** + * Returns {@link DividerSnapAlgorithm.SnapTarget} which matches passing position and velocity. + */ + public DividerSnapAlgorithm.SnapTarget findSnapTarget(int position, float velocity) { + return mDividerSnapAlgorithm.calculateSnapTarget(position, velocity); + } + + private DividerSnapAlgorithm getSnapAlgorithm(Resources resources, Rect rootBounds) { + final boolean isLandscape = isLandscape(rootBounds); + return new DividerSnapAlgorithm( + resources, + rootBounds.width(), + rootBounds.height(), + mDividerSize, + !isLandscape, + new Rect() /* insets */, + isLandscape ? DOCKED_LEFT : DOCKED_TOP /* dockSide */); + } + + private static boolean isLandscape(Rect bounds) { + return bounds.width() > bounds.height(); + } + + /** Listens layout change event. */ + public interface LayoutChangeListener { + /** Calls when dismissing split. */ + void onSnappedToDismiss(boolean snappedToEnd); + /** Calls when the bounds is changing due to animation or dragging divider bar. */ + void onBoundsChanging(SplitLayout layout); + /** Calls when the target bounds changed. */ + void onBoundsChanged(SplitLayout layout); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java new file mode 100644 index 000000000000..542867d83e29 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.common.split; + +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; +import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; +import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; +import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; +import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; + +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.Region; +import android.os.Binder; +import android.os.IBinder; +import android.view.IWindow; +import android.view.LayoutInflater; +import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; +import android.view.WindowManager; +import android.view.WindowlessWindowManager; + +import androidx.annotation.Nullable; + +import com.android.wm.shell.R; + +/** + * Holds view hierarchy of a root surface and helps to inflate {@link DividerView} for a split. + */ +public final class SplitWindowManager extends WindowlessWindowManager { + private static final String DIVIDER_WINDOW_TITLE = "SplitDivider"; + + private Context mContext; + private SurfaceControlViewHost mViewHost; + + public SplitWindowManager(Context context, Configuration config, SurfaceControl rootSurface) { + super(config, rootSurface, null /* hostInputToken */); + mContext = context.createConfigurationContext(config); + } + + @Override + public void setTouchRegion(IBinder window, Region region) { + super.setTouchRegion(window, region); + } + + @Override + public SurfaceControl getSurfaceControl(IWindow window) { + return super.getSurfaceControl(window); + } + + @Override + public void setConfiguration(Configuration configuration) { + super.setConfiguration(configuration); + mContext = mContext.createConfigurationContext(configuration); + } + + /** Inflates {@link DividerView} on to the root surface. */ + void init(SplitLayout splitLayout) { + if (mViewHost == null) { + mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); + } + + final Rect dividerBounds = splitLayout.getDividerBounds(); + final DividerView dividerView = (DividerView) LayoutInflater.from(mContext) + .inflate(R.layout.split_divider, null /* root */); + + WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + dividerBounds.width(), dividerBounds.height(), TYPE_DOCK_DIVIDER, + FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL | FLAG_WATCH_OUTSIDE_TOUCH + | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY, + PixelFormat.TRANSLUCENT); + lp.token = new Binder(); + lp.setTitle(DIVIDER_WINDOW_TITLE); + lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION; + mViewHost.setView(dividerView, lp); + dividerView.setup(splitLayout, mViewHost, null /* dragListener */); + } + + /** + * Releases the surface control of the current {@link DividerView} and tear down the view + * hierarchy. + */ + void release() { + if (mViewHost == null) return; + mViewHost.release(); + mViewHost = null; + } + + /** + * Gets {@link SurfaceControl} of the surface holding divider view. @return {@code null} if not + * feasible. + */ + @Nullable + SurfaceControl getSurfaceControl() { + return mViewHost == null ? null : getSurfaceControl(mViewHost.getWindowToken()); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java index 6145b7c71c09..5593268588fd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java @@ -21,7 +21,6 @@ import android.content.ComponentName; import android.content.pm.ParceledListSlice; import android.os.RemoteException; import android.view.DisplayInfo; -import android.view.IPinnedStackController; import android.view.IPinnedStackListener; import android.view.WindowManagerGlobal; @@ -62,12 +61,6 @@ public class PinnedStackListenerForwarder { displayId, mListenerImpl); } - private void onListenerRegistered(IPinnedStackController controller) { - for (PinnedStackListener listener : mListeners) { - listener.onListenerRegistered(controller); - } - } - private void onMovementBoundsChanged(boolean fromImeAdjustment) { for (PinnedStackListener listener : mListeners) { listener.onMovementBoundsChanged(fromImeAdjustment); @@ -113,13 +106,6 @@ public class PinnedStackListenerForwarder { @BinderThread private class PinnedStackListenerImpl extends IPinnedStackListener.Stub { @Override - public void onListenerRegistered(IPinnedStackController controller) { - mShellMainExecutor.execute(() -> { - PinnedStackListenerForwarder.this.onListenerRegistered(controller); - }); - } - - @Override public void onMovementBoundsChanged(boolean fromImeAdjustment) { mShellMainExecutor.execute(() -> { PinnedStackListenerForwarder.this.onMovementBoundsChanged(fromImeAdjustment); @@ -174,8 +160,6 @@ public class PinnedStackListenerForwarder { * Subclasses can ignore those methods they do not intend to take action upon. */ public static class PinnedStackListener { - public void onListenerRegistered(IPinnedStackController controller) {} - public void onMovementBoundsChanged(boolean fromImeAdjustment) {} public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 119ef6d60bea..3234ef6ccf66 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -41,7 +41,6 @@ import android.util.Log; import android.util.Pair; import android.util.Slog; import android.view.DisplayInfo; -import android.view.IPinnedStackController; import android.view.WindowManagerGlobal; import android.window.WindowContainerTransaction; @@ -162,11 +161,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac private class PipControllerPinnedStackListener extends PinnedStackListenerForwarder.PinnedStackListener { @Override - public void onListenerRegistered(IPinnedStackController controller) { - mTouchHandler.setPinnedStackController(controller); - } - - @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { mPipBoundsState.setImeVisibility(imeVisible, imeHeight); mTouchHandler.onImeVisibilityChanged(imeVisible, imeHeight); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java index 4e991f2e919b..2e10fc93cafb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java @@ -475,7 +475,7 @@ public class PipMenuView extends FrameLayout { final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS, Uri.fromParts("package", topPipActivityInfo.first.getPackageName(), null)); settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK); - mContext.startActivityAsUser(settingsIntent, UserHandle.CURRENT); + mContext.startActivityAsUser(settingsIntent, UserHandle.of(topPipActivityInfo.second)); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index 1ab31f846db7..b7cfad9030f7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -31,11 +31,8 @@ import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.os.Handler; -import android.os.RemoteException; import android.provider.DeviceConfig; -import android.util.Log; import android.util.Size; -import android.view.IPinnedStackController; import android.view.InputEvent; import android.view.MotionEvent; import android.view.ViewConfiguration; @@ -76,7 +73,6 @@ public class PipTouchHandler { private final PipDismissTargetHandler mPipDismissTargetHandler; private PipResizeGestureHandler mPipResizeGestureHandler; - private IPinnedStackController mPinnedStackController; private WeakReference<Consumer<Rect>> mPipExclusionBoundsChangeListener; private final PhonePipMenuController mMenuController; @@ -464,10 +460,6 @@ public class PipTouchHandler { if (!(inputEvent instanceof MotionEvent)) { return true; } - // Skip touch handling until we are bound to the controller - if (mPinnedStackController == null) { - return true; - } MotionEvent ev = (MotionEvent) inputEvent; if (!mPipBoundsState.isStashed() && mPipResizeGestureHandler.willStartResizeGesture(ev)) { @@ -591,13 +583,6 @@ public class PipTouchHandler { } /** - * Sets the controller to update the system of changes from user interaction. - */ - void setPinnedStackController(IPinnedStackController controller) { - mPinnedStackController = controller; - } - - /** * Sets the menu visibility. */ private void setMenuState(int menuState, boolean resize, Runnable callback) { @@ -625,13 +610,9 @@ public class PipTouchHandler { // bounds which are now stale. In such a case we defer the animation to the // normal bounds until after the next onMovementBoundsChanged() call to get the // bounds in the new orientation - try { - int displayRotation = mPinnedStackController.getDisplayRotation(); - if (mDisplayRotation != displayRotation) { - mDeferResizeToNormalBoundsUntilRotation = displayRotation; - } - } catch (RemoteException e) { - Log.e(TAG, "Could not get display rotation from controller"); + int displayRotation = mContext.getDisplay().getRotation(); + if (mDisplayRotation != displayRotation) { + mDeferResizeToNormalBoundsUntilRotation = displayRotation; } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt index 0fb43e263d05..3ed53fb221a7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt @@ -18,78 +18,6 @@ package com.android.wm.shell.flicker import com.android.server.wm.flicker.dsl.EventLogAssertion import com.android.server.wm.flicker.dsl.LayersAssertion -import com.android.server.wm.flicker.dsl.WmAssertion -import com.android.server.wm.flicker.helpers.WindowUtils - -@JvmOverloads -fun WmAssertion.statusBarWindowIsAlwaysVisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("statusBarWindowIsAlwaysVisible", bugId, enabled) { - this.showsAboveAppWindow(FlickerTestBase.STATUS_BAR_WINDOW_TITLE) - } -} - -@JvmOverloads -fun WmAssertion.navBarWindowIsAlwaysVisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("navBarWindowIsAlwaysVisible", bugId, enabled) { - this.showsAboveAppWindow(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE) - } -} - -@JvmOverloads -fun LayersAssertion.noUncoveredRegions( - beginRotation: Int, - endRotation: Int = beginRotation, - allStates: Boolean = true, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - val startingBounds = WindowUtils.getDisplayBounds(beginRotation) - val endingBounds = WindowUtils.getDisplayBounds(endRotation) - if (allStates) { - all("noUncoveredRegions", bugId, enabled) { - if (startingBounds == endingBounds) { - this.coversAtLeastRegion(startingBounds) - } else { - this.coversAtLeastRegion(startingBounds) - .then() - .coversAtLeastRegion(endingBounds) - } - } - } else { - start("noUncoveredRegions_StartingPos") { - this.coversAtLeastRegion(startingBounds) - } - end("noUncoveredRegions_EndingPos") { - this.coversAtLeastRegion(endingBounds) - } - } -} - -@JvmOverloads -fun LayersAssertion.statusBarLayerIsAlwaysVisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("statusBarLayerIsAlwaysVisible", bugId, enabled) { - this.showsLayer(FlickerTestBase.STATUS_BAR_WINDOW_TITLE) - } -} - -@JvmOverloads -fun LayersAssertion.navBarLayerIsAlwaysVisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("navBarLayerIsAlwaysVisible", bugId, enabled) { - this.showsLayer(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE) - } -} @JvmOverloads fun LayersAssertion.appPairsDividerIsVisible( @@ -131,48 +59,6 @@ fun LayersAssertion.dockedStackDividerIsInvisible( } } -@JvmOverloads -fun LayersAssertion.navBarLayerRotatesAndScales( - beginRotation: Int, - endRotation: Int = beginRotation, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - val startingPos = WindowUtils.getNavigationBarPosition(beginRotation) - val endingPos = WindowUtils.getNavigationBarPosition(endRotation) - - start("navBarLayerRotatesAndScales_StartingPos", bugId, enabled) { - this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, startingPos) - } - end("navBarLayerRotatesAndScales_EndingPost", bugId, enabled) { - this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, endingPos) - } - - if (startingPos == endingPos) { - all("navBarLayerRotatesAndScales", bugId, enabled) { - this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, startingPos) - } - } -} - -@JvmOverloads -fun LayersAssertion.statusBarLayerRotatesScales( - beginRotation: Int, - endRotation: Int = beginRotation, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - val startingPos = WindowUtils.getStatusBarPosition(beginRotation) - val endingPos = WindowUtils.getStatusBarPosition(endRotation) - - start("statusBarLayerRotatesScales_StartingPos", bugId, enabled) { - this.hasVisibleRegion(FlickerTestBase.STATUS_BAR_WINDOW_TITLE, startingPos) - } - end("statusBarLayerRotatesScales_EndingPos", bugId, enabled) { - this.hasVisibleRegion(FlickerTestBase.STATUS_BAR_WINDOW_TITLE, endingPos) - } -} - fun EventLogAssertion.focusChanges( vararg windows: String, bugId: Int = 0, diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt index ced99de21a46..7ac91b065fca 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt @@ -29,10 +29,10 @@ import com.android.wm.shell.flicker.helpers.AppPairsHelper import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.TEST_REPETITIONS import com.android.wm.shell.flicker.appPairsDividerIsInvisible import com.android.wm.shell.flicker.appPairsDividerIsVisible -import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt index 0663eb344f46..c9396aa7ea63 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt @@ -23,10 +23,10 @@ import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.wm.shell.flicker.helpers.FixedAppHelper import com.android.wm.shell.flicker.helpers.PipAppHelper -import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP import org.junit.FixMethodOrder import org.junit.Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt index 322034ce7688..76aabc1b83cf 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt @@ -24,10 +24,10 @@ import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.wm.shell.flicker.helpers.FixedAppHelper import com.android.wm.shell.flicker.helpers.PipAppHelper -import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt index 96d98d56e069..a67b3b760c49 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt @@ -31,13 +31,13 @@ import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.wm.shell.flicker.helpers.FixedAppHelper import com.android.wm.shell.flicker.helpers.PipAppHelper -import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.navBarLayerRotatesAndScales -import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.noUncoveredRegions -import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarLayerRotatesScales -import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.noUncoveredRegions +import com.android.server.wm.flicker.navBarLayerRotatesAndScales +import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP import org.junit.FixMethodOrder import org.junit.runner.RunWith diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipSplitScreenTest.kt index d20552f0739d..f79b21ff278d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipSplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipSplitScreenTest.kt @@ -28,10 +28,10 @@ import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.wm.shell.flicker.helpers.ImeAppHelper import com.android.wm.shell.flicker.helpers.FixedAppHelper import com.android.wm.shell.flicker.helpers.PipAppHelper -import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP import org.junit.FixMethodOrder import org.junit.Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt index c61a0f171714..5570a562a515 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt @@ -25,10 +25,10 @@ import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.wm.shell.flicker.dockedStackDividerIsVisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper.Companion.TEST_REPETITIONS -import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt index bf9286980b9a..7c47d1f1b1ae 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt @@ -29,8 +29,8 @@ import com.android.server.wm.flicker.helpers.resizeSplitScreen import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.wm.shell.flicker.dockedStackDividerIsInvisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper.Companion.TEST_REPETITIONS -import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairLayoutTests.java deleted file mode 100644 index c9d32c4b1f76..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairLayoutTests.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.apppairs; - -import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; -import static android.content.res.Configuration.ORIENTATION_PORTRAIT; - -import static com.google.common.truth.Truth.assertThat; - -import android.content.res.Configuration; -import android.graphics.Rect; -import android.view.Display; -import android.view.SurfaceControl; - -import androidx.test.annotation.UiThreadTest; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import com.android.wm.shell.ShellTestCase; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -/** Tests for {@link AppPairLayout} */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class AppPairLayoutTests extends ShellTestCase { - @Mock SurfaceControl mSurfaceControl; - private Display mDisplay; - private Configuration mConfiguration; - private AppPairLayout mAppPairLayout; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - mConfiguration = getConfiguration(false); - mDisplay = mContext.getDisplay(); - mAppPairLayout = new AppPairLayout(mContext, mDisplay, mConfiguration, mSurfaceControl); - } - - @After - @UiThreadTest - public void tearDown() { - mAppPairLayout.release(); - } - - @Test - @UiThreadTest - public void testUpdateConfiguration() { - assertThat(mAppPairLayout.updateConfiguration(getConfiguration(false))).isFalse(); - assertThat(mAppPairLayout.updateConfiguration(getConfiguration(true))).isTrue(); - } - - @Test - @UiThreadTest - public void testInitRelease() { - mAppPairLayout.init(); - assertThat(mAppPairLayout.getDividerLeash()).isNotNull(); - mAppPairLayout.release(); - assertThat(mAppPairLayout.getDividerLeash()).isNull(); - } - - private static Configuration getConfiguration(boolean isLandscape) { - final Configuration configuration = new Configuration(); - configuration.unset(); - configuration.orientation = isLandscape ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; - configuration.windowConfiguration.setBounds( - new Rect(0, 0, isLandscape ? 2160 : 1080, isLandscape ? 1080 : 2160)); - return configuration; - } -} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java index f12648a7f709..8dbc1d56bcc2 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java @@ -55,13 +55,13 @@ public class AppPairTests extends ShellTestCase { @Before public void setUp() { MockitoAnnotations.initMocks(this); + when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext); + when(mDisplayController.getDisplay(anyInt())).thenReturn( + mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY)); mController = new TestAppPairsController( mTaskOrganizer, mSyncQueue, mDisplayController); - when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext); - when(mDisplayController.getDisplay(anyInt())).thenReturn( - mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY)); } @After diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java index f8c68d2018da..fada694a4c07 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java @@ -55,14 +55,14 @@ public class AppPairsControllerTests extends ShellTestCase { @Before public void setUp() { MockitoAnnotations.initMocks(this); + when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext); + when(mDisplayController.getDisplay(anyInt())).thenReturn( + mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY)); mController = new TestAppPairsController( mTaskOrganizer, mSyncQueue, mDisplayController); mPool = mController.getPool(); - when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext); - when(mDisplayController.getDisplay(anyInt())).thenReturn( - mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY)); } @After diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java index 8ece913de53f..a3f134ee97ed 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java @@ -18,10 +18,14 @@ package com.android.wm.shell.apppairs; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.when; + import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.SyncTransactionQueue; @@ -35,7 +39,7 @@ import org.mockito.MockitoAnnotations; /** Tests for {@link AppPairsPool} */ @SmallTest @RunWith(AndroidJUnit4.class) -public class AppPairsPoolTests { +public class AppPairsPoolTests extends ShellTestCase { private TestAppPairsController mController; private TestAppPairsPool mPool; @Mock private SyncTransactionQueue mSyncQueue; @@ -45,6 +49,7 @@ public class AppPairsPoolTests { @Before public void setUp() { MockitoAnnotations.initMocks(this); + when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext); mController = new TestAppPairsController( mTaskOrganizer, mSyncQueue, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java new file mode 100644 index 000000000000..d87f4c60fad4 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.common.split; + +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import android.content.res.Configuration; +import android.graphics.Rect; +import android.view.SurfaceControl; + +import androidx.test.annotation.UiThreadTest; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.internal.policy.DividerSnapAlgorithm; +import com.android.wm.shell.ShellTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** Tests for {@link SplitLayout} */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class SplitLayoutTests extends ShellTestCase { + @Mock SplitLayout.LayoutChangeListener mLayoutChangeListener; + @Mock SurfaceControl mRootLeash; + private SplitLayout mSplitLayout; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mSplitLayout = new SplitLayout( + mContext, + getConfiguration(false), + mLayoutChangeListener, + mRootLeash); + } + + @Test + @UiThreadTest + public void testUpdateConfiguration() { + assertThat(mSplitLayout.updateConfiguration(getConfiguration(false))).isFalse(); + assertThat(mSplitLayout.updateConfiguration(getConfiguration(true))).isTrue(); + } + + @Test + public void testUpdateDividePosition() { + mSplitLayout.updateDividePosition(anyInt()); + verify(mLayoutChangeListener).onBoundsChanging(any(SplitLayout.class)); + } + + @Test + public void testSetSnapTarget() { + DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0, + DividerSnapAlgorithm.SnapTarget.FLAG_NONE); + mSplitLayout.setSnapTarget(snapTarget); + verify(mLayoutChangeListener).onBoundsChanged(any(SplitLayout.class)); + + // verify it callbacks properly when the snap target indicates dismissing split. + snapTarget = getSnapTarget(0, DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START); + mSplitLayout.setSnapTarget(snapTarget); + verify(mLayoutChangeListener).onSnappedToDismiss(eq(false)); + snapTarget = getSnapTarget(0, DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END); + mSplitLayout.setSnapTarget(snapTarget); + verify(mLayoutChangeListener).onSnappedToDismiss(eq(true)); + } + + private static Configuration getConfiguration(boolean isLandscape) { + final Configuration configuration = new Configuration(); + configuration.unset(); + configuration.orientation = isLandscape ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; + configuration.windowConfiguration.setBounds( + new Rect(0, 0, isLandscape ? 2160 : 1080, isLandscape ? 1080 : 2160)); + return configuration; + } + + private static DividerSnapAlgorithm.SnapTarget getSnapTarget(int position, int flag) { + return new DividerSnapAlgorithm.SnapTarget( + position /* position */, position /* taskPosition */, flag); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java new file mode 100644 index 000000000000..aa0eb2f95ed8 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.common.split; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.content.res.Configuration; +import android.graphics.Rect; +import android.view.SurfaceControl; + +import androidx.test.annotation.UiThreadTest; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.ShellTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** Tests for {@link SplitWindowManager} */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class SplitWindowManagerTests extends ShellTestCase { + @Mock SurfaceControl mSurfaceControl; + @Mock SplitLayout mSplitLayout; + private SplitWindowManager mSplitWindowManager; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + final Configuration configuration = new Configuration(); + configuration.setToDefaults(); + mSplitWindowManager = new SplitWindowManager(mContext, configuration, mSurfaceControl); + when(mSplitLayout.getDividerBounds()).thenReturn( + new Rect(0, 0, configuration.windowConfiguration.getBounds().width(), + configuration.windowConfiguration.getBounds().height())); + } + + @Test + @UiThreadTest + public void testInitRelease() { + mSplitWindowManager.init(mSplitLayout); + assertThat(mSplitWindowManager.getSurfaceControl()).isNotNull(); + mSplitWindowManager.release(); + assertThat(mSplitWindowManager.getSurfaceControl()).isNull(); + } +} diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 047b809bc766..604c4a1de8f9 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -1509,8 +1509,15 @@ public class LocationManager { } /** - * Removes location updates for the specified {@link LocationListener}. Following this call, - * the listener will not receive any more invocations of any kind. + * Removes all location updates for the specified {@link LocationListener}. The given listener + * is guaranteed not to receive any invocations that <b>happens-after</b> this method is + * invoked. + * + * <p>If the given listener has any batched requests, this method will not flush any incomplete + * location batches before stopping location updates. If you wish to flush any pending locations + * before stopping, you must first call {@link #requestFlush(String, LocationListener, int)} and + * then call this method once the flush is complete. If this method is invoked before the flush + * is complete, you may not receive the flushed locations. * * @param listener listener that no longer needs location updates * @@ -1537,6 +1544,8 @@ public class LocationManager { * Removes location updates for the specified {@link PendingIntent}. Following this call, the * PendingIntent will no longer receive location updates. * + * <p>See {@link #removeUpdates(LocationListener)} for more detail on how this method works. + * * @param pendingIntent pending intent that no longer needs location updates * * @throws IllegalArgumentException if pendingIntent is null diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 39bdf9557595..bd27f6d068bc 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -18,6 +18,7 @@ package android.media; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.annotation.TestApi; import android.bluetooth.BluetoothCodecConfig; import android.compat.annotation.UnsupportedAppUsage; @@ -1550,9 +1551,11 @@ public class AudioSystem /** @hide returns master balance value in range -1.f -> 1.f, where 0.f is dead center. */ @TestApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS) public static native float getMasterBalance(); /** @hide Changes the audio balance of the device. */ @TestApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS) public static native int setMasterBalance(float balance); // helpers for android.media.AudioManager.getProperty(), see description there for meaning diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index c2613716cd07..da14ee1f3212 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -60,6 +60,7 @@ import com.android.internal.util.FrameworkStatsLog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -296,8 +297,8 @@ public class Tuner implements AutoCloseable { private Executor mOnResourceLostListenerExecutor; private Integer mDemuxHandle; - private Map<Integer, Descrambler> mDescramblers = new HashMap<>(); - private List<Filter> mFilters = new ArrayList<>(); + private Map<Integer, WeakReference<Descrambler>> mDescramblers = new HashMap<>(); + private List<WeakReference<Filter>> mFilters = new ArrayList<WeakReference<Filter>>(); private final TunerResourceManager.ResourcesReclaimListener mResourceListener = new TunerResourceManager.ResourcesReclaimListener() { @@ -486,18 +487,28 @@ public class Tuner implements AutoCloseable { if (mLnb != null) { mLnb.close(); } - if (!mDescramblers.isEmpty()) { - for (Map.Entry<Integer, Descrambler> d : mDescramblers.entrySet()) { - d.getValue().close(); - mTunerResourceManager.releaseDescrambler(d.getKey(), mClientId); + synchronized (mDescramblers) { + if (!mDescramblers.isEmpty()) { + for (Map.Entry<Integer, WeakReference<Descrambler>> d : mDescramblers.entrySet()) { + Descrambler descrambler = d.getValue().get(); + if (descrambler != null) { + descrambler.close(); + } + mTunerResourceManager.releaseDescrambler(d.getKey(), mClientId); + } + mDescramblers.clear(); } - mDescramblers.clear(); } - if (!mFilters.isEmpty()) { - for (Filter f : mFilters) { - f.close(); + synchronized (mFilters) { + if (!mFilters.isEmpty()) { + for (WeakReference<Filter> weakFilter : mFilters) { + Filter filter = weakFilter.get(); + if (filter != null) { + filter.close(); + } + } + mFilters.clear(); } - mFilters.clear(); } if (mDemuxHandle != null) { int res = nativeCloseDemux(mDemuxHandle); @@ -1183,7 +1194,10 @@ public class Tuner implements AutoCloseable { if (mHandler == null) { mHandler = createEventHandler(); } - mFilters.add(filter); + synchronized (mFilters) { + WeakReference<Filter> weakFilter = new WeakReference<Filter>(filter); + mFilters.add(weakFilter); + } } return filter; } @@ -1351,7 +1365,10 @@ public class Tuner implements AutoCloseable { int handle = descramblerHandle[0]; Descrambler descrambler = nativeOpenDescramblerByHandle(handle); if (descrambler != null) { - mDescramblers.put(handle, descrambler); + synchronized (mDescramblers) { + WeakReference weakDescrambler = new WeakReference<Descrambler>(descrambler); + mDescramblers.put(handle, weakDescrambler); + } } else { mTunerResourceManager.releaseDescrambler(handle, mClientId); } diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java index af1ff01a3ebe..6dd41ffeed40 100644 --- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java +++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java @@ -28,7 +28,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * Frontend status. + * A Frontend Status class that contains the metrics of the active frontend. * * @hide */ @@ -461,7 +461,7 @@ public class FrontendStatus { } /** - * Lock status for Demod. + * Gets if the demod is currently locked or not. */ public boolean isDemodLocked() { if (mIsDemodLocked == null) { @@ -470,7 +470,7 @@ public class FrontendStatus { return mIsDemodLocked; } /** - * Gets Signal to Noise Ratio in thousandths of a deciBel (0.001dB). + * Gets the current Signal to Noise Ratio in thousandths of a deciBel (0.001dB). */ public int getSnr() { if (mSnr == null) { @@ -479,7 +479,7 @@ public class FrontendStatus { return mSnr; } /** - * Gets Bit Error Ratio. + * Gets the current Bit Error Ratio. * * <p>The number of error bit per 1 billion bits. */ @@ -491,7 +491,7 @@ public class FrontendStatus { } /** - * Gets Packages Error Ratio. + * Gets the current Packages Error Ratio. * * <p>The number of error package per 1 billion packages. */ @@ -502,7 +502,7 @@ public class FrontendStatus { return mPer; } /** - * Gets Bit Error Ratio before Forward Error Correction (FEC). + * Gets the current Bit Error Ratio before Forward Error Correction (FEC). * * <p>The number of error bit per 1 billion bits before FEC. */ @@ -513,7 +513,7 @@ public class FrontendStatus { return mPerBer; } /** - * Gets Signal Quality in percent. + * Gets the current Signal Quality in percent. */ public int getSignalQuality() { if (mSignalQuality == null) { @@ -522,7 +522,7 @@ public class FrontendStatus { return mSignalQuality; } /** - * Gets Signal Strength in thousandths of a dBm (0.001dBm). + * Gets the current Signal Strength in thousandths of a dBm (0.001dBm). */ public int getSignalStrength() { if (mSignalStrength == null) { @@ -531,7 +531,7 @@ public class FrontendStatus { return mSignalStrength; } /** - * Gets symbol rate in symbols per second. + * Gets the current symbol rate in symbols per second. */ public int getSymbolRate() { if (mSymbolRate == null) { @@ -540,7 +540,7 @@ public class FrontendStatus { return mSymbolRate; } /** - * Gets Inner Forward Error Correction type as specified in ETSI EN 300 468 V1.15.1 + * Gets the current Inner Forward Error Correction type as specified in ETSI EN 300 468 V1.15.1 * and ETSI EN 302 307-2 V1.1.1. */ @FrontendSettings.InnerFec @@ -551,7 +551,7 @@ public class FrontendStatus { return mInnerFec; } /** - * Gets modulation. + * Gets the currently configured modulation. */ @FrontendModulation public int getModulation() { @@ -561,7 +561,7 @@ public class FrontendStatus { return mModulation; } /** - * Gets Spectral Inversion for DVBC. + * Gets the currently configured Spectral Inversion for DVBC. */ @FrontendSettings.FrontendSpectralInversion public int getSpectralInversion() { @@ -571,7 +571,7 @@ public class FrontendStatus { return mInversion; } /** - * Gets Power Voltage Type for LNB. + * Gets the current Power Voltage Type for LNB. */ @Lnb.Voltage public int getLnbVoltage() { @@ -581,7 +581,7 @@ public class FrontendStatus { return mLnbVoltage; } /** - * Gets Physical Layer Pipe ID. + * Gets the current Physical Layer Pipe ID. */ public int getPlpId() { if (mPlpId == null) { @@ -599,7 +599,7 @@ public class FrontendStatus { return mIsEwbs; } /** - * Gets Automatic Gain Control value which is normalized from 0 to 255. + * Gets the current Automatic Gain Control value which is normalized from 0 to 255. */ public int getAgc() { if (mAgc == null) { @@ -617,7 +617,7 @@ public class FrontendStatus { return mIsLnaOn; } /** - * Gets Error status by layer. + * Gets the current Error information by layer. */ @NonNull public boolean[] getLayerErrors() { @@ -627,7 +627,7 @@ public class FrontendStatus { return mIsLayerErrors; } /** - * Gets Modulation Error Ratio in thousandths of a deciBel (0.001dB). + * Gets the current Modulation Error Ratio in thousandths of a deciBel (0.001dB). */ public int getMer() { if (mMer == null) { @@ -636,7 +636,7 @@ public class FrontendStatus { return mMer; } /** - * Gets frequency difference in Hz. + * Gets the current frequency difference in Hz. * * <p>Difference between tuning frequency and actual locked frequency. */ @@ -647,7 +647,7 @@ public class FrontendStatus { return mFreqOffset; } /** - * Gets hierarchy Type for DVBT. + * Gets the current hierarchy Type for DVBT. */ @DvbtFrontendSettings.Hierarchy public int getHierarchy() { @@ -657,7 +657,7 @@ public class FrontendStatus { return mHierarchy; } /** - * Gets lock status for RF. + * Gets if the RF is locked or not. */ public boolean isRfLocked() { if (mIsRfLocked == null) { @@ -666,7 +666,7 @@ public class FrontendStatus { return mIsRfLocked; } /** - * Gets an array of PLP status for tuned PLPs for ATSC3 frontend. + * Gets an array of the current tuned PLPs information of ATSC3 frontend. */ @NonNull public Atsc3PlpTuningInfo[] getAtsc3PlpTuningInfo() { @@ -677,9 +677,9 @@ public class FrontendStatus { } /** - * Gets an array of extended bit error ratio status. + * Gets an array of the current extended bit error ratio. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ @NonNull @@ -693,10 +693,9 @@ public class FrontendStatus { } /** - * Gets an array of code rates status. The {@link FrontendSettings.InnerFec} would be used to - * show the code rate. + * Gets an array of the current code rates. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ @NonNull @@ -711,9 +710,9 @@ public class FrontendStatus { } /** - * Gets bandwidth status. + * Gets the current bandwidth information. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ @FrontendBandwidth @@ -727,9 +726,9 @@ public class FrontendStatus { } /** - * Gets guard interval status. + * Gets the current guard interval information. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ @FrontendGuardInterval @@ -743,9 +742,9 @@ public class FrontendStatus { } /** - * Gets tansmission mode status. + * Gets the current transmission mode information. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ @FrontendTransmissionMode @@ -759,10 +758,10 @@ public class FrontendStatus { } /** - * Gets the Uncorrectable Error Counts of the frontend's Physical Layer Pipe (PLP) since the - * last tune operation. + * Gets the current Uncorrectable Error Counts of the frontend's Physical Layer Pipe (PLP) + * since the last tune operation. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ public int getUec() { @@ -775,9 +774,9 @@ public class FrontendStatus { } /** - * Gets the current DVB-T2 system id status. + * Gets the current DVB-T2 system id. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ @IntRange(from = 0, to = 0xffff) @@ -791,10 +790,9 @@ public class FrontendStatus { } /** - * Gets an array of interleaving status. Array value should be within {@link - * FrontendInterleaveMode}. + * Gets an array of the current interleaving mode information. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ @NonNull @@ -809,9 +807,10 @@ public class FrontendStatus { } /** - * Gets an array of the segments status in ISDB-T Specification of all the channels. + * Gets an array of the current segments information in ISDB-T Specification of all the + * channels. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ @NonNull @@ -828,7 +827,7 @@ public class FrontendStatus { /** * Gets an array of the Transport Stream Data Rate in BPS of the current channel. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ @NonNull @@ -842,13 +841,13 @@ public class FrontendStatus { } /** - * Gets an array of the extended modulations status. Array value should be withink {@link - * FrontendModulation}. + * Gets an array of the current extended modulations information. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ @NonNull + @FrontendModulation public int[] getExtendedModulations() { TunerVersionChecker.checkHigherOrEqualVersionTo( TunerVersionChecker.TUNER_VERSION_1_1, "getExtendedModulations status"); @@ -859,9 +858,9 @@ public class FrontendStatus { } /** - * Gets roll off status. + * Gets the current roll off information. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ @FrontendRollOff @@ -875,9 +874,9 @@ public class FrontendStatus { } /** - * Gets is MISO enabled status. + * Gets is MISO enabled or not. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ public boolean isMisoEnabled() { @@ -890,9 +889,9 @@ public class FrontendStatus { } /** - * Gets is the Code Rate of the frontend is linear or not status. + * Gets is the Code Rate of the frontend is linear or not. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ public boolean isLinear() { @@ -905,9 +904,9 @@ public class FrontendStatus { } /** - * Gets is the Short Frames enabled or not status. + * Gets is the Short Frames enabled or not. * - * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * <p>This query is only supported by Tuner HAL 1.1 or higher. Use * {@link TunerVersionChecker.getTunerVersion()} to check the version. */ public boolean isShortFramesEnabled() { @@ -920,7 +919,7 @@ public class FrontendStatus { } /** - * Status for each tuning Physical Layer Pipes. + * Information of each tuning Physical Layer Pipes. */ public static class Atsc3PlpTuningInfo { private final int mPlpId; diff --git a/native/android/Android.bp b/native/android/Android.bp index 7793d6c45d5e..6db4da95f6db 100644 --- a/native/android/Android.bp +++ b/native/android/Android.bp @@ -36,6 +36,7 @@ cc_library_shared { defaults: ["libandroid_defaults"], srcs: [ + "activity_manager.cpp", "asset_manager.cpp", "choreographer.cpp", "configuration.cpp", @@ -95,6 +96,10 @@ cc_library_shared { include_dirs: ["bionic/libc/dns/include"], + local_include_dirs: [ "include_platform", ], + + export_include_dirs: [ "include_platform", ], + version_script: "libandroid.map.txt", stubs: { symbol_file: "libandroid.map.txt", diff --git a/native/android/activity_manager.cpp b/native/android/activity_manager.cpp new file mode 100644 index 000000000000..82f4569b871e --- /dev/null +++ b/native/android/activity_manager.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_NDEBUG 0 +#define LOG_TAG "AActivityManager" +#include <utils/Log.h> + +#include <android/activity_manager.h> +#include <binder/ActivityManager.h> + +namespace android { +namespace activitymanager { + +// Global instance of ActivityManager, service is obtained only on first use. +static ActivityManager gAm; +// String tag used with ActivityManager. +static const String16& getTag() { + static String16 tag("libandroid"); + return tag; +} + +struct UidObserver : public BnUidObserver, public virtual IBinder::DeathRecipient { + explicit UidObserver(const AActivityManager_onUidImportance& cb, + int32_t cutpoint, void* cookie) + : mCallback(cb), mImportanceCutpoint(cutpoint), mCookie(cookie), mRegistered(false) {} + bool registerSelf(); + void unregisterSelf(); + + // IUidObserver + void onUidGone(uid_t uid, bool disabled) override; + void onUidActive(uid_t uid) override; + void onUidIdle(uid_t uid, bool disabled) override; + void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq, + int32_t capability) override; + + // IBinder::DeathRecipient implementation + void binderDied(const wp<IBinder>& who) override; + + static int32_t procStateToImportance(int32_t procState); + static int32_t importanceToProcState(int32_t importance); + + AActivityManager_onUidImportance mCallback; + int32_t mImportanceCutpoint; + void* mCookie; + std::mutex mRegisteredLock; + bool mRegistered GUARDED_BY(mRegisteredLock); +}; + +//static +int32_t UidObserver::procStateToImportance(int32_t procState) { + // TODO: remove this after adding Importance to onUidStateChanged callback. + if (procState == ActivityManager::PROCESS_STATE_NONEXISTENT) { + return AACTIVITYMANAGER_IMPORTANCE_GONE; + } else if (procState >= ActivityManager::PROCESS_STATE_HOME) { + return AACTIVITYMANAGER_IMPORTANCE_CACHED; + } else if (procState == ActivityManager::PROCESS_STATE_HEAVY_WEIGHT) { + return AACTIVITYMANAGER_IMPORTANCE_CANT_SAVE_STATE; + } else if (procState >= ActivityManager::PROCESS_STATE_TOP_SLEEPING) { + return AACTIVITYMANAGER_IMPORTANCE_TOP_SLEEPING; + } else if (procState >= ActivityManager::PROCESS_STATE_SERVICE) { + return AACTIVITYMANAGER_IMPORTANCE_SERVICE; + } else if (procState >= ActivityManager::PROCESS_STATE_TRANSIENT_BACKGROUND) { + return AACTIVITYMANAGER_IMPORTANCE_PERCEPTIBLE; + } else if (procState >= ActivityManager::PROCESS_STATE_IMPORTANT_FOREGROUND) { + return AACTIVITYMANAGER_IMPORTANCE_VISIBLE; + } else if (procState >= ActivityManager::PROCESS_STATE_FOREGROUND_SERVICE) { + return AACTIVITYMANAGER_IMPORTANCE_FOREGROUND_SERVICE; + } else { + return AACTIVITYMANAGER_IMPORTANCE_FOREGROUND; + } +} + +//static +int32_t UidObserver::importanceToProcState(int32_t importance) { + // TODO: remove this after adding Importance to onUidStateChanged callback. + if (importance == AACTIVITYMANAGER_IMPORTANCE_GONE) { + return ActivityManager::PROCESS_STATE_NONEXISTENT; + } else if (importance >= AACTIVITYMANAGER_IMPORTANCE_CACHED) { + return ActivityManager::PROCESS_STATE_HOME; + } else if (importance >= AACTIVITYMANAGER_IMPORTANCE_CANT_SAVE_STATE) { + return ActivityManager::PROCESS_STATE_HEAVY_WEIGHT; + } else if (importance >= AACTIVITYMANAGER_IMPORTANCE_TOP_SLEEPING) { + return ActivityManager::PROCESS_STATE_TOP_SLEEPING; + } else if (importance >= AACTIVITYMANAGER_IMPORTANCE_SERVICE) { + return ActivityManager::PROCESS_STATE_SERVICE; + } else if (importance >= AACTIVITYMANAGER_IMPORTANCE_PERCEPTIBLE) { + return ActivityManager::PROCESS_STATE_TRANSIENT_BACKGROUND; + } else if (importance >= AACTIVITYMANAGER_IMPORTANCE_VISIBLE) { + return ActivityManager::PROCESS_STATE_IMPORTANT_FOREGROUND; + } else if (importance >= AACTIVITYMANAGER_IMPORTANCE_FOREGROUND_SERVICE) { + return ActivityManager::PROCESS_STATE_FOREGROUND_SERVICE; + } else { + return ActivityManager::PROCESS_STATE_TOP; + } +} + + +void UidObserver::onUidGone(uid_t uid, bool disabled __unused) { + std::scoped_lock lock{mRegisteredLock}; + + if (mRegistered && mCallback) { + mCallback(uid, AACTIVITYMANAGER_IMPORTANCE_GONE, mCookie); + } +} + +void UidObserver::onUidActive(uid_t uid __unused) {} + +void UidObserver::onUidIdle(uid_t uid __unused, bool disabled __unused) {} + +void UidObserver::onUidStateChanged(uid_t uid, int32_t procState, + int64_t procStateSeq __unused, + int32_t capability __unused) { + std::scoped_lock lock{mRegisteredLock}; + + if (mRegistered && mCallback) { + mCallback(uid, procStateToImportance(procState), mCookie); + } +} + +void UidObserver::binderDied(const wp<IBinder>& /*who*/) { + // ActivityManager is dead, try to re-register. + { + std::scoped_lock lock{mRegisteredLock}; + // If client already unregistered, don't try to re-register. + if (!mRegistered) { + return; + } + // Clear mRegistered to re-register. + mRegistered = false; + } + registerSelf(); +} + +bool UidObserver::registerSelf() { + std::scoped_lock lock{mRegisteredLock}; + if (mRegistered) { + return true; + } + + status_t res = gAm.linkToDeath(this); + if (res != OK) { + ALOGE("UidObserver: Failed to linkToDeath with ActivityManager (err %d)", res); + return false; + } + + // TODO: it seems only way to get all changes is to set cutoff to PROCESS_STATE_UNKNOWN. + // But there is no equivalent of PROCESS_STATE_UNKNOWN in the UidImportance. + // If mImportanceCutpoint is < 0, use PROCESS_STATE_UNKNOWN instead. + res = gAm.registerUidObserver( + this, + ActivityManager::UID_OBSERVER_GONE | ActivityManager::UID_OBSERVER_PROCSTATE, + (mImportanceCutpoint < 0) ? ActivityManager::PROCESS_STATE_UNKNOWN + : importanceToProcState(mImportanceCutpoint), + getTag()); + if (res != OK) { + ALOGE("UidObserver: Failed to register with ActivityManager (err %d)", res); + gAm.unlinkToDeath(this); + return false; + } + + mRegistered = true; + ALOGV("UidObserver: Registered with ActivityManager"); + return true; +} + +void UidObserver::unregisterSelf() { + std::scoped_lock lock{mRegisteredLock}; + + if (mRegistered) { + gAm.unregisterUidObserver(this); + gAm.unlinkToDeath(this); + mRegistered = false; + } + + ALOGV("UidObserver: Unregistered with ActivityManager"); +} + +} // activitymanager +} // android + +using namespace android; +using namespace activitymanager; + +struct AActivityManager_UidImportanceListener : public UidObserver { +}; + +AActivityManager_UidImportanceListener* AActivityManager_addUidImportanceListener( + AActivityManager_onUidImportance onUidImportance, int32_t importanceCutpoint, void* cookie) { + sp<UidObserver> observer(new UidObserver(onUidImportance, importanceCutpoint, cookie)); + if (observer == nullptr || !observer->registerSelf()) { + return nullptr; + } + observer->incStrong((void *)AActivityManager_addUidImportanceListener); + return static_cast<AActivityManager_UidImportanceListener*>(observer.get()); +} + +void AActivityManager_removeUidImportanceListener( + AActivityManager_UidImportanceListener* listener) { + if (listener != nullptr) { + UidObserver* observer = static_cast<UidObserver*>(listener); + observer->unregisterSelf(); + observer->decStrong((void *)AActivityManager_addUidImportanceListener); + } +} + +bool AActivityManager_isUidActive(uid_t uid) { + return gAm.isUidActive(uid, getTag()); +} + +int32_t AActivityManager_getUidImportance(uid_t uid) { + return UidObserver::procStateToImportance(gAm.getUidProcessState(uid, getTag())); +} + diff --git a/native/android/include_platform/android/activity_manager.h b/native/android/include_platform/android/activity_manager.h new file mode 100644 index 000000000000..0ecd56d512a2 --- /dev/null +++ b/native/android/include_platform/android/activity_manager.h @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __AACTIVITYMANAGER_H__ +#define __AACTIVITYMANAGER_H__ + +#include <sys/cdefs.h> +#include <sys/types.h> + +__BEGIN_DECLS + +struct AActivityManager_UidImportanceListener; +typedef struct AActivityManager_UidImportanceListener AActivityManager_UidImportanceListener; + +/** + * Callback interface when Uid Importance has changed for a uid. + * + * This callback will be called on an arbitrary thread. Calls to a given listener will be + * serialized. + * + * @param uid the uid for which the importance has changed. + * @param uidImportance the new uidImportance for the uid. + * @cookie the same cookie when the UidImportanceListener was added. + * + * Introduced in API 31. + */ +typedef void (*AActivityManager_onUidImportance)(uid_t uid, int32_t uidImportance, void* cookie); + +/** + * ActivityManager Uid Importance constants. + * + * Introduced in API 31. + */ +enum { + /** + * Constant for Uid Importance: This process is running the + * foreground UI; that is, it is the thing currently at the top of the screen + * that the user is interacting with. + */ + AACTIVITYMANAGER_IMPORTANCE_FOREGROUND = 100, + + /** + * Constant for Uid Importance: This process is running a foreground + * service, for example to perform music playback even while the user is + * not immediately in the app. This generally indicates that the process + * is doing something the user actively cares about. + */ + AACTIVITYMANAGER_IMPORTANCE_FOREGROUND_SERVICE = 125, + + /** + * Constant for Uid Importance: This process is running something + * that is actively visible to the user, though not in the immediate + * foreground. This may be running a window that is behind the current + * foreground (so paused and with its state saved, not interacting with + * the user, but visible to them to some degree); it may also be running + * other services under the system's control that it inconsiders important. + */ + AACTIVITYMANAGER_IMPORTANCE_VISIBLE = 200, + + /** + * Constant for Uid Importance: This process is not something the user + * is directly aware of, but is otherwise perceptible to them to some degree. + */ + AACTIVITYMANAGER_IMPORTANCE_PERCEPTIBLE = 230, + + /** + * Constant for Uid Importance: This process contains services + * that should remain running. These are background services apps have + * started, not something the user is aware of, so they may be killed by + * the system relatively freely (though it is generally desired that they + * stay running as long as they want to). + */ + AACTIVITYMANAGER_IMPORTANCE_SERVICE = 300, + + /** + * Constant for Uid Importance: This process is running the foreground + * UI, but the device is asleep so it is not visible to the user. Though the + * system will try hard to keep its process from being killed, in all other + * ways we consider it a kind of cached process, with the limitations that go + * along with that state: network access, running background services, etc. + */ + AACTIVITYMANAGER_IMPORTANCE_TOP_SLEEPING = 325, + + /** + * Constant for Uid Importance: This process is running an + * application that can not save its state, and thus can't be killed + * while in the background. This will be used with apps that have + * {@link android.R.attr#cantSaveState} set on their application tag. + */ + AACTIVITYMANAGER_IMPORTANCE_CANT_SAVE_STATE = 350, + + /** + * Constant for Uid Importance: This process process contains + * cached code that is expendable, not actively running any app components + * we care about. + */ + AACTIVITYMANAGER_IMPORTANCE_CACHED = 400, + + /** + * Constant for Uid Importance: This process does not exist. + */ + AACTIVITYMANAGER_IMPORTANCE_GONE = 1000, +}; + +#if __ANDROID_API__ >= 31 + +/** + * Adds a UidImportanceListener to the ActivityManager. + * + * This API requires android.Manifest.permission.PACKAGE_USAGE_STATS permission. + * + * @param onUidImportance the listener callback that will receive change reports. + * + * @param importanceCutpoint the level of importance in which the caller is interested + * in differences. For example, if AACTIVITYMANAGER_IMPORTANCE_PERCEPTIBLE is used + * here, you will receive a call each time a uid's importance transitions between being + * <= AACTIVITYMANAGER_IMPORTANCE_PERCEPTIBLE and > AACTIVITYMANAGER_IMPORTANCE_PERCEPTIBLE. + * + * @param cookie a cookie that will be passed back to the listener callback. + * + * @return an opaque pointer of AActivityManager_UidImportanceListener, or nullptr + * upon failure. Upon success, the returned AActivityManager_UidImportanceListener pointer + * must be removed and released through AActivityManager_removeUidImportanceListener. + */ +AActivityManager_UidImportanceListener* AActivityManager_addUidImportanceListener( + AActivityManager_onUidImportance onUidImportance, + int32_t importanceCutpoint, + void* cookie) __INTRODUCED_IN(31); + +/** + * Removes a UidImportanceListener that was added with AActivityManager_addUidImportanceListener. + * + * When this returns, it's guaranteed the listener callback will no longer be invoked. + * + * @param listener the UidImportanceListener to be removed. + */ +void AActivityManager_removeUidImportanceListener( + AActivityManager_UidImportanceListener* listener) __INTRODUCED_IN(31); + +/** + * Queries if a uid is currently active. + * + * This API requires android.Manifest.permission.PACKAGE_USAGE_STATS permission. + * + * @return true if the uid is active, false otherwise. + */ +bool AActivityManager_isUidActive(uid_t uid) __INTRODUCED_IN(31); + +/** + * Queries the current Uid Importance value of a uid. + * + * This API requires android.Manifest.permission.PACKAGE_USAGE_STATS permission. + * + * @param uid the uid for which the importance value is queried. + * @return the current uid importance value for uid. + */ +int32_t AActivityManager_getUidImportance(uid_t uid) __INTRODUCED_IN(31); + +#endif // __ANDROID_API__ >= 31 + +__END_DECLS + +#endif // __AACTIVITYMANAGER_H__ diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index eca67bd7d211..8fa3acf502bc 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -1,5 +1,9 @@ LIBANDROID { global: + AActivityManager_addUidImportanceListener; # apex # introduced=31 + AActivityManager_removeUidImportanceListener; # apex # introduced=31 + AActivityManager_isUidActive; # apex # introduced=31 + AActivityManager_getUidImportance; # apex # introduced=31 AAssetDir_close; AAssetDir_getNextFileName; AAssetDir_rewind; diff --git a/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp b/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp new file mode 100644 index 000000000000..1a51218616d2 --- /dev/null +++ b/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp @@ -0,0 +1,11 @@ +android_test_helper_app { + name: "UidImportanceHelperApp", + manifest: "HelperAppManifest.xml", + static_libs: ["androidx.test.rules"], + sdk_version: "test_current", + srcs: ["src/**/*.java"], + test_suites: [ + "general-tests", + ], +} + diff --git a/native/android/tests/activitymanager/UidImportanceHelperApps/HelperAppManifest.xml b/native/android/tests/activitymanager/UidImportanceHelperApps/HelperAppManifest.xml new file mode 100644 index 000000000000..3583b33756e9 --- /dev/null +++ b/native/android/tests/activitymanager/UidImportanceHelperApps/HelperAppManifest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.tests.UidImportanceHelper" + android:versionCode="1" + android:versionName="1.0" > + + <application android:label="UidImportanceHelper"> + <activity android:name="com.android.tests.UidImportanceHelper.MainActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.DEFAULT"/> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> + diff --git a/native/android/tests/activitymanager/UidImportanceHelperApps/src/com/android/tests/UidImportanceHelper/MainActivity.java b/native/android/tests/activitymanager/UidImportanceHelperApps/src/com/android/tests/UidImportanceHelper/MainActivity.java new file mode 100644 index 000000000000..db0f2b5ed967 --- /dev/null +++ b/native/android/tests/activitymanager/UidImportanceHelperApps/src/com/android/tests/UidImportanceHelper/MainActivity.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.tests.UidImportanceHelper; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; + +/** + * This is an empty activity for testing the UID policy of media transcoding service. + */ +public class MainActivity extends Activity { + private static final String TAG = "MainActivity"; + + // Called at the start of the full lifetime. + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // Initialize Activity and inflate the UI. + } + + // Called after onCreate has finished, use to restore UI state + @Override + public void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + // Restore UI state from the savedInstanceState. + // This bundle has also been passed to onCreate. + // Will only be called if the Activity has been + // killed by the system since it was last visible. + } + + // Called before subsequent visible lifetimes + // for an activity process. + @Override + public void onRestart() { + super.onRestart(); + // Load changes knowing that the Activity has already + // been visible within this process. + } + + // Called at the start of the visible lifetime. + @Override + public void onStart() { + super.onStart(); + // Apply any required UI change now that the Activity is visible. + } + + // Called at the start of the active lifetime. + @Override + public void onResume() { + super.onResume(); + // Resume any paused UI updates, threads, or processes required + // by the Activity but suspended when it was inactive. + } + + // Called to save UI state changes at the + // end of the active lifecycle. + @Override + public void onSaveInstanceState(Bundle savedInstanceState) { + // Save UI state changes to the savedInstanceState. + // This bundle will be passed to onCreate and + // onRestoreInstanceState if the process is + // killed and restarted by the run time. + super.onSaveInstanceState(savedInstanceState); + } + + // Called at the end of the active lifetime. + @Override + public void onPause() { + // Suspend UI updates, threads, or CPU intensive processes + // that don't need to be updated when the Activity isn't + // the active foreground Activity. + super.onPause(); + } + + // Called at the end of the visible lifetime. + @Override + public void onStop() { + // Suspend remaining UI updates, threads, or processing + // that aren't required when the Activity isn't visible. + // Persist all edits or state changes + // as after this call the process is likely to be killed. + super.onStop(); + } + + // Sometimes called at the end of the full lifetime. + @Override + public void onDestroy() { + // Clean up any resources including ending threads, + // closing database connections etc. + super.onDestroy(); + } +} diff --git a/native/android/tests/activitymanager/nativeTests/Android.bp b/native/android/tests/activitymanager/nativeTests/Android.bp new file mode 100644 index 000000000000..d4b5015ad8f3 --- /dev/null +++ b/native/android/tests/activitymanager/nativeTests/Android.bp @@ -0,0 +1,39 @@ +cc_test { + name: "ActivityManagerNativeTestCases", + + multilib: { + lib32: { + suffix: "32", + }, + lib64: { + suffix: "64", + }, + }, + + srcs: ["src/ActivityManagerNativeTest.cpp"], + + shared_libs: [ + "liblog", + "libutils", + "libandroid", + "libbinder", + ], + + static_libs: [ + "libbase", + "libgtest", + ], + stl: "c++_shared", + + test_suites: [ + "general-tests", + ], + + cflags: [ + "-Werror", + "-Wall", + ], + required: [ + "UidImportanceHelperApp", + ], +} diff --git a/native/android/tests/activitymanager/nativeTests/AndroidTest.xml b/native/android/tests/activitymanager/nativeTests/AndroidTest.xml new file mode 100644 index 000000000000..bf6287ad4883 --- /dev/null +++ b/native/android/tests/activitymanager/nativeTests/AndroidTest.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Config for ActivityManager native test cases"> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-native" /> + <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> + <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> + <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> + + <!-- Force root to allow registering UidImportanceListener from native test --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"> + <option name="force-root" value="true" /> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="input keyevent KEYCODE_WAKEUP" /> + <option name="run-command" value="wm dismiss-keyguard" /> + </target_preparer> + <!-- Install the helper apk --> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="UidImportanceHelperApp.apk" /> + </target_preparer> + + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher"> + <option name="cleanup" value="true" /> + <option name="push" value="ActivityManagerNativeTestCases->/data/local/tmp/ActivityManagerNativeTestCases" /> + <option name="append-bitness" value="true" /> + </target_preparer> + + <test class="com.android.tradefed.testtype.GTest" > + <option name="native-test-device-path" value="/data/local/tmp" /> + <option name="module-name" value="ActivityManagerNativeTestCases" /> + <option name="runtime-hint" value="1m" /> + </test> + + <!-- Controller that will skip the module if a native bridge situation is detected --> + <!-- For example: module wants to run arm and device is x86 --> + <object type="module_controller" class="com.android.tradefed.testtype.suite.module.NativeBridgeModuleController" /> +</configuration> diff --git a/native/android/tests/activitymanager/nativeTests/src/ActivityManagerNativeTest.cpp b/native/android/tests/activitymanager/nativeTests/src/ActivityManagerNativeTest.cpp new file mode 100644 index 000000000000..75ba0ffb229a --- /dev/null +++ b/native/android/tests/activitymanager/nativeTests/src/ActivityManagerNativeTest.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "ActivityManagerNativeTest" + +#include <android-base/logging.h> +#include <android/activity_manager.h> +#include <binder/PermissionController.h> +#include <binder/ProcessState.h> +#include <gtest/gtest.h> + +constexpr const char* kTestPackage = "com.android.tests.UidImportanceHelper"; +constexpr const char* kTestActivity = "com.android.tests.UidImportanceHelper.MainActivity"; +constexpr int64_t kEventTimeoutUs = 500000; + +//----------------------------------------------------------------- +class ActivityManagerNativeTest : public ::testing::Test { +protected: + ActivityManagerNativeTest() : mUidObserver(nullptr), mTestAppUid(-1), mLastUidImportance(-1) {} + + virtual ~ActivityManagerNativeTest() {} + + /* Test setup*/ + virtual void SetUp() { android::ProcessState::self()->startThreadPool(); } + + /* Test tear down */ + virtual void TearDown() {} + + bool waitForImportance(int32_t val, int64_t timeoutUs) { + std::unique_lock lock(mLock); + + if (mLastUidImportance != val && timeoutUs > 0) { + mCondition.wait_for(lock, std::chrono::microseconds(timeoutUs)); + } + + return mLastUidImportance == val; + } + + void onUidImportanceChanged(uid_t uid, int32_t uidImportance) { + LOG(ERROR) << "OnUidImportance: uid " << uid << ", importance " << uidImportance; + std::unique_lock lock(mLock); + + if (uid == mTestAppUid) { + mLastUidImportance = uidImportance; + mCondition.notify_one(); + } + } + + static void OnUidImportance(uid_t uid, int32_t uidImportance, void* cookie) { + ActivityManagerNativeTest* owner = reinterpret_cast<ActivityManagerNativeTest*>(cookie); + owner->onUidImportanceChanged(uid, uidImportance); + } + + AActivityManager_UidImportanceListener* mUidObserver; + uid_t mTestAppUid; + std::mutex mLock; + std::condition_variable mCondition; + int32_t mLastUidImportance; +}; + +static bool getUidForPackage(const char* packageName, /*inout*/ uid_t& uid) { + android::PermissionController pc; + uid = pc.getPackageUid(android::String16(packageName), 0); + if (uid <= 0) { + ALOGE("Unknown package: '%s'", packageName); + return false; + } + return true; +} + +struct ShellHelper { + static bool RunCmd(const std::string& cmdStr) { + int ret = system(cmdStr.c_str()); + if (ret != 0) { + LOG(ERROR) << "Failed to run cmd: " << cmdStr << ", exitcode " << ret; + return false; + } + return true; + } + + static bool Start(const char* packageName, const char* activityName) { + return RunCmd("am start -W " + std::string(packageName) + "/" + std::string(activityName) + + " &> /dev/null"); + } + + static bool Stop(const char* packageName) { + return RunCmd("am force-stop " + std::string(packageName)); + } +}; + +//------------------------------------------------------------------------------------------------- +TEST_F(ActivityManagerNativeTest, testUidImportance) { + pid_t selfPid = ::getpid(); + uid_t selfUid = ::getuid(); + + uid_t testAppUid; + EXPECT_TRUE(getUidForPackage(kTestPackage, testAppUid)); + LOG(INFO) << "testUidImportance: uidselfUid" << selfUid << ", selfPid " << selfPid + << ", testAppUid " << testAppUid; + mTestAppUid = testAppUid; + + // Expect the initial UidImportance to be GONE. + EXPECT_FALSE(AActivityManager_isUidActive(testAppUid)); + EXPECT_EQ(AActivityManager_getUidImportance(testAppUid), AACTIVITYMANAGER_IMPORTANCE_GONE); + + mUidObserver = AActivityManager_addUidImportanceListener(&OnUidImportance, + AACTIVITYMANAGER_IMPORTANCE_FOREGROUND, + (void*)this); + EXPECT_TRUE(mUidObserver != nullptr); + + // Start the test activity, and expect to receive UidImportance change to FOREGROUND. + EXPECT_TRUE(ShellHelper::Start(kTestPackage, kTestActivity)); + EXPECT_TRUE(waitForImportance(AACTIVITYMANAGER_IMPORTANCE_FOREGROUND, kEventTimeoutUs)); + EXPECT_TRUE(AActivityManager_isUidActive(testAppUid)); + EXPECT_EQ(AActivityManager_getUidImportance(testAppUid), + AACTIVITYMANAGER_IMPORTANCE_FOREGROUND); + + // Stop the test activity, and expect to receive UidImportance change to GONE. + EXPECT_TRUE(ShellHelper::Stop(kTestPackage)); + EXPECT_TRUE(waitForImportance(AACTIVITYMANAGER_IMPORTANCE_GONE, kEventTimeoutUs)); + EXPECT_FALSE(AActivityManager_isUidActive(testAppUid)); + EXPECT_EQ(AActivityManager_getUidImportance(testAppUid), AACTIVITYMANAGER_IMPORTANCE_GONE); + + AActivityManager_removeUidImportanceListener(mUidObserver); + mUidObserver = nullptr; +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index 3c4830272099..7eb921b9e6bb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -335,7 +335,7 @@ public class NotificationLockscreenUserManagerImpl implements } NotificationEntry visibleEntry = getEntryManager().getActiveNotificationUnfiltered(key); return isLockscreenPublicMode(mCurrentUserId) && visibleEntry != null - && visibleEntry.getRanking().getVisibilityOverride() == VISIBILITY_SECRET; + && visibleEntry.getRanking().getLockscreenVisibilityOverride() == VISIBILITY_SECRET; } public boolean shouldShowOnKeyguard(NotificationEntry entry) { @@ -513,7 +513,8 @@ public class NotificationLockscreenUserManagerImpl implements } NotificationEntry entry = getEntryManager().getActiveNotificationUnfiltered(key); return entry != null - && entry.getRanking().getVisibilityOverride() == Notification.VISIBILITY_PRIVATE; + && entry.getRanking().getLockscreenVisibilityOverride() + == Notification.VISIBILITY_PRIVATE; } private void updateCurrentProfilesCache() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java index b1c6f535ba87..23d5369833c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java @@ -120,7 +120,7 @@ public class KeyguardCoordinator implements Coordinator { // notifications to show in public mode if (mLockscreenUserManager.isLockscreenPublicMode(currUserId) || mLockscreenUserManager.isLockscreenPublicMode(notifUserId)) { - if (entry.getRanking().getVisibilityOverride() == VISIBILITY_SECRET) { + if (entry.getRanking().getLockscreenVisibilityOverride() == VISIBILITY_SECRET) { return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index 37d5da24a704..7c5d4a3efee7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -20,6 +20,7 @@ import static com.android.systemui.statusbar.notification.TransformState.TRANSFO import android.app.Notification; import android.content.Context; +import android.content.res.ColorStateList; import android.util.ArraySet; import android.view.NotificationHeaderView; import android.view.NotificationTopLineView; @@ -168,9 +169,11 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { public void applyConversationSkin() { if (mAppNameText != null) { + final ColorStateList colors = mAppNameText.getTextColors(); mAppNameText.setTextAppearance( com.android.internal.R.style .TextAppearance_DeviceDefault_Notification_Conversation_AppName); + mAppNameText.setTextColor(colors); MarginLayoutParams layoutParams = (MarginLayoutParams) mAppNameText.getLayoutParams(); layoutParams.setMarginStart(0); } @@ -189,11 +192,13 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { public void clearConversationSkin() { if (mAppNameText != null) { + final ColorStateList colors = mAppNameText.getTextColors(); final int textAppearance = Utils.getThemeAttr( mAppNameText.getContext(), com.android.internal.R.attr.notificationHeaderTextAppearance, com.android.internal.R.style.TextAppearance_DeviceDefault_Notification_Info); mAppNameText.setTextAppearance(textAppearance); + mAppNameText.setTextColor(colors); MarginLayoutParams layoutParams = (MarginLayoutParams) mAppNameText.getLayoutParams(); final int marginStart = mAppNameText.getResources().getDimensionPixelSize( com.android.internal.R.dimen.notification_header_app_name_margin_start); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index 9854f5450df1..4ca9c5db013c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -289,6 +289,8 @@ public class KeyguardBouncer { SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__HIDDEN); mDismissCallbackRegistry.notifyDismissCancelled(); } + mExpansion = EXPANSION_HIDDEN; + dispatchExpansionChanged(); mIsScrimmed = false; mFalsingCollector.onBouncerHidden(); mCallback.onBouncerVisiblityChanged(false /* shown */); @@ -377,6 +379,7 @@ public class KeyguardBouncer { */ public void setExpansion(float fraction) { float oldExpansion = mExpansion; + boolean expansionChanged = mExpansion != fraction; mExpansion = fraction; if (mKeyguardViewController != null && !mIsAnimatingAway) { mKeyguardViewController.setExpansion(fraction); @@ -394,6 +397,10 @@ public class KeyguardBouncer { mKeyguardViewController.onStartingToHide(); } } + + if (expansionChanged) { + dispatchExpansionChanged(); + } } public boolean willDismissWithAction() { @@ -518,6 +525,12 @@ public class KeyguardBouncer { } } + private void dispatchExpansionChanged() { + for (BouncerExpansionCallback callback : mExpansionCallbacks) { + callback.onExpansionChanged(mExpansion); + } + } + public void dump(PrintWriter pw) { pw.println("KeyguardBouncer"); pw.println(" isShowing(): " + isShowing()); @@ -534,6 +547,12 @@ public class KeyguardBouncer { void onStartingToHide(); void onStartingToShow(); void onFullyHidden(); + + /** + * From 0f {@link KeyguardBouncer#EXPANSION_VISIBLE} when fully visible + * to 1f {@link KeyguardBouncer#EXPANSION_HIDDEN} when fully hidden + */ + default void onExpansionChanged(float bouncerHideAmount) {} } /** Create a {@link KeyguardBouncer} once a container and bouncer callback are available. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java index 0e7e2fd8173c..547a3705266a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java @@ -23,6 +23,7 @@ import static com.android.systemui.statusbar.phone.LockIcon.STATE_LOCKED; import static com.android.systemui.statusbar.phone.LockIcon.STATE_LOCK_OPEN; import static com.android.systemui.statusbar.phone.LockIcon.STATE_SCANNING_FACE; +import android.animation.ArgbEvaluator; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; @@ -38,6 +39,7 @@ import com.android.internal.logging.nano.MetricsProto; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.settingslib.Utils; import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; @@ -84,7 +86,7 @@ public class LockscreenLockIconController { private boolean mDocked; private boolean mWakeAndUnlockRunning; private boolean mShowingLaunchAffordance; - private boolean mBouncerShowing; + private float mBouncerHiddenAmount = KeyguardBouncer.EXPANSION_HIDDEN; private boolean mBouncerShowingScrimmed; private boolean mFingerprintUnlock; private int mStatusBarState = StatusBarState.SHADE; @@ -104,6 +106,8 @@ public class LockscreenLockIconController { mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure(); mConfigurationListener.onThemeChanged(); + + updateColor(); update(); } @@ -348,7 +352,6 @@ public class LockscreenLockIconController { */ public void attach(LockIcon lockIcon) { mLockIcon = lockIcon; - updateColor(); mLockIcon.setOnClickListener(this::handleClick); mLockIcon.setOnLongClickListener(this::handleLongClick); @@ -408,20 +411,44 @@ public class LockscreenLockIconController { /** Sets whether the bouncer is showing. */ public void setBouncerShowingScrimmed(boolean showing, boolean scrimmed) { - mBouncerShowing = showing; mBouncerShowingScrimmed = scrimmed; update(); } + /** + * Sets how hidden the bouncer is, where 0f is fully visible and 1f is fully hidden + * See {@link KeyguardBouncer#EXPANSION_VISIBLE} and {@link KeyguardBouncer#EXPANSION_HIDDEN}. + */ + public void setBouncerHideAmount(float hideAmount) { + mBouncerHiddenAmount = hideAmount; + updateColor(); + } + private void updateColor() { if (mLockIcon == null) { return; } - TypedArray typedArray = mLockIcon.getContext().getTheme().obtainStyledAttributes( - null, new int[]{ android.R.attr.textColorPrimary }, 0, 0); - int iconColor = typedArray.getColor(0, Color.WHITE); - typedArray.recycle(); + int iconColor = -1; + if (mBouncerHiddenAmount == KeyguardBouncer.EXPANSION_VISIBLE) { + TypedArray typedArray = mLockIcon.getContext().getTheme().obtainStyledAttributes( + null, new int[]{ android.R.attr.textColorPrimary }, 0, 0); + iconColor = typedArray.getColor(0, Color.WHITE); + typedArray.recycle(); + } else if (mBouncerHiddenAmount == KeyguardBouncer.EXPANSION_HIDDEN) { + iconColor = Utils.getColorAttrDefaultColor( + mLockIcon.getContext(), com.android.systemui.R.attr.wallpaperTextColor); + } else { + // bouncer is transitioning + TypedArray typedArray = mLockIcon.getContext().getTheme().obtainStyledAttributes( + null, new int[]{ android.R.attr.textColorPrimary }, 0, 0); + int bouncerIconColor = typedArray.getColor(0, Color.WHITE); + typedArray.recycle(); + int keyguardIconColor = Utils.getColorAttrDefaultColor( + mLockIcon.getContext(), com.android.systemui.R.attr.wallpaperTextColor); + iconColor = (int) new ArgbEvaluator().evaluate( + mBouncerHiddenAmount, bouncerIconColor, keyguardIconColor); + } mLockIcon.updateColor(iconColor); } @@ -520,10 +547,7 @@ public class LockscreenLockIconController { return changed; } boolean onAodOrDocked = mStatusBarStateController.isDozing() || mDocked; - boolean onKeyguardWithoutBouncer = mStatusBarState == StatusBarState.KEYGUARD - && !mBouncerShowing; - boolean invisible = onAodOrDocked || mWakeAndUnlockRunning || mShowingLaunchAffordance - || onKeyguardWithoutBouncer; + boolean invisible = onAodOrDocked || mWakeAndUnlockRunning || mShowingLaunchAffordance; boolean fingerprintOrBypass = mFingerprintUnlock || mKeyguardBypassController.getBypassEnabled(); if (fingerprintOrBypass && !mBouncerShowingScrimmed) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 9e872ab65591..981f9a662deb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -3794,6 +3794,14 @@ public class StatusBar extends SystemUI implements DemoMode, } /** + * Sets how hidden the bouncer is, where 0f is fully visible and 1f is fully hidden + * See {@link KeyguardBouncer#EXPANSION_VISIBLE} and {@link KeyguardBouncer#EXPANSION_HIDDEN}. + */ + public void setBouncerHideAmount(float hideAmount) { + mLockscreenLockIconController.setBouncerHideAmount(hideAmount); + } + + /** * Collapses the notification shade if it is tracking or expanded. */ public void collapseShade() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index b912614ba3e8..055b78a2c000 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -130,6 +130,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb updateStates(); updateLockIcon(); } + + @Override + public void onExpansionChanged(float hideAmount) { + mStatusBar.setBouncerHideAmount(hideAmount); + } }; private final DockManager.DockEventListener mDockEventListener = new DockManager.DockEventListener() { 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 46ddc3b809a7..6c5251b02291 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.policy; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.Notification; @@ -75,6 +74,7 @@ import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewW import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.LightBarController; +import java.util.Collection; import java.util.HashMap; import java.util.function.Consumer; @@ -315,8 +315,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mRemoteInputs = remoteInputs; mRemoteInput = remoteInput; mEditText.setHint(mRemoteInput.getLabel()); - mEditText.mSupportedMimeTypes = (remoteInput.getAllowedDataTypes() == null) ? null - : remoteInput.getAllowedDataTypes().toArray(new String[0]); + mEditText.setSupportedMimeTypes(remoteInput.getAllowedDataTypes()); mEntry.editedSuggestionInfo = editedSuggestionInfo; if (editedSuggestionInfo != null) { @@ -570,12 +569,13 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene */ public static class RemoteEditText extends EditText { + private final OnReceiveContentListener mOnReceiveContentListener = this::onReceiveContent; + private final Drawable mBackground; private RemoteInputView mRemoteInputView; boolean mShowImeOnInputConnection; private LightBarController mLightBarController; UserHandle mUser; - private String[] mSupportedMimeTypes; public RemoteEditText(Context context, AttributeSet attrs) { super(context, attrs); @@ -583,39 +583,14 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mLightBarController = Dependency.get(LightBarController.class); } - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - if (mSupportedMimeTypes != null && mSupportedMimeTypes.length > 0) { - setOnReceiveContentListener(mSupportedMimeTypes, - new OnReceiveContentListener() { - @Override - @Nullable - public ContentInfo onReceiveContent(@NonNull View view, - @NonNull ContentInfo payload) { - Pair<ContentInfo, ContentInfo> split = payload.partition( - item -> item.getUri() != null); - ContentInfo uriContent = split.first; - ContentInfo remaining = split.second; - if (uriContent != null) { - ClipData clip = uriContent.getClip(); - ClipDescription description = clip.getDescription(); - if (clip.getItemCount() > 1 - || description.getMimeTypeCount() < 1 - || remaining != null) { - // TODO(b/172363500): Update to loop over all the items - return payload; - } - Uri contentUri = clip.getItemAt(0).getUri(); - String mimeType = description.getMimeType(0); - Intent dataIntent = mRemoteInputView - .prepareRemoteInputFromData(mimeType, contentUri); - mRemoteInputView.sendRemoteInput(dataIntent); - } - return remaining; - } - }); + void setSupportedMimeTypes(@Nullable Collection<String> mimeTypes) { + String[] types = null; + OnReceiveContentListener listener = null; + if (mimeTypes != null && !mimeTypes.isEmpty()) { + types = mimeTypes.toArray(new String[0]); + listener = mOnReceiveContentListener; } + setOnReceiveContentListener(types, listener); } private void defocusIfNeeded(boolean animate) { @@ -759,5 +734,28 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene setBackground(null); } } + + private ContentInfo onReceiveContent(View view, ContentInfo payload) { + Pair<ContentInfo, ContentInfo> split = + payload.partition(item -> item.getUri() != null); + ContentInfo uriItems = split.first; + ContentInfo remainingItems = split.second; + if (uriItems != null) { + ClipData clip = uriItems.getClip(); + ClipDescription description = clip.getDescription(); + if (clip.getItemCount() > 1 + || description.getMimeTypeCount() < 1 + || remainingItems != null) { + // TODO(b/172363500): Update to loop over all the items + return payload; + } + Uri contentUri = clip.getItemAt(0).getUri(); + String mimeType = description.getMimeType(0); + Intent dataIntent = + mRemoteInputView.prepareRemoteInputFromData(mimeType, contentUri); + mRemoteInputView.sendRemoteInput(dataIntent); + } + return remainingItems; + } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java index a0ae35ffef00..11150432f757 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java @@ -29,20 +29,23 @@ import static com.android.systemui.accessibility.MagnificationModeSwitch.FADING_ import static com.android.systemui.accessibility.MagnificationModeSwitch.getIconResId; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; import static org.hamcrest.CoreMatchers.hasItems; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.content.pm.ActivityInfo; +import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.testing.AndroidTestingRunner; @@ -60,15 +63,18 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.Captor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; +import java.util.List; + @SmallTest @RunWith(AndroidTestingRunner.class) public class MagnificationModeSwitchTest extends SysuiTestCase { @@ -81,11 +87,10 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { private AccessibilityManager mAccessibilityManager; @Mock private WindowManager mWindowManager; - @Mock private ViewPropertyAnimator mViewPropertyAnimator; private MagnificationModeSwitch mMagnificationModeSwitch; - @Captor - private ArgumentCaptor<View.OnTouchListener> mTouchListenerCaptor; + private View.OnTouchListener mTouchListener; + private List<MotionEvent> mMotionEvents = new ArrayList<>(); @Before public void setUp() throws Exception { @@ -97,9 +102,23 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager); mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager); mSpyImageView = Mockito.spy(new ImageView(mContext)); - resetMockImageViewAndAnimator(); - + mViewPropertyAnimator = Mockito.spy(mSpyImageView.animate()); + resetAndStubMockImageViewAndAnimator(); + doAnswer((invocation) -> { + mTouchListener = invocation.getArgument(0); + return null; + }).when(mSpyImageView).setOnTouchListener( + any(View.OnTouchListener.class)); mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mSpyImageView); + assertNotNull(mTouchListener); + } + + @After + public void tearDown() { + for (MotionEvent event:mMotionEvents) { + event.recycle(); + } + mMotionEvents.clear(); } @Test @@ -124,7 +143,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { } @Test - public void showMagnificationButton_a11yTimeout_autoFadeOut() { + public void showMagnificationButton_setA11yTimeout_postDelayedAnimationWithA11yTimeout() { final int a11yTimeout = 12345; when(mAccessibilityManager.getRecommendedTimeoutMillis(anyInt(), anyInt())).thenReturn( a11yTimeout); @@ -134,14 +153,21 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { verify(mAccessibilityManager).getRecommendedTimeoutMillis( DEFAULT_FADE_OUT_ANIMATION_DELAY_MS, AccessibilityManager.FLAG_CONTENT_ICONS | AccessibilityManager.FLAG_CONTENT_CONTROLS); - final ArgumentCaptor<Runnable> fadeOutCaptor = ArgumentCaptor.forClass(Runnable.class); - final ArgumentCaptor<Long> fadeOutDelay = ArgumentCaptor.forClass(Long.class); - verify(mSpyImageView).postOnAnimationDelayed(fadeOutCaptor.capture(), - fadeOutDelay.capture()); - assertEquals(a11yTimeout, (long) fadeOutDelay.getValue()); + verify(mSpyImageView).postOnAnimationDelayed(any(Runnable.class), eq((long) a11yTimeout)); + } + + @Test + public void showMagnificationButton_windowMode_verifyAnimationEndAction() { + // Execute the runnable immediately to run the animation. + doAnswer((invocation) -> { + final Runnable action = invocation.getArgument(0); + action.run(); + return null; + }).when(mSpyImageView).postOnAnimationDelayed(any(Runnable.class), anyLong()); + + mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); // Verify the end action after fade-out. - fadeOutCaptor.getValue().run(); final ArgumentCaptor<Runnable> endActionCaptor = ArgumentCaptor.forClass(Runnable.class); verify(mViewPropertyAnimator).withEndAction(endActionCaptor.capture()); @@ -154,7 +180,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { @Test public void onConfigurationChanged_buttonIsShowing_setImageResource() { mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - resetMockImageViewAndAnimator(); + resetAndStubMockImageViewAndAnimator(); mMagnificationModeSwitch.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY); @@ -165,42 +191,46 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { @Test public void performSingleTap_fullscreenMode_removeViewAndChangeSettingsValue() { mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - resetMockImageViewAndAnimator(); + resetAndStubMockImageViewAndAnimator(); // Perform a single-tap - final View.OnTouchListener listener = mTouchListenerCaptor.getValue(); - listener.onTouch(mSpyImageView, MotionEvent.obtain( - 0, 0, ACTION_DOWN, 100, 100, 0)); + final long downTime = SystemClock.uptimeMillis(); + mTouchListener.onTouch(mSpyImageView, + obtainMotionEvent(downTime, 0, ACTION_DOWN, 100, 100)); + verify(mViewPropertyAnimator).cancel(); - resetMockImageViewAndAnimator(); - listener.onTouch(mSpyImageView, MotionEvent.obtain( - 0, ViewConfiguration.getTapTimeout(), ACTION_UP, 100, 100, 0)); + resetAndStubMockImageViewAndAnimator(); + mTouchListener.onTouch(mSpyImageView, + obtainMotionEvent(downTime, downTime, ACTION_UP, 100, 100)); + verifyTapAction(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); } @Test - public void showMagnificationButton_performDragging_updateViewLayout() { + public void performDragging_showMagnificationButton_updateViewLayout() { mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - resetMockImageViewAndAnimator(); - - // Perform dragging - final View.OnTouchListener listener = mTouchListenerCaptor.getValue(); - final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop(); + resetAndStubMockImageViewAndAnimator(); final int previousMode = Settings.Secure.getIntForUser(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0, UserHandle.USER_CURRENT); - listener.onTouch(mSpyImageView, MotionEvent.obtain( - 0, 0, ACTION_DOWN, 100, 100, 0)); + + // Perform dragging + final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop() + 10; + final long downTime = SystemClock.uptimeMillis(); + mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( + downTime, 0, ACTION_DOWN, 100, 100)); verify(mViewPropertyAnimator).cancel(); - listener.onTouch(mSpyImageView, MotionEvent.obtain( - 0, ViewConfiguration.getTapTimeout(), ACTION_MOVE, 100 + offset, 100, 0)); + mTouchListener.onTouch(mSpyImageView, + obtainMotionEvent(downTime, downTime, ACTION_MOVE, 100 + offset, + 100)); verify(mWindowManager).updateViewLayout(eq(mSpyImageView), any(WindowManager.LayoutParams.class)); - resetMockImageViewAndAnimator(); - listener.onTouch(mSpyImageView, MotionEvent.obtain( - 0, ViewConfiguration.getTapTimeout() + 10, ACTION_UP, 100 + offset, 100, 0)); + resetAndStubMockImageViewAndAnimator(); + mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( + downTime, downTime, ACTION_UP, 100 + offset, 100)); + assertModeUnchanged(previousMode); assertShowFadingAnimation(FADE_OUT_ALPHA); } @@ -208,18 +238,17 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { @Test public void performSingleTapActionCanceled_showButtonAnimation() { mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - resetMockImageViewAndAnimator(); - - // Perform single tap - final View.OnTouchListener listener = mTouchListenerCaptor.getValue(); + resetAndStubMockImageViewAndAnimator(); final int previousMode = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0); - listener.onTouch(mSpyImageView, MotionEvent.obtain( - 0, 0, ACTION_DOWN, 100, 100, 0)); - resetMockImageViewAndAnimator(); - listener.onTouch(mSpyImageView, MotionEvent.obtain( - 0, ViewConfiguration.getTapTimeout(), ACTION_CANCEL, 100, 100, 0)); + final long downTime = SystemClock.uptimeMillis(); + mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( + downTime, downTime, ACTION_DOWN, 100, 100)); + resetAndStubMockImageViewAndAnimator(); + mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( + downTime, downTime, ACTION_CANCEL, 100, 100)); + assertModeUnchanged(previousMode); assertShowFadingAnimation(FADE_OUT_ALPHA); } @@ -227,21 +256,21 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { @Test public void performDraggingActionCanceled_showButtonAnimation() { mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - resetMockImageViewAndAnimator(); - - // Perform dragging - final View.OnTouchListener listener = mTouchListenerCaptor.getValue(); - final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop(); + resetAndStubMockImageViewAndAnimator(); final int previousMode = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0); - listener.onTouch(mSpyImageView, MotionEvent.obtain( - 0, 0, ACTION_DOWN, 100, 100, 0)); - listener.onTouch(mSpyImageView, MotionEvent.obtain( - 0, ViewConfiguration.getTapTimeout(), ACTION_MOVE, 100 + offset, 100, 0)); - - resetMockImageViewAndAnimator(); - listener.onTouch(mSpyImageView, MotionEvent.obtain( - 0, ViewConfiguration.getTapTimeout(), ACTION_CANCEL, 100 + offset, 100, 0)); + + // Perform dragging + final long downTime = SystemClock.uptimeMillis(); + final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop() + 10; + mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( + 0, 0, ACTION_DOWN, 100, 100)); + mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( + downTime, downTime, ACTION_MOVE, 100 + offset, 100)); + resetAndStubMockImageViewAndAnimator(); + mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( + downTime, downTime, ACTION_CANCEL, 100 + offset, 100)); + assertModeUnchanged(previousMode); assertShowFadingAnimation(FADE_OUT_ALPHA); } @@ -266,7 +295,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { @Test public void performA11yActions_showWindowModeButton_verifyTapAction() { mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); - resetMockImageViewAndAnimator(); + resetAndStubMockImageViewAndAnimator(); mSpyImageView.performAccessibilityAction( ACTION_CLICK.getId(), null); @@ -278,7 +307,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { public void showButton_showFadeOutAnimation_fadeOutAnimationCanceled() { mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); assertShowFadingAnimation(FADE_OUT_ALPHA); - resetMockImageViewAndAnimator(); + resetAndStubMockImageViewAndAnimator(); mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); @@ -327,7 +356,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { } else { // Fade-out verify(mSpyImageView).postOnAnimationDelayed(runnableCaptor.capture(), anyLong()); } - resetMockAnimator(); + resetAndStubMockAnimator(); runnableCaptor.getValue().run(); @@ -336,20 +365,15 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { verify(mViewPropertyAnimator).start(); } - private void resetMockImageViewAndAnimator() { + private void resetAndStubMockImageViewAndAnimator() { + resetAndStubMockAnimator(); Mockito.reset(mSpyImageView); - doAnswer(invocation -> null).when(mSpyImageView).setOnTouchListener( - mTouchListenerCaptor.capture()); - resetMockAnimator(); + doReturn(mViewPropertyAnimator).when(mSpyImageView).animate(); } - private void resetMockAnimator() { + private void resetAndStubMockAnimator() { Mockito.reset(mViewPropertyAnimator); - when(mViewPropertyAnimator.setDuration(anyLong())).thenReturn(mViewPropertyAnimator); - when(mViewPropertyAnimator.alpha(anyFloat())).thenReturn(mViewPropertyAnimator); - when(mViewPropertyAnimator.withEndAction(any(Runnable.class))).thenReturn( - mViewPropertyAnimator); - when(mSpyImageView.animate()).thenReturn(mViewPropertyAnimator); + doNothing().when(mViewPropertyAnimator).start(); } /** @@ -366,4 +390,11 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0, UserHandle.USER_CURRENT); assertEquals(expectedMode, actualMode); } + + private MotionEvent obtainMotionEvent(long downTime, long eventTime, int action, float x, + float y) { + MotionEvent event = MotionEvent.obtain(downTime, eventTime, action, x, y, 0); + mMotionEvents.add(event); + return event; + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java index 152c51e1f9f4..ac699f7192c8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java @@ -65,7 +65,7 @@ public class RankingBuilder { mKey = ranking.getKey(); mRank = ranking.getRank(); mMatchesInterruptionFilter = ranking.matchesInterruptionFilter(); - mVisibilityOverride = ranking.getVisibilityOverride(); + mVisibilityOverride = ranking.getLockscreenVisibilityOverride(); mSuppressedVisualEffects = ranking.getSuppressedVisualEffects(); mImportance = ranking.getImportance(); mExplanation = ranking.getImportanceExplanation(); diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index d6d4e4f6c746..8f093c7e6674 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -1797,4 +1797,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ public void setTouchExplorationPassthroughRegion(int displayId, Region region) { mSystemSupport.setTouchExplorationPassthroughRegion(displayId, region); } + + @Override + public void setFocusAppearance(int strokeWidth, int color) { } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index be2f8f16a412..c6919ad24572 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -1968,6 +1968,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // Update the capabilities before the mode. updateMagnificationCapabilitiesSettingsChangeLocked(userState); updateMagnificationModeChangeSettingsLocked(userState); + updateFocusAppearanceDataLocked(userState); } private void updateWindowsForAccessibilityCallbackLocked(AccessibilityUserState userState) { @@ -3012,6 +3013,30 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } + /** + * Gets the stroke width of the focus rectangle. + * @return The stroke width. + */ + public int getFocusStrokeWidth() { + synchronized (mLock) { + final AccessibilityUserState userState = getCurrentUserStateLocked(); + + return userState.getFocusStrokeWidthLocked(); + } + } + + /** + * Gets the color of the focus rectangle. + * @return The color. + */ + public int getFocusColor() { + synchronized (mLock) { + final AccessibilityUserState userState = getCurrentUserStateLocked(); + + return userState.getFocusColorLocked(); + } + } + @Override public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return; @@ -3623,4 +3648,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } } + + private void updateFocusAppearanceDataLocked(AccessibilityUserState userState) { + if (userState.mUserId != mCurrentUserId) { + return; + } + + mMainHandler.post(() -> { + broadcastToClients(userState, ignoreRemoteException(client -> { + client.mCallback.setFocusAppearance(userState.getFocusStrokeWidthLocked(), + userState.getFocusColorLocked()); + })); + }); + + } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java index e48d11d17f40..5d67992316a2 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java @@ -120,6 +120,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect AccessibilityUserState userState = mUserStateWeakReference.get(); if (userState == null) return; userState.removeServiceLocked(this); + userState.resetFocusAppearanceLocked(); + mSystemSupport.onClientChangeLocked(false); mSystemSupport.getFullScreenMagnificationController().resetAllIfNeeded(mId); mActivityTaskManagerService.setAllowAppSwitches(mComponentName.flattenToString(), -1, userState.mUserId); @@ -144,6 +146,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect } finally { Binder.restoreCallingIdentity(identity); } + userState.resetFocusAppearanceLocked(); mSystemSupport.onClientChangeLocked(false); } } @@ -310,6 +313,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect AccessibilityUserState userState = mUserStateWeakReference.get(); if (userState != null) { userState.serviceDisconnectedLocked(this); + userState.resetFocusAppearanceLocked(); } resetLocked(); mSystemSupport.getFullScreenMagnificationController().resetAllIfNeeded(mId); @@ -391,4 +395,32 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect } } } + + @Override + public void setFocusAppearance(int strokeWidth, int color) { + AccessibilityUserState userState = mUserStateWeakReference.get(); + if (userState == null) { + return; + } + + synchronized (mLock) { + if (!hasRightsToCurrentUserLocked()) { + return; + } + + if (!mSecurityPolicy.checkAccessibilityAccess(this)) { + return; + } + + if (userState.getFocusStrokeWidthLocked() == strokeWidth + && userState.getFocusColorLocked() == color) { + return; + } + + // Sets the appearance data in the A11yUserState. + userState.setFocusAppearanceLocked(strokeWidth, color); + // Updates the appearance data in the A11yManager. + mSystemSupport.onClientChangeLocked(false); + } + } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java index 240c7ff061c8..90e2fdfa2f03 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java @@ -45,6 +45,7 @@ import android.util.Slog; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IAccessibilityManagerClient; +import com.android.internal.R; import com.android.internal.accessibility.AccessibilityShortcutController; import java.io.FileDescriptor; @@ -122,6 +123,15 @@ class AccessibilityUserState { // The magnification capabilities used to know magnification mode could be switched. private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; + /** The stroke width of the focus rectangle in pixels */ + private int mFocusStrokeWidth; + /** The color of the focus rectangle */ + private int mFocusColor; + // The default value of the focus stroke width. + private final int mFocusStrokeWidthDefaultValue; + // The default value of the focus color. + private final int mFocusColorDefaultValue; + private Context mContext; @SoftKeyboardShowMode @@ -140,6 +150,12 @@ class AccessibilityUserState { mUserId = userId; mContext = context; mServiceInfoChangeListener = serviceInfoChangeListener; + mFocusStrokeWidthDefaultValue = mContext.getResources().getDimensionPixelSize( + R.dimen.accessibility_focus_highlight_stroke_width); + mFocusColorDefaultValue = mContext.getResources().getColor( + R.color.accessibility_focus_highlight_color); + mFocusStrokeWidth = mFocusStrokeWidthDefaultValue; + mFocusColor = mFocusColorDefaultValue; } boolean isHandlingAccessibilityEventsLocked() { @@ -178,6 +194,7 @@ class AccessibilityUserState { mUserNonInteractiveUiTimeout = 0; mUserInteractiveUiTimeout = 0; mMagnificationMode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; + resetFocusAppearanceLocked(); } void addServiceLocked(AccessibilityServiceConnection serviceConnection) { @@ -880,4 +897,40 @@ class AccessibilityUserState { } return false; } + + /** + * Gets the stroke width of the focus rectangle. + * @return The stroke width. + */ + public int getFocusStrokeWidthLocked() { + return mFocusStrokeWidth; + } + + /** + * Gets the color of the focus rectangle. + * @return The color. + */ + public int getFocusColorLocked() { + return mFocusColor; + } + + /** + * Sets the stroke width and color of the focus rectangle. + * + * @param strokeWidth The strokeWidth of the focus rectangle. + * @param color The color of the focus rectangle. + */ + public void setFocusAppearanceLocked(int strokeWidth, int color) { + mFocusStrokeWidth = strokeWidth; + mFocusColor = color; + } + + /** + * Resets the stroke width and color of the focus rectangle to the default value. + * + */ + public void resetFocusAppearanceLocked() { + mFocusStrokeWidth = mFocusStrokeWidthDefaultValue; + mFocusColor = mFocusColorDefaultValue; + } } diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index f1988e9f8b6b..449063d95770 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -19,6 +19,7 @@ package com.android.server.appwidget; import static android.content.Context.KEYGUARD_SERVICE; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; + import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import android.annotation.UserIdInt; @@ -102,6 +103,7 @@ import android.util.proto.ProtoOutputStream; import android.view.Display; import android.view.View; import android.widget.RemoteViews; + import com.android.internal.R; import com.android.internal.app.SuspendedAppActivity; import com.android.internal.app.UnlaunchableAppActivity; @@ -111,11 +113,14 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.os.SomeArgs; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.widget.IRemoteViewsFactory; import com.android.server.LocalServices; import com.android.server.WidgetBackupProvider; import com.android.server.policy.IconUtilities; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -137,9 +142,6 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBackupProvider, OnCrossProfileWidgetProvidersChangeListener { @@ -2497,85 +2499,80 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } } - private static void serializeProvider(XmlSerializer out, Provider p) throws IOException { + private static void serializeProvider(TypedXmlSerializer out, Provider p) throws IOException { out.startTag(null, "p"); out.attribute(null, "pkg", p.info.provider.getPackageName()); out.attribute(null, "cl", p.info.provider.getClassName()); - out.attribute(null, "tag", Integer.toHexString(p.tag)); + out.attributeIntHex(null, "tag", p.tag); if (!TextUtils.isEmpty(p.infoTag)) { out.attribute(null, "info_tag", p.infoTag); } out.endTag(null, "p"); } - private static void serializeHost(XmlSerializer out, Host host) throws IOException { + private static void serializeHost(TypedXmlSerializer out, Host host) throws IOException { out.startTag(null, "h"); out.attribute(null, "pkg", host.id.packageName); - out.attribute(null, "id", Integer.toHexString(host.id.hostId)); - out.attribute(null, "tag", Integer.toHexString(host.tag)); + out.attributeIntHex(null, "id", host.id.hostId); + out.attributeIntHex(null, "tag", host.tag); out.endTag(null, "h"); } - private static void serializeAppWidget(XmlSerializer out, Widget widget, + private static void serializeAppWidget(TypedXmlSerializer out, Widget widget, boolean saveRestoreCompleted) throws IOException { out.startTag(null, "g"); - out.attribute(null, "id", Integer.toHexString(widget.appWidgetId)); - out.attribute(null, "rid", Integer.toHexString(widget.restoredId)); - out.attribute(null, "h", Integer.toHexString(widget.host.tag)); + out.attributeIntHex(null, "id", widget.appWidgetId); + out.attributeIntHex(null, "rid", widget.restoredId); + out.attributeIntHex(null, "h", widget.host.tag); if (widget.provider != null) { - out.attribute(null, "p", Integer.toHexString(widget.provider.tag)); + out.attributeIntHex(null, "p", widget.provider.tag); } if (widget.options != null) { int minWidth = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH); int minHeight = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT); int maxWidth = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH); int maxHeight = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT); - out.attribute(null, "min_width", Integer.toHexString((minWidth > 0) ? minWidth : 0)); - out.attribute(null, "min_height", Integer.toHexString((minHeight > 0) ? minHeight : 0)); - out.attribute(null, "max_width", Integer.toHexString((maxWidth > 0) ? maxWidth : 0)); - out.attribute(null, "max_height", Integer.toHexString((maxHeight > 0) ? maxHeight : 0)); - out.attribute(null, "host_category", Integer.toHexString(widget.options.getInt( - AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY))); + out.attributeIntHex(null, "min_width", (minWidth > 0) ? minWidth : 0); + out.attributeIntHex(null, "min_height", (minHeight > 0) ? minHeight : 0); + out.attributeIntHex(null, "max_width", (maxWidth > 0) ? maxWidth : 0); + out.attributeIntHex(null, "max_height", (maxHeight > 0) ? maxHeight : 0); + out.attributeIntHex(null, "host_category", widget.options.getInt( + AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)); if (saveRestoreCompleted) { boolean restoreCompleted = widget.options.getBoolean( AppWidgetManager.OPTION_APPWIDGET_RESTORE_COMPLETED); - out.attribute(null, "restore_completed", Boolean.toString(restoreCompleted)); + out.attributeBoolean(null, "restore_completed", restoreCompleted); } } out.endTag(null, "g"); } - private static Bundle parseWidgetIdOptions(XmlPullParser parser) { + private static Bundle parseWidgetIdOptions(TypedXmlPullParser parser) { Bundle options = new Bundle(); - String restoreCompleted = parser.getAttributeValue(null, "restore_completed"); - if (restoreCompleted != null) { - options.putBoolean(AppWidgetManager.OPTION_APPWIDGET_RESTORE_COMPLETED, - Boolean.valueOf(restoreCompleted)); - } - String minWidthString = parser.getAttributeValue(null, "min_width"); - if (minWidthString != null) { - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, - Integer.parseInt(minWidthString, 16)); - } - String minHeightString = parser.getAttributeValue(null, "min_height"); - if (minHeightString != null) { - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, - Integer.parseInt(minHeightString, 16)); - } - String maxWidthString = parser.getAttributeValue(null, "max_width"); - if (maxWidthString != null) { - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, - Integer.parseInt(maxWidthString, 16)); - } - String maxHeightString = parser.getAttributeValue(null, "max_height"); - if (maxHeightString != null) { - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, - Integer.parseInt(maxHeightString, 16)); - } - String categoryString = parser.getAttributeValue(null, "host_category"); - if (categoryString != null) { - options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, - Integer.parseInt(categoryString, 16)); + boolean restoreCompleted = parser.getAttributeBoolean(null, "restore_completed", false); + if (restoreCompleted) { + options.putBoolean(AppWidgetManager.OPTION_APPWIDGET_RESTORE_COMPLETED, true); + } + int minWidth = parser.getAttributeIntHex(null, "min_width", -1); + if (minWidth != -1) { + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, minWidth); + } + int minHeight = parser.getAttributeIntHex(null, "min_height", -1); + if (minHeight != -1) { + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, minHeight); + } + int maxWidth = parser.getAttributeIntHex(null, "max_width", -1); + if (maxWidth != -1) { + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, maxWidth); + } + int maxHeight = parser.getAttributeIntHex(null, "max_height", -1); + if (maxHeight != -1) { + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, maxHeight); + } + int category = parser.getAttributeIntHex(null, "host_category", + AppWidgetProviderInfo.WIDGET_CATEGORY_UNKNOWN); + if (category != AppWidgetProviderInfo.WIDGET_CATEGORY_UNKNOWN) { + options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, category); } return options; } @@ -3080,7 +3077,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku TypedXmlSerializer out = Xml.resolveSerializer(stream); out.startDocument(null, true); out.startTag(null, "gs"); - out.attribute(null, "version", String.valueOf(CURRENT_VERSION)); + out.attributeInt(null, "version", CURRENT_VERSION); N = mProviders.size(); for (int i = 0; i < N; i++) { @@ -3149,12 +3146,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku if (type == XmlPullParser.START_TAG) { String tag = parser.getName(); if ("gs".equals(tag)) { - String attributeValue = parser.getAttributeValue(null, "version"); - try { - version = Integer.parseInt(attributeValue); - } catch (NumberFormatException e) { - version = 0; - } + version = parser.getAttributeInt(null, "version", 0); } else if ("p".equals(tag)) { legacyProviderIndex++; // TODO: do we need to check that this package has the same signature @@ -3193,9 +3185,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku mProviders.add(provider); } - String tagAttribute = parser.getAttributeValue(null, "tag"); - final int providerTag = !TextUtils.isEmpty(tagAttribute) - ? Integer.parseInt(tagAttribute, 16) : legacyProviderIndex; + final int providerTag = parser.getAttributeIntHex(null, "tag", + legacyProviderIndex); provider.tag = providerTag; provider.infoTag = parser.getAttributeValue(null, "info_tag"); @@ -3221,12 +3212,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku if (!host.zombie || mSafeMode) { // In safe mode, we don't discard the hosts we don't recognize // so that they're not pruned from our list. Otherwise, we do. - final int hostId = Integer.parseInt(parser.getAttributeValue( - null, "id"), 16); - - String tagAttribute = parser.getAttributeValue(null, "tag"); - final int hostTag = !TextUtils.isEmpty(tagAttribute) - ? Integer.parseInt(tagAttribute, 16) : legacyHostIndex; + final int hostId = parser.getAttributeIntHex(null, "id"); + final int hostTag = parser.getAttributeIntHex(null, "tag", + legacyHostIndex); host.tag = hostTag; host.id = new HostId(uid, hostId, pkg); @@ -3241,21 +3229,17 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } } else if ("g".equals(tag)) { Widget widget = new Widget(); - widget.appWidgetId = Integer.parseInt(parser.getAttributeValue( - null, "id"), 16); + widget.appWidgetId = parser.getAttributeIntHex(null, "id"); setMinAppWidgetIdLocked(userId, widget.appWidgetId + 1); // restored ID is allowed to be absent - String restoredIdString = parser.getAttributeValue(null, "rid"); - widget.restoredId = (restoredIdString == null) ? 0 - : Integer.parseInt(restoredIdString, 16); + widget.restoredId = parser.getAttributeIntHex(null, "rid", 0); widget.options = parseWidgetIdOptions(parser); - final int hostTag = Integer.parseInt(parser.getAttributeValue( - null, "h"), 16); + final int hostTag = parser.getAttributeIntHex(null, "h"); String providerString = parser.getAttributeValue(null, "p"); - final int providerTag = (providerString != null) ? Integer.parseInt( - parser.getAttributeValue(null, "p"), 16) : TAG_UNDEFINED; + final int providerTag = (providerString != null) + ? parser.getAttributeIntHex(null, "p") : TAG_UNDEFINED; // We can match widgets with hosts and providers only after hosts // and providers for all users have been loaded since the widget @@ -4372,11 +4356,11 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } try { - XmlSerializer out = new FastXmlSerializer(); + TypedXmlSerializer out = Xml.newFastSerializer(); out.setOutput(stream, StandardCharsets.UTF_8.name()); out.startDocument(null, true); out.startTag(null, "ws"); // widget state - out.attribute(null, "version", String.valueOf(WIDGET_STATE_VERSION)); + out.attributeInt(null, "version", WIDGET_STATE_VERSION); out.attribute(null, "pkg", backedupPackage); // Remember all the providers that are currently hosted or published @@ -4464,7 +4448,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku // Hosts mentioned in the widget dataset by ordinal ArrayList<Host> restoredHosts = new ArrayList<>(); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(stream, StandardCharsets.UTF_8.name()); synchronized (mLock) { @@ -4474,11 +4458,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku if (type == XmlPullParser.START_TAG) { final String tag = parser.getName(); if ("ws".equals(tag)) { - String version = parser.getAttributeValue(null, "version"); - - final int versionNumber = Integer.parseInt(version); + final int versionNumber = parser.getAttributeInt(null, "version"); if (versionNumber > WIDGET_STATE_VERSION) { - Slog.w(TAG, "Unable to process state version " + version); + Slog.w(TAG, "Unable to process state version " + versionNumber); return; } @@ -4520,8 +4502,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku String pkg = parser.getAttributeValue(null, "pkg"); final int uid = getUidForPackage(pkg, userId); - final int hostId = Integer.parseInt( - parser.getAttributeValue(null, "id"), 16); + final int hostId = parser.getAttributeIntHex(null, "id"); HostId id = new HostId(uid, hostId, pkg); Host h = lookupOrAddHostLocked(id); @@ -4532,17 +4513,14 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku + "]: {" + h.id + "}"); } } else if ("g".equals(tag)) { - int restoredId = Integer.parseInt( - parser.getAttributeValue(null, "id"), 16); - int hostIndex = Integer.parseInt( - parser.getAttributeValue(null, "h"), 16); + int restoredId = parser.getAttributeIntHex(null, "id"); + int hostIndex = parser.getAttributeIntHex(null, "h"); Host host = restoredHosts.get(hostIndex); Provider p = null; - String prov = parser.getAttributeValue(null, "p"); - if (prov != null) { + int which = parser.getAttributeIntHex(null, "p", -1); + if (which != -1) { // could have been null if the app had allocated an id // but not yet established a binding under that id - int which = Integer.parseInt(prov, 16); p = restoredProviders.get(which); } diff --git a/services/art-profile b/services/art-profile index 23a2e16bdb5d..287e6094bfe0 100644 --- a/services/art-profile +++ b/services/art-profile @@ -32073,6 +32073,32 @@ PLcom/android/server/wm/AccessibilityController;->removeObserverOfEmbeddedDispla PLcom/android/server/wm/AccessibilityController;->setMagnificationCallbacksLocked(ILcom/android/server/wm/WindowManagerInternal$MagnificationCallbacks;)Z PLcom/android/server/wm/AccessibilityController;->setMagnificationSpecLocked(ILandroid/view/MagnificationSpec;)V PLcom/android/server/wm/AccessibilityController;->setWindowsForAccessibilityCallbackLocked(ILcom/android/server/wm/WindowManagerInternal$WindowsForAccessibilityCallback;)Z +HPLcom/android/server/wm/ActivityClientController;->activityDestroyed(Landroid/os/IBinder;)V +HPLcom/android/server/wm/ActivityClientController;->activityIdle(Landroid/os/IBinder;Landroid/content/res/Configuration;Z)V +HPLcom/android/server/wm/ActivityClientController;->activityPaused(Landroid/os/IBinder;)V +PLcom/android/server/wm/ActivityClientController;->activityRelaunched(Landroid/os/IBinder;)V +HPLcom/android/server/wm/ActivityClientController;->activityResumed(Landroid/os/IBinder;)V +HPLcom/android/server/wm/ActivityClientController;->activityStopped(Landroid/os/IBinder;Landroid/os/Bundle;Landroid/os/PersistableBundle;Ljava/lang/CharSequence;)V +HPLcom/android/server/wm/ActivityClientController;->activityTopResumedStateLost()V +PLcom/android/server/wm/ActivityClientController;->ensureValidPictureInPictureActivityParamsLocked(Ljava/lang/String;Landroid/os/IBinder;Landroid/app/PictureInPictureParams;)Lcom/android/server/wm/ActivityRecord; +PLcom/android/server/wm/ActivityClientController;->enterPictureInPictureMode(Landroid/os/IBinder;Landroid/app/PictureInPictureParams;)Z +HPLcom/android/server/wm/ActivityClientController;->getActivityOptions(Landroid/os/IBinder;)Landroid/os/Bundle; +PLcom/android/server/wm/ActivityClientController;->getCallingRecord(Landroid/os/IBinder;)Lcom/android/server/wm/ActivityRecord; +PLcom/android/server/wm/ActivityClientController;->isTopOfTask(Landroid/os/IBinder;)Z +PLcom/android/server/wm/ActivityClientController;->moveActivityTaskToBack(Landroid/os/IBinder;Z)Z +PLcom/android/server/wm/ActivityClientController;->navigateUpTo(Landroid/os/IBinder;Landroid/content/Intent;ILandroid/content/Intent;)Z +HPLcom/android/server/wm/ActivityClientController;->notifyActivityDrawn(Landroid/os/IBinder;)V +HPLcom/android/server/wm/ActivityClientController;->onBackPressedOnTaskRoot(Landroid/os/IBinder;)V +PLcom/android/server/wm/ActivityClientController;->overridePendingTransition(Landroid/os/IBinder;Ljava/lang/String;II)V +PLcom/android/server/wm/ActivityClientController;->registerRemoteAnimations(Landroid/os/IBinder;Landroid/view/RemoteAnimationDefinition;)V +PLcom/android/server/wm/ActivityClientController;->releaseActivityInstance(Landroid/os/IBinder;)Z +PLcom/android/server/wm/ActivityClientController;->reportActivityFullyDrawn(Landroid/os/IBinder;Z)V +HPLcom/android/server/wm/ActivityClientController;->reportSizeConfigurations(Landroid/os/IBinder;[I[I[I)V +PLcom/android/server/wm/ActivityClientController;->setDisablePreviewScreenshots(Landroid/os/IBinder;Z)V +PLcom/android/server/wm/ActivityClientController;->setShowWhenLocked(Landroid/os/IBinder;Z)V +HPLcom/android/server/wm/ActivityClientController;->setTaskDescription(Landroid/os/IBinder;Landroid/app/ActivityManager$TaskDescription;)V +PLcom/android/server/wm/ActivityClientController;->setTurnScreenOn(Landroid/os/IBinder;Z)V +PLcom/android/server/wm/ActivityClientController;->unregisterRemoteAnimations(Landroid/os/IBinder;)V HSPLcom/android/server/wm/ActivityMetricsLogger$LaunchingState;-><init>()V HSPLcom/android/server/wm/ActivityMetricsLogger$LaunchingState;->access$000(Lcom/android/server/wm/ActivityMetricsLogger$LaunchingState;)J HSPLcom/android/server/wm/ActivityMetricsLogger$LaunchingState;->access$002(Lcom/android/server/wm/ActivityMetricsLogger$LaunchingState;J)J @@ -32859,13 +32885,6 @@ HSPLcom/android/server/wm/ActivityTaskManagerService;->access$300(Lcom/android/s HSPLcom/android/server/wm/ActivityTaskManagerService;->access$602(Lcom/android/server/wm/ActivityTaskManagerService;Lcom/android/server/wm/BackgroundActivityStartCallback;)Lcom/android/server/wm/BackgroundActivityStartCallback; PLcom/android/server/wm/ActivityTaskManagerService;->access$802(Lcom/android/server/wm/ActivityTaskManagerService;Z)Z HSPLcom/android/server/wm/ActivityTaskManagerService;->access$900(Lcom/android/server/wm/ActivityTaskManagerService;)Z -HPLcom/android/server/wm/ActivityTaskManagerService;->activityDestroyed(Landroid/os/IBinder;)V -HPLcom/android/server/wm/ActivityTaskManagerService;->activityIdle(Landroid/os/IBinder;Landroid/content/res/Configuration;Z)V -HPLcom/android/server/wm/ActivityTaskManagerService;->activityPaused(Landroid/os/IBinder;)V -PLcom/android/server/wm/ActivityTaskManagerService;->activityRelaunched(Landroid/os/IBinder;)V -HPLcom/android/server/wm/ActivityTaskManagerService;->activityResumed(Landroid/os/IBinder;)V -HPLcom/android/server/wm/ActivityTaskManagerService;->activityStopped(Landroid/os/IBinder;Landroid/os/Bundle;Landroid/os/PersistableBundle;Ljava/lang/CharSequence;)V -HPLcom/android/server/wm/ActivityTaskManagerService;->activityTopResumedStateLost()V HSPLcom/android/server/wm/ActivityTaskManagerService;->addWindowLayoutReasons(I)V HPLcom/android/server/wm/ActivityTaskManagerService;->applyUpdateLockStateLocked(Lcom/android/server/wm/ActivityRecord;)V HPLcom/android/server/wm/ActivityTaskManagerService;->applyUpdateVrModeLocked(Lcom/android/server/wm/ActivityRecord;)V @@ -32898,8 +32917,6 @@ HPLcom/android/server/wm/ActivityTaskManagerService;->enforceNotIsolatedCaller(L HPLcom/android/server/wm/ActivityTaskManagerService;->enforceTaskPermission(Ljava/lang/String;)V PLcom/android/server/wm/ActivityTaskManagerService;->enqueueAssistContext(ILandroid/content/Intent;Ljava/lang/String;Landroid/app/IAssistDataReceiver;Landroid/os/Bundle;Landroid/os/IBinder;ZZILandroid/os/Bundle;JI)Lcom/android/server/wm/ActivityTaskManagerService$PendingAssistExtras; HSPLcom/android/server/wm/ActivityTaskManagerService;->ensureConfigAndVisibilityAfterUpdate(Lcom/android/server/wm/ActivityRecord;I)Z -PLcom/android/server/wm/ActivityTaskManagerService;->ensureValidPictureInPictureActivityParamsLocked(Ljava/lang/String;Landroid/os/IBinder;Landroid/app/PictureInPictureParams;)Lcom/android/server/wm/ActivityRecord; -PLcom/android/server/wm/ActivityTaskManagerService;->enterPictureInPictureMode(Landroid/os/IBinder;Landroid/app/PictureInPictureParams;)Z PLcom/android/server/wm/ActivityTaskManagerService;->enterPictureInPictureMode(Lcom/android/server/wm/ActivityRecord;Landroid/app/PictureInPictureParams;)Z PLcom/android/server/wm/ActivityTaskManagerService;->expireStartAsCallerTokenMsg(Landroid/os/IBinder;)V HPLcom/android/server/wm/ActivityTaskManagerService;->finishActivity(Landroid/os/IBinder;ILandroid/content/Intent;I)Z @@ -32909,7 +32926,6 @@ PLcom/android/server/wm/ActivityTaskManagerService;->finishSubActivity(Landroid/ PLcom/android/server/wm/ActivityTaskManagerService;->finishVoiceTask(Landroid/service/voice/IVoiceInteractionSession;)V PLcom/android/server/wm/ActivityTaskManagerService;->forgetStartAsCallerTokenMsg(Landroid/os/IBinder;)V PLcom/android/server/wm/ActivityTaskManagerService;->getActivityClassForToken(Landroid/os/IBinder;)Landroid/content/ComponentName; -HPLcom/android/server/wm/ActivityTaskManagerService;->getActivityOptions(Landroid/os/IBinder;)Landroid/os/Bundle; HSPLcom/android/server/wm/ActivityTaskManagerService;->getActivityStartController()Lcom/android/server/wm/ActivityStartController; PLcom/android/server/wm/ActivityTaskManagerService;->getAllRootTaskInfosOnDisplay(I)Ljava/util/List; HSPLcom/android/server/wm/ActivityTaskManagerService;->getAppInfoForUser(Landroid/content/pm/ApplicationInfo;I)Landroid/content/pm/ApplicationInfo; @@ -32919,7 +32935,6 @@ HPLcom/android/server/wm/ActivityTaskManagerService;->getAppWarningsLocked()Lcom HSPLcom/android/server/wm/ActivityTaskManagerService;->getBackgroundActivityStartCallback()Lcom/android/server/wm/BackgroundActivityStartCallback; PLcom/android/server/wm/ActivityTaskManagerService;->getCallingActivity(Landroid/os/IBinder;)Landroid/content/ComponentName; PLcom/android/server/wm/ActivityTaskManagerService;->getCallingPackage(Landroid/os/IBinder;)Ljava/lang/String; -PLcom/android/server/wm/ActivityTaskManagerService;->getCallingRecordLocked(Landroid/os/IBinder;)Lcom/android/server/wm/ActivityRecord; PLcom/android/server/wm/ActivityTaskManagerService;->getConfiguration()Landroid/content/res/Configuration; PLcom/android/server/wm/ActivityTaskManagerService;->getCurrentUserId()I HPLcom/android/server/wm/ActivityTaskManagerService;->getDeviceConfigurationInfo()Landroid/content/pm/ConfigurationInfo; @@ -32984,7 +32999,6 @@ PLcom/android/server/wm/ActivityTaskManagerService;->isKeyguardLocked()Z HPLcom/android/server/wm/ActivityTaskManagerService;->isSameApp(ILjava/lang/String;)Z HSPLcom/android/server/wm/ActivityTaskManagerService;->isSleepingLocked()Z HPLcom/android/server/wm/ActivityTaskManagerService;->isSleepingOrShuttingDownLocked()Z -PLcom/android/server/wm/ActivityTaskManagerService;->isTopOfTask(Landroid/os/IBinder;)Z HPLcom/android/server/wm/ActivityTaskManagerService;->isUidForeground(I)Z HPLcom/android/server/wm/ActivityTaskManagerService;->keyguardGoingAway(I)V PLcom/android/server/wm/ActivityTaskManagerService;->lambda$4YLTqMi21jZ51BFcKX_h_gIoeGg(Lcom/android/server/wm/ActivityTaskManagerService;Ljava/util/Locale;)V @@ -33000,33 +33014,22 @@ HPLcom/android/server/wm/ActivityTaskManagerService;->lambda$setLockScreenShown$ PLcom/android/server/wm/ActivityTaskManagerService;->lambda$tPFRgtovnZu_2Zm4sCcLa9-oBto(Lcom/android/server/wm/ActivityTaskManagerService;Landroid/os/IBinder;)V PLcom/android/server/wm/ActivityTaskManagerService;->lambda$uDPnzqVuuoVSFA7RJcXFWsrCwrY(Lcom/android/server/wm/ActivityTaskManagerService;ILandroid/content/res/Configuration;)V PLcom/android/server/wm/ActivityTaskManagerService;->logAppTooSlow(Lcom/android/server/wm/WindowProcessController;JLjava/lang/String;)V -PLcom/android/server/wm/ActivityTaskManagerService;->moveActivityTaskToBack(Landroid/os/IBinder;Z)Z PLcom/android/server/wm/ActivityTaskManagerService;->moveTaskToFrontLocked(Landroid/app/IApplicationThread;Ljava/lang/String;IILcom/android/server/wm/SafeActivityOptions;)V -PLcom/android/server/wm/ActivityTaskManagerService;->navigateUpTo(Landroid/os/IBinder;Landroid/content/Intent;ILandroid/content/Intent;)Z -HPLcom/android/server/wm/ActivityTaskManagerService;->notifyActivityDrawn(Landroid/os/IBinder;)V -HSPLcom/android/server/wm/ActivityTaskManagerService;->notifyEnterAnimationComplete(Landroid/os/IBinder;)V -PLcom/android/server/wm/ActivityTaskManagerService;->notifyLaunchTaskBehindComplete(Landroid/os/IBinder;)V PLcom/android/server/wm/ActivityTaskManagerService;->notifyTaskPersisterLocked(Lcom/android/server/wm/Task;Z)V HSPLcom/android/server/wm/ActivityTaskManagerService;->onActivityManagerInternalAdded()V -HPLcom/android/server/wm/ActivityTaskManagerService;->onBackPressedOnTaskRoot(Landroid/os/IBinder;)V HSPLcom/android/server/wm/ActivityTaskManagerService;->onInitPowerManagement()V HPLcom/android/server/wm/ActivityTaskManagerService;->onScreenAwakeChanged(Z)V HSPLcom/android/server/wm/ActivityTaskManagerService;->onStartActivitySetDidAppSwitch()V HSPLcom/android/server/wm/ActivityTaskManagerService;->onSystemReady()V -PLcom/android/server/wm/ActivityTaskManagerService;->overridePendingTransition(Landroid/os/IBinder;Ljava/lang/String;II)V PLcom/android/server/wm/ActivityTaskManagerService;->pendingAssistExtrasTimedOut(Lcom/android/server/wm/ActivityTaskManagerService$PendingAssistExtras;)V PLcom/android/server/wm/ActivityTaskManagerService;->postFinishBooting(ZZ)V PLcom/android/server/wm/ActivityTaskManagerService;->registerRemoteAnimationForNextActivityStart(Ljava/lang/String;Landroid/view/RemoteAnimationAdapter;)V -PLcom/android/server/wm/ActivityTaskManagerService;->registerRemoteAnimations(Landroid/os/IBinder;Landroid/view/RemoteAnimationDefinition;)V HPLcom/android/server/wm/ActivityTaskManagerService;->registerTaskStackListener(Landroid/app/ITaskStackListener;)V PLcom/android/server/wm/ActivityTaskManagerService;->relaunchReasonToString(I)Ljava/lang/String; -PLcom/android/server/wm/ActivityTaskManagerService;->releaseActivityInstance(Landroid/os/IBinder;)Z PLcom/android/server/wm/ActivityTaskManagerService;->releaseSomeActivities(Landroid/app/IApplicationThread;)V PLcom/android/server/wm/ActivityTaskManagerService;->removeRootTasksInWindowingModes([I)V PLcom/android/server/wm/ActivityTaskManagerService;->removeTask(I)Z -PLcom/android/server/wm/ActivityTaskManagerService;->reportActivityFullyDrawn(Landroid/os/IBinder;Z)V PLcom/android/server/wm/ActivityTaskManagerService;->reportAssistContextExtras(Landroid/os/IBinder;Landroid/os/Bundle;Landroid/app/assist/AssistStructure;Landroid/app/assist/AssistContent;Landroid/net/Uri;)V -HPLcom/android/server/wm/ActivityTaskManagerService;->reportSizeConfigurations(Landroid/os/IBinder;[I[I[I)V PLcom/android/server/wm/ActivityTaskManagerService;->requestAssistContextExtras(ILandroid/app/IAssistDataReceiver;Landroid/os/Bundle;Landroid/os/IBinder;ZZ)Z PLcom/android/server/wm/ActivityTaskManagerService;->requestAutofillData(Landroid/app/IAssistDataReceiver;Landroid/os/Bundle;Landroid/os/IBinder;I)Z PLcom/android/server/wm/ActivityTaskManagerService;->requestStartActivityPermissionToken(Landroid/os/IBinder;)Landroid/os/IBinder; @@ -33039,7 +33042,6 @@ PLcom/android/server/wm/ActivityTaskManagerService;->sendPutConfigurationForUser PLcom/android/server/wm/ActivityTaskManagerService;->setBooted(Z)V PLcom/android/server/wm/ActivityTaskManagerService;->setBooting(Z)V HSPLcom/android/server/wm/ActivityTaskManagerService;->setDeviceOwnerUid(I)V -PLcom/android/server/wm/ActivityTaskManagerService;->setDisablePreviewScreenshots(Landroid/os/IBinder;Z)V PLcom/android/server/wm/ActivityTaskManagerService;->setFocusedTask(I)V PLcom/android/server/wm/ActivityTaskManagerService;->setImmersive(Landroid/os/IBinder;Z)V HPLcom/android/server/wm/ActivityTaskManagerService;->setLockScreenShown(ZZ)V @@ -33047,10 +33049,7 @@ PLcom/android/server/wm/ActivityTaskManagerService;->setPictureInPictureParams(L HSPLcom/android/server/wm/ActivityTaskManagerService;->setRecentTasks(Lcom/android/server/wm/RecentTasks;)V PLcom/android/server/wm/ActivityTaskManagerService;->setRequestedOrientation(Landroid/os/IBinder;I)V HPLcom/android/server/wm/ActivityTaskManagerService;->setResumedActivityUncheckLocked(Lcom/android/server/wm/ActivityRecord;Ljava/lang/String;)V -PLcom/android/server/wm/ActivityTaskManagerService;->setShowWhenLocked(Landroid/os/IBinder;Z)V PLcom/android/server/wm/ActivityTaskManagerService;->setSplitScreenResizing(Z)V -HPLcom/android/server/wm/ActivityTaskManagerService;->setTaskDescription(Landroid/os/IBinder;Landroid/app/ActivityManager$TaskDescription;)V -PLcom/android/server/wm/ActivityTaskManagerService;->setTurnScreenOn(Landroid/os/IBinder;Z)V HSPLcom/android/server/wm/ActivityTaskManagerService;->setUsageStatsManager(Landroid/app/usage/UsageStatsManagerInternal;)V HSPLcom/android/server/wm/ActivityTaskManagerService;->setWindowManager(Lcom/android/server/wm/WindowManagerService;)V PLcom/android/server/wm/ActivityTaskManagerService;->shouldDisableNonVrUiLocked()Z @@ -33070,7 +33069,6 @@ HPLcom/android/server/wm/ActivityTaskManagerService;->startTimeTrackingFocusedAc PLcom/android/server/wm/ActivityTaskManagerService;->stopAppSwitches()V PLcom/android/server/wm/ActivityTaskManagerService;->stopLockTaskModeInternal(Landroid/os/IBinder;Z)V PLcom/android/server/wm/ActivityTaskManagerService;->stopSystemLockTaskMode()V -PLcom/android/server/wm/ActivityTaskManagerService;->unregisterRemoteAnimations(Landroid/os/IBinder;)V HPLcom/android/server/wm/ActivityTaskManagerService;->unregisterTaskStackListener(Landroid/app/ITaskStackListener;)V HPLcom/android/server/wm/ActivityTaskManagerService;->updateActivityUsageStats(Lcom/android/server/wm/ActivityRecord;I)V HPLcom/android/server/wm/ActivityTaskManagerService;->updateBatteryStats(Lcom/android/server/wm/ActivityRecord;Z)V diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 22134e5b408a..ee9d49244d7e 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -8200,8 +8200,10 @@ public class ConnectivityService extends IConnectivityManager.Stub final NetworkRequestInfo nri = cbInfo.mRequestInfo; - if (uid != nri.mUid) { - if (VDBG) loge("Different uid than registrant attempting to unregister cb"); + // Caller's UID must either be the registrants (if they are unregistering) or the System's + // (if the Binder died) + if (uid != nri.mUid && uid != Process.SYSTEM_UID) { + if (DBG) loge("Uid(" + uid + ") not registrant's (" + nri.mUid + ") or System's"); return; } diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index 9bf63cbbb25e..99a1d86d244e 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -52,9 +52,7 @@ import com.android.internal.util.XmlUtils; import libcore.io.IoUtils; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileNotFoundException; @@ -149,6 +147,7 @@ public class PackageWatchdog { private static final String ATTR_DURATION = "duration"; private static final String ATTR_EXPLICIT_HEALTH_CHECK_DURATION = "health-check-duration"; private static final String ATTR_PASSED_HEALTH_CHECK = "passed-health-check"; + private static final String ATTR_MITIGATION_CALLS = "mitigation-calls"; @GuardedBy("PackageWatchdog.class") private static PackageWatchdog sPackageWatchdog; @@ -779,7 +778,6 @@ public class PackageWatchdog { @GuardedBy("mLock") private Set<String> getPackagesPendingHealthChecksLocked() { - Slog.d(TAG, "Getting all observed packages pending health checks"); Set<String> packages = new ArraySet<>(); Iterator<ObserverInternal> oit = mAllObservers.values().iterator(); while (oit.hasNext()) { @@ -828,7 +826,6 @@ public class PackageWatchdog { Slog.i(TAG, "Cancelling state sync, nothing to sync"); mUptimeAtLastStateSync = 0; } else { - Slog.i(TAG, "Scheduling next state sync in " + durationMs + "ms"); mUptimeAtLastStateSync = mSystemClock.uptimeMillis(); mShortTaskHandler.postDelayed(mSyncStateWithScheduledReason, durationMs); } @@ -869,7 +866,6 @@ public class PackageWatchdog { return; } - Slog.i(TAG, "Removing " + elapsedMs + "ms from all packages on all observers"); Iterator<ObserverInternal> it = mAllObservers.values().iterator(); while (it.hasNext()) { ObserverInternal observer = it.next(); @@ -1067,6 +1063,33 @@ public class PackageWatchdog { } } + /** Convert a {@code LongArrayQueue} to a String of comma-separated values. */ + public static String longArrayQueueToString(LongArrayQueue queue) { + if (queue.size() > 0) { + StringBuilder sb = new StringBuilder(); + sb.append(queue.get(0)); + for (int i = 1; i < queue.size(); i++) { + sb.append(","); + sb.append(queue.get(i)); + } + return sb.toString(); + } + return ""; + } + + /** Parse a comma-separated String of longs into a LongArrayQueue. */ + public static LongArrayQueue parseLongArrayQueue(String commaSeparatedValues) { + LongArrayQueue result = new LongArrayQueue(); + if (!TextUtils.isEmpty(commaSeparatedValues)) { + String[] values = commaSeparatedValues.split(","); + for (String value : values) { + result.addLast(Long.parseLong(value)); + } + } + return result; + } + + /** Dump status of every observer in mAllObservers. */ public void dump(IndentingPrintWriter pw) { pw.println("Package Watchdog status"); @@ -1240,16 +1263,7 @@ public class PackageWatchdog { while (XmlUtils.nextElementWithin(parser, innerDepth)) { if (TAG_PACKAGE.equals(parser.getName())) { try { - String packageName = parser.getAttributeValue( - null, ATTR_NAME); - long duration = parser.getAttributeLong( - null, ATTR_DURATION); - long healthCheckDuration = parser.getAttributeLong( - null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION); - boolean hasPassedHealthCheck = parser.getAttributeBoolean( - null, ATTR_PASSED_HEALTH_CHECK, false); - MonitoredPackage pkg = watchdog.newMonitoredPackage(packageName, - duration, healthCheckDuration, hasPassedHealthCheck); + MonitoredPackage pkg = watchdog.parseMonitoredPackage(parser); if (pkg != null) { packages.add(pkg); } @@ -1305,16 +1319,31 @@ public class PackageWatchdog { MonitoredPackage newMonitoredPackage( String name, long durationMs, boolean hasPassedHealthCheck) { - return newMonitoredPackage(name, durationMs, Long.MAX_VALUE, hasPassedHealthCheck); + return newMonitoredPackage(name, durationMs, Long.MAX_VALUE, hasPassedHealthCheck, + new LongArrayQueue()); } MonitoredPackage newMonitoredPackage(String name, long durationMs, long healthCheckDurationMs, - boolean hasPassedHealthCheck) { + boolean hasPassedHealthCheck, LongArrayQueue mitigationCalls) { VersionedPackage pkg = getVersionedPackage(name); if (pkg == null) { return null; } - return new MonitoredPackage(pkg, durationMs, healthCheckDurationMs, hasPassedHealthCheck); + return new MonitoredPackage(pkg, durationMs, healthCheckDurationMs, + hasPassedHealthCheck, mitigationCalls); + } + + MonitoredPackage parseMonitoredPackage(TypedXmlPullParser parser) + throws XmlPullParserException { + String packageName = parser.getAttributeValue(null, ATTR_NAME); + long duration = parser.getAttributeLong(null, ATTR_DURATION); + long healthCheckDuration = parser.getAttributeLong(null, + ATTR_EXPLICIT_HEALTH_CHECK_DURATION); + boolean hasPassedHealthCheck = parser.getAttributeBoolean(null, ATTR_PASSED_HEALTH_CHECK); + LongArrayQueue mitigationCalls = parseLongArrayQueue( + parser.getAttributeValue(null, ATTR_MITIGATION_CALLS)); + return newMonitoredPackage(packageName, + duration, healthCheckDuration, hasPassedHealthCheck, mitigationCalls); } /** @@ -1332,7 +1361,7 @@ public class PackageWatchdog { // Times when an observer was called to mitigate this package's failure. Sorted in // ascending order. @GuardedBy("mLock") - private final LongArrayQueue mMitigationCalls = new LongArrayQueue(); + private final LongArrayQueue mMitigationCalls; // One of STATE_[ACTIVE|INACTIVE|PASSED|FAILED]. Updated on construction and after // methods that could change the health check state: handleElapsedTimeLocked and // tryPassHealthCheckLocked @@ -1353,12 +1382,14 @@ public class PackageWatchdog { @GuardedBy("mLock") private long mHealthCheckDurationMs = Long.MAX_VALUE; - private MonitoredPackage(VersionedPackage pkg, long durationMs, - long healthCheckDurationMs, boolean hasPassedHealthCheck) { + MonitoredPackage(VersionedPackage pkg, long durationMs, + long healthCheckDurationMs, boolean hasPassedHealthCheck, + LongArrayQueue mitigationCalls) { mPackage = pkg; mDurationMs = durationMs; mHealthCheckDurationMs = healthCheckDurationMs; mHasPassedHealthCheck = hasPassedHealthCheck; + mMitigationCalls = mitigationCalls; updateHealthCheckStateLocked(); } @@ -1370,6 +1401,8 @@ public class PackageWatchdog { out.attributeLong(null, ATTR_DURATION, mDurationMs); out.attributeLong(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION, mHealthCheckDurationMs); out.attributeBoolean(null, ATTR_PASSED_HEALTH_CHECK, mHasPassedHealthCheck); + LongArrayQueue normalizedCalls = normalizeMitigationCalls(); + out.attribute(null, ATTR_MITIGATION_CALLS, longArrayQueueToString(normalizedCalls)); out.endTag(null, TAG_PACKAGE); } @@ -1423,6 +1456,23 @@ public class PackageWatchdog { } /** + * Before writing to disk, make the mitigation call timestamps relative to the current + * system uptime. This is because they need to be relative to the uptime which will reset + * at the next boot. + * + * @return a LongArrayQueue of the mitigation calls relative to the current system uptime. + */ + @GuardedBy("mLock") + public LongArrayQueue normalizeMitigationCalls() { + LongArrayQueue normalized = new LongArrayQueue(); + final long now = mSystemClock.uptimeMillis(); + for (int i = 0; i < mMitigationCalls.size(); i++) { + normalized.addLast(mMitigationCalls.get(i) - now); + } + return normalized; + } + + /** * Sets the initial health check duration. * * @return the new health check state @@ -1582,6 +1632,16 @@ public class PackageWatchdog { private long toPositive(long value) { return value > 0 ? value : Long.MAX_VALUE; } + + /** Compares the equality of this object with another {@link MonitoredPackage}. */ + @VisibleForTesting + boolean isEqualTo(MonitoredPackage pkg) { + return (getName().equals(pkg.getName())) + && mDurationMs == pkg.mDurationMs + && mHasPassedHealthCheck == pkg.mHasPassedHealthCheck + && mHealthCheckDurationMs == pkg.mHealthCheckDurationMs + && (mMitigationCalls.toString()).equals(pkg.mMitigationCalls.toString()); + } } /** diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java index d30e9fb002e0..51e2b12bcee4 100644 --- a/services/core/java/com/android/server/SensorPrivacyService.java +++ b/services/core/java/com/android/server/SensorPrivacyService.java @@ -158,7 +158,7 @@ public final class SensorPrivacyService extends SystemService { XmlUtils.beginDocument(parser, XML_TAG_SENSOR_PRIVACY); parser.next(); String tagName = parser.getName(); - enabled = Boolean.valueOf(parser.getAttributeValue(null, XML_ATTRIBUTE_ENABLED)); + enabled = parser.getAttributeBoolean(null, XML_ATTRIBUTE_ENABLED, false); } catch (IOException | XmlPullParserException e) { Log.e(TAG, "Caught an exception reading the state from storage: ", e); // Delete the file to prevent the same error on subsequent calls and assume sensor diff --git a/services/core/java/com/android/server/accounts/AccountAuthenticatorCache.java b/services/core/java/com/android/server/accounts/AccountAuthenticatorCache.java index b9453db8eb0a..725bccf76bb9 100644 --- a/services/core/java/com/android/server/accounts/AccountAuthenticatorCache.java +++ b/services/core/java/com/android/server/accounts/AccountAuthenticatorCache.java @@ -27,10 +27,10 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; @@ -83,13 +83,13 @@ import java.io.IOException; private static class MySerializer implements XmlSerializerAndParser<AuthenticatorDescription> { @Override - public void writeAsXml(AuthenticatorDescription item, XmlSerializer out) + public void writeAsXml(AuthenticatorDescription item, TypedXmlSerializer out) throws IOException { out.attribute(null, "type", item.type); } @Override - public AuthenticatorDescription createFromXml(XmlPullParser parser) + public AuthenticatorDescription createFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { return AuthenticatorDescription.newKey(parser.getAttributeValue(null, "type")); } diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java index d99b195351f1..1f35b88c8cbd 100644 --- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java +++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java @@ -71,7 +71,6 @@ import android.util.Xml; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.XmlUtils; import com.android.internal.util.dump.DualDumpOutputStream; @@ -79,7 +78,6 @@ import com.android.server.FgThread; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.BufferedReader; import java.io.File; @@ -89,7 +87,6 @@ import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.SecureRandom; import java.util.AbstractMap; @@ -1757,21 +1754,21 @@ public class AdbDebuggingManager { dump.write("user_keys", AdbDebuggingManagerProto.USER_KEYS, FileUtils.readTextFile(new File("/data/misc/adb/adb_keys"), 0, null)); } catch (IOException e) { - Slog.e(TAG, "Cannot read user keys", e); + Slog.i(TAG, "Cannot read user keys", e); } try { dump.write("system_keys", AdbDebuggingManagerProto.SYSTEM_KEYS, FileUtils.readTextFile(new File("/adb_keys"), 0, null)); } catch (IOException e) { - Slog.e(TAG, "Cannot read system keys", e); + Slog.i(TAG, "Cannot read system keys", e); } try { dump.write("keystore", AdbDebuggingManagerProto.KEYSTORE, FileUtils.readTextFile(getAdbTempKeysFile(), 0, null)); } catch (IOException e) { - Slog.e(TAG, "Cannot read keystore: ", e); + Slog.i(TAG, "Cannot read keystore: ", e); } dump.end(token); @@ -1966,9 +1963,9 @@ public class AdbDebuggingManager { String key = parser.getAttributeValue(null, XML_ATTRIBUTE_KEY); long connectionTime; try { - connectionTime = Long.valueOf( - parser.getAttributeValue(null, XML_ATTRIBUTE_LAST_CONNECTION)); - } catch (NumberFormatException e) { + connectionTime = parser.getAttributeLong(null, + XML_ATTRIBUTE_LAST_CONNECTION); + } catch (XmlPullParserException e) { Slog.e(TAG, "Caught a NumberFormatException parsing the last connection time: " + e); @@ -2020,9 +2017,9 @@ public class AdbDebuggingManager { String key = parser.getAttributeValue(null, XML_ATTRIBUTE_KEY); long connectionTime; try { - connectionTime = Long.valueOf( - parser.getAttributeValue(null, XML_ATTRIBUTE_LAST_CONNECTION)); - } catch (NumberFormatException e) { + connectionTime = parser.getAttributeLong(null, + XML_ATTRIBUTE_LAST_CONNECTION); + } catch (XmlPullParserException e) { Slog.e(TAG, "Caught a NumberFormatException parsing the last connection time: " + e); @@ -2150,8 +2147,8 @@ public class AdbDebuggingManager { for (Map.Entry<String, Long> keyEntry : mKeyMap.entrySet()) { serializer.startTag(null, XML_TAG_ADB_KEY); serializer.attribute(null, XML_ATTRIBUTE_KEY, keyEntry.getKey()); - serializer.attribute(null, XML_ATTRIBUTE_LAST_CONNECTION, - String.valueOf(keyEntry.getValue())); + serializer.attributeLong(null, XML_ATTRIBUTE_LAST_CONNECTION, + keyEntry.getValue()); serializer.endTag(null, XML_TAG_ADB_KEY); } for (String bssid : mTrustedNetworks) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 63128ced8f93..bcd122d33f7d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -265,7 +265,6 @@ import android.os.UserManager; import android.os.WorkSource; import android.os.storage.IStorageManager; import android.os.storage.StorageManager; -import android.permission.PermissionManagerInternal.CheckPermissionDelegate; import android.provider.DeviceConfig; import android.provider.Settings; import android.server.ServerProtoEnums; @@ -326,7 +325,6 @@ import com.android.internal.util.MemInfoReader; import com.android.internal.util.Preconditions; import com.android.internal.util.function.HeptFunction; import com.android.internal.util.function.QuadFunction; -import com.android.internal.util.function.TriFunction; import com.android.server.AlarmManagerInternal; import com.android.server.AttributeCache; import com.android.server.DeviceIdleInternal; @@ -394,7 +392,6 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; -import java.util.function.BiFunction; public class ActivityManagerService extends IActivityManager.Stub implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { @@ -14486,7 +14483,7 @@ public class ActivityManagerService extends IActivityManager.Stub mAppOpsService.setMode(AppOpsManager.OP_NO_ISOLATED_STORAGE, app.uid, app.info.packageName, AppOpsManager.MODE_ERRORED); mAppOpsService.setAppOpsServiceDelegate(null); - getPermissionManagerInternalLocked().setCheckPermissionDelegate(null); + getPermissionManagerInternalLocked().stopShellPermissionIdentityDelegation(); mHandler.obtainMessage(SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG, instr.mUiAutomationConnection).sendToTarget(); } @@ -17201,12 +17198,6 @@ public class ActivityManagerService extends IActivityManager.Stub // We allow delegation only to one instrumentation started from the shell synchronized (ActivityManagerService.this) { - // If there is a delegate it should be the same instance for app ops and permissions. - if (mAppOpsService.getAppOpsServiceDelegate() - != getPermissionManagerInternalLocked().getCheckPermissionDelegate()) { - throw new IllegalStateException("Bad shell delegate state"); - } - // If the delegate is already set up for the target UID, nothing to do. if (mAppOpsService.getAppOpsServiceDelegate() != null) { if (!(mAppOpsService.getAppOpsServiceDelegate() instanceof ShellDelegate)) { @@ -17235,10 +17226,14 @@ public class ActivityManagerService extends IActivityManager.Stub } // Hook them up... - final ShellDelegate shellDelegate = new ShellDelegate( - instr.mTargetInfo.packageName, delegateUid, permissions); + final ShellDelegate shellDelegate = new ShellDelegate(delegateUid, + permissions); mAppOpsService.setAppOpsServiceDelegate(shellDelegate); - getPermissionManagerInternalLocked().setCheckPermissionDelegate(shellDelegate); + final String packageName = instr.mTargetInfo.packageName; + final List<String> permissionNames = permissions != null ? + Arrays.asList(permissions) : null; + getPermissionManagerInternalLocked().startShellPermissionIdentityDelegation( + delegateUid, packageName, permissionNames); return; } } @@ -17252,17 +17247,15 @@ public class ActivityManagerService extends IActivityManager.Stub } synchronized (ActivityManagerService.this) { mAppOpsService.setAppOpsServiceDelegate(null); - getPermissionManagerInternalLocked().setCheckPermissionDelegate(null); + getPermissionManagerInternalLocked().stopShellPermissionIdentityDelegation(); } } - private class ShellDelegate implements CheckOpsDelegate, CheckPermissionDelegate { - private final String mTargetPackageName; + private class ShellDelegate implements CheckOpsDelegate { private final int mTargetUid; private @Nullable String[] mPermissions; - ShellDelegate(String targetPackageName, int targetUid, @Nullable String[] permissions) { - mTargetPackageName = targetPackageName; + ShellDelegate(int targetUid, @Nullable String[] permissions) { mTargetUid = targetUid; mPermissions = permissions; } @@ -17325,34 +17318,6 @@ public class ActivityManagerService extends IActivityManager.Stub message, shouldCollectMessage); } - @Override - public int checkPermission(String permName, String pkgName, int userId, - TriFunction<String, String, Integer, Integer> superImpl) { - if (mTargetPackageName.equals(pkgName) && isTargetPermission(permName)) { - final long identity = Binder.clearCallingIdentity(); - try { - return superImpl.apply(permName, "com.android.shell", userId); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - return superImpl.apply(permName, pkgName, userId); - } - - @Override - public int checkUidPermission(String permName, int uid, - BiFunction<String, Integer, Integer> superImpl) { - if (uid == mTargetUid && isTargetPermission(permName)) { - final long identity = Binder.clearCallingIdentity(); - try { - return superImpl.apply(permName, Process.SHELL_UID); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - return superImpl.apply(permName, uid); - } - private boolean isTargetOp(int code) { // null permissions means all ops are targeted if (mPermissions == null) { diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 89150aee44ad..4d971a51e885 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -545,7 +545,6 @@ public class AudioService extends IAudioService.Stub AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET, AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET, AudioSystem.DEVICE_OUT_HDMI_ARC, - AudioSystem.DEVICE_OUT_SPDIF, AudioSystem.DEVICE_OUT_AUX_LINE)); // Devices for which the volume is always max, no volume panel Set<Integer> mFullVolumeDevices = new HashSet<>(); diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index 0194259d6289..75e1938656e3 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -30,6 +30,7 @@ import android.app.admin.DevicePolicyManager; import android.app.trust.ITrustManager; import android.content.ContentResolver; import android.content.Context; +import android.content.pm.PackageManager; import android.database.ContentObserver; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; @@ -809,12 +810,13 @@ public class BiometricService extends SystemService { public List<FingerprintSensorPropertiesInternal> getFingerprintSensorProperties( Context context) { - final FingerprintManager fpm = context.getSystemService(FingerprintManager.class); - if (fpm != null) { - return fpm.getSensorPropertiesInternal(); - } else { - return new ArrayList<>(); + if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { + final FingerprintManager fpm = context.getSystemService(FingerprintManager.class); + if (fpm != null) { + return fpm.getSensorPropertiesInternal(); + } } + return new ArrayList<>(); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java index 7dc2cba0b974..cb56e8cd4b7f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java @@ -127,17 +127,6 @@ public class FaceService extends SystemService implements BiometricServiceCallba return properties; } - @NonNull - private List<Face> getEnrolledFaces(int userId, String opPackageName) { - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); - if (provider == null) { - Slog.w(TAG, "Null provider for getEnrolledFaces, caller: " + opPackageName); - return Collections.emptyList(); - } - - return provider.second.getEnrolledFaces(provider.first, userId); - } - /** * Receives the incoming binder calls from FaceManager. */ @@ -438,6 +427,7 @@ public class FaceService extends SystemService implements BiometricServiceCallba pw.println("Dumping for sensorId: " + props.sensorId + ", provider: " + provider.getClass().getSimpleName()); provider.dumpInternal(props.sensorId, pw); + pw.println(); } } } @@ -471,7 +461,13 @@ public class FaceService extends SystemService implements BiometricServiceCallba Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS); } - return FaceService.this.getEnrolledFaces(userId, opPackageName); + final ServiceProvider provider = getProviderForSensor(sensorId); + if (provider == null) { + Slog.w(TAG, "Null provider for getEnrolledFaces, caller: " + opPackageName); + return Collections.emptyList(); + } + + return provider.getEnrolledFaces(sensorId, userId); } @Override // Binder call @@ -482,7 +478,16 @@ public class FaceService extends SystemService implements BiometricServiceCallba Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS); } - return !FaceService.this.getEnrolledFaces(userId, opPackageName).isEmpty(); + final ServiceProvider provider = getProviderForSensor(sensorId); + if (provider == null) { + Slog.w(TAG, "Null provider for hasEnrolledFaces, caller: " + opPackageName); + return false; + } + + final boolean enrolled = provider.getEnrolledFaces(sensorId, userId).size() > 0; + Slog.d(TAG, "hasEnrolledFaces, sensor: " + sensorId + ", enrolled: " + enrolled); + + return provider.getEnrolledFaces(sensorId, userId).size() > 0; } @Override // Binder call diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java index a0ffe58c779b..a0cd4a56ea12 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java @@ -72,13 +72,13 @@ public class FaceUtils implements BiometricUtils<Face> { } /** - * Legacy getter for {@link android.hardware.biometrics.face.V1_0} and its extended subclasses, - * which do not support a well defined sensorId from the HAL. + * Legacy getter for {@link android.hardware.biometrics.face.V1_0} and its extended subclasses. + * Framework-side cache is always stored in the same file, regardless of sensorId. */ - public static FaceUtils getInstance() { + public static FaceUtils getLegacyInstance(int sensorId) { // Note that sensorId for legacy services can be hard-coded to 0 since it's only used // to index into the sensor states map. - return getInstance(0 /* sensorId */, LEGACY_FACE_FILE); + return getInstance(sensorId, LEGACY_FACE_FILE); } private FaceUtils(String fileName) { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java index 4c983fb5fa6c..9ed8f789aaa2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java @@ -153,7 +153,8 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { Utils.checkPermission(mContext, TEST_BIOMETRIC); // Fake authentication with any of the existing fingers - List<Face> faces = FaceUtils.getInstance().getBiometricsForUser(mContext, userId); + List<Face> faces = FaceUtils.getLegacyInstance(mSensorId) + .getBiometricsForUser(mContext, userId); if (faces.isEmpty()) { Slog.w(TAG, "No faces, returning"); return; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index d384bc645d61..c4e4d1fe0f82 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -166,7 +166,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @Override public void onEnrollResult(long deviceId, int faceId, int userId, int remaining) { mHandler.post(() -> { - final CharSequence name = FaceUtils.getInstance() + final CharSequence name = FaceUtils.getLegacyInstance(mSensorId) .getUniqueName(mContext, userId); final Face face = new Face(name, faceId, deviceId); @@ -471,7 +471,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @Override @NonNull public List<Face> getEnrolledFaces(int sensorId, int userId) { - return FaceUtils.getInstance().getBiometricsForUser(mContext, userId); + return FaceUtils.getLegacyInstance(mSensorId).getBiometricsForUser(mContext, userId); } @Override @@ -610,8 +610,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, - opPackageName, FaceUtils.getInstance(), disabledFeatures, ENROLL_TIMEOUT_SEC, - surfaceHandle, mSensorId); + opPackageName, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures, + ENROLL_TIMEOUT_SEC, surfaceHandle, mSensorId); mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() { @Override @@ -665,7 +665,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final FaceRemovalClient client = new FaceRemovalClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), faceId, userId, opPackageName, - FaceUtils.getInstance(), mSensorId, mAuthenticatorIds); + FaceUtils.getLegacyInstance(mSensorId), mSensorId, mAuthenticatorIds); mScheduler.scheduleClientMonitor(client); }); } @@ -748,7 +748,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final List<Face> enrolledList = getEnrolledFaces(mSensorId, userId); final FaceInternalCleanupClient client = new FaceInternalCleanupClient(mContext, mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId, enrolledList, - FaceUtils.getInstance(), mAuthenticatorIds); + FaceUtils.getLegacyInstance(mSensorId), mAuthenticatorIds); mScheduler.scheduleClientMonitor(client); }); } @@ -777,7 +777,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final long userToken = proto.start(SensorStateProto.USER_STATES); proto.write(UserStateProto.USER_ID, userId); - proto.write(UserStateProto.NUM_ENROLLED, FaceUtils.getInstance() + proto.write(UserStateProto.NUM_ENROLLED, FaceUtils.getLegacyInstance(mSensorId) .getBiometricsForUser(mContext, userId).size()); proto.end(userToken); } @@ -801,7 +801,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { JSONArray sets = new JSONArray(); for (UserInfo user : UserManager.get(mContext).getUsers()) { final int userId = user.getUserHandle().getIdentifier(); - final int c = FaceUtils.getInstance().getBiometricsForUser(mContext, userId).size(); + final int c = FaceUtils.getLegacyInstance(mSensorId) + .getBiometricsForUser(mContext, userId).size(); JSONObject set = new JSONObject(); set.put("id", userId); set.put("count", c); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 0cbd440a6a25..61f9cc40d233 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -587,13 +587,13 @@ public class FingerprintService extends SystemService implements BiometricServic @Nullable byte [] hardwareAuthToken, String opPackageName) { Utils.checkPermission(getContext(), RESET_FINGERPRINT_LOCKOUT); - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for resetLockout, caller: " + opPackageName); return; } - provider.second.scheduleResetLockout(sensorId, userId, hardwareAuthToken); + provider.scheduleResetLockout(sensorId, userId, hardwareAuthToken); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java index 6da86502b64c..b3d2419901e4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java @@ -75,12 +75,12 @@ public class FingerprintUtils implements BiometricUtils<Fingerprint> { /** * Legacy getter for {@link android.hardware.biometrics.fingerprint.V2_1} ands its extended - * subclasses, which do not support a well defined sensorId from the HAL. + * subclasses. Framework-side cache is always stored in the same file, regardless of sensorId. */ - public static FingerprintUtils getInstance() { + public static FingerprintUtils getLegacyInstance(int sensorId) { // Note that sensorId for legacy services can be hard-coded to 0 since it's only used // to index into the sensor states map. - return getInstance(0 /* sensorId */, LEGACY_FINGERPRINT_FILE); + return getInstance(sensorId, LEGACY_FINGERPRINT_FILE); } private FingerprintUtils(String fileName) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java index 65ce34d31b61..74549b917e82 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java @@ -144,7 +144,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { Utils.checkPermission(mContext, TEST_BIOMETRIC); // Fake authentication with any of the existing fingers - List<Fingerprint> fingerprints = FingerprintUtils.getInstance() + List<Fingerprint> fingerprints = FingerprintUtils.getLegacyInstance(mSensorId) .getBiometricsForUser(mContext, userId); if (fingerprints.isEmpty()) { Slog.w(TAG, "No fingerprints, returning"); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index 7c5b7c92c1c6..b8d27aa61806 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -111,6 +111,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @NonNull private final HalResultController mHalResultController; @Nullable private IUdfpsOverlayController mUdfpsOverlayController; private int mCurrentUserId = UserHandle.USER_NULL; + private final int mSensorId; private final class BiometricTaskStackListener extends TaskStackListener { @Override @@ -167,13 +168,15 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider void onHardwareUnavailable(); } + private final int mSensorId; @NonNull private final Context mContext; @NonNull final Handler mHandler; @NonNull final BiometricScheduler mScheduler; @Nullable private Callback mCallback; - HalResultController(@NonNull Context context, @NonNull Handler handler, + HalResultController(int sensorId, @NonNull Context context, @NonNull Handler handler, @NonNull BiometricScheduler scheduler) { + mSensorId = sensorId; mContext = context; mHandler = handler; mScheduler = scheduler; @@ -194,7 +197,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider } final int currentUserId = client.getTargetUserId(); - final CharSequence name = FingerprintUtils.getInstance() + final CharSequence name = FingerprintUtils.getLegacyInstance(mSensorId) .getUniqueName(mContext, currentUserId); final Fingerprint fingerprint = new Fingerprint(name, groupId, fingerId, deviceId); @@ -307,6 +310,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull HalResultController controller) { mContext = context; + mSensorId = sensorId; mScheduler = scheduler; mHandler = handler; mActivityTaskManager = ActivityTaskManager.getInstance(); @@ -361,7 +365,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider final Handler handler = new Handler(Looper.getMainLooper()); final BiometricScheduler scheduler = new BiometricScheduler(TAG, gestureAvailabilityDispatcher); - final HalResultController controller = new HalResultController(context, handler, scheduler); + final HalResultController controller = new HalResultController(sensorId, context, handler, + scheduler); return new Fingerprint21(context, scheduler, handler, sensorId, strength, lockoutResetDispatcher, controller); } @@ -549,7 +554,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, - hardwareAuthToken, opPackageName, FingerprintUtils.getInstance(), + hardwareAuthToken, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId), ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId, mUdfpsOverlayController); mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() { @Override @@ -624,7 +629,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId, - userId, opPackageName, FingerprintUtils.getInstance(), + userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId), mSensorProperties.sensorId, mAuthenticatorIds); mScheduler.scheduleClientMonitor(client); }); @@ -638,8 +643,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mSensorProperties.sensorId, userId); final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient( mContext, mLazyDaemon, userId, mContext.getOpPackageName(), - mSensorProperties.sensorId, enrolledList, FingerprintUtils.getInstance(), - mAuthenticatorIds); + mSensorProperties.sensorId, enrolledList, + FingerprintUtils.getLegacyInstance(mSensorId), mAuthenticatorIds); mScheduler.scheduleClientMonitor(client); }); } @@ -657,14 +662,15 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @Override public void rename(int sensorId, int fingerId, int userId, @NonNull String name) { mHandler.post(() -> { - FingerprintUtils.getInstance().renameBiometricForUser(mContext, userId, fingerId, name); + FingerprintUtils.getLegacyInstance(mSensorId) + .renameBiometricForUser(mContext, userId, fingerId, name); }); } @Override @NonNull public List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId) { - return FingerprintUtils.getInstance().getBiometricsForUser(mContext, userId); + return FingerprintUtils.getLegacyInstance(mSensorId).getBiometricsForUser(mContext, userId); } @Override @@ -716,7 +722,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider final long userToken = proto.start(SensorStateProto.USER_STATES); proto.write(UserStateProto.USER_ID, userId); - proto.write(UserStateProto.NUM_ENROLLED, FingerprintUtils.getInstance() + proto.write(UserStateProto.NUM_ENROLLED, FingerprintUtils.getLegacyInstance(mSensorId) .getBiometricsForUser(mContext, userId).size()); proto.end(userToken); } @@ -737,7 +743,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider proto.write(FingerprintUserStatsProto.USER_ID, userId); proto.write(FingerprintUserStatsProto.NUM_FINGERPRINTS, - FingerprintUtils.getInstance().getBiometricsForUser(mContext, userId).size()); + FingerprintUtils.getLegacyInstance(mSensorId) + .getBiometricsForUser(mContext, userId).size()); // Normal fingerprint authentications (e.g. lockscreen) long countsToken = proto.start(FingerprintUserStatsProto.NORMAL); @@ -777,7 +784,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider JSONArray sets = new JSONArray(); for (UserInfo user : UserManager.get(mContext).getUsers()) { final int userId = user.getUserHandle().getIdentifier(); - final int N = FingerprintUtils.getInstance() + final int N = FingerprintUtils.getLegacyInstance(mSensorId) .getBiometricsForUser(mContext, userId).size(); JSONObject set = new JSONObject(); set.put("id", userId); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java index e4933e40ccd5..791d224b9728 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java @@ -210,9 +210,9 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage @NonNull private Fingerprint21UdfpsMock mFingerprint21; @Nullable private LastAuthArgs mLastAuthArgs; - MockHalResultController(@NonNull Context context, @NonNull Handler handler, + MockHalResultController(int sensorId, @NonNull Context context, @NonNull Handler handler, @NonNull BiometricScheduler scheduler) { - super(context, handler, scheduler); + super(sensorId, context, handler, scheduler); } void init(@NonNull RestartAuthRunnable restartAuthRunnable, @@ -280,7 +280,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage final TestableBiometricScheduler scheduler = new TestableBiometricScheduler(TAG, gestureAvailabilityDispatcher); final MockHalResultController controller = - new MockHalResultController(context, handler, scheduler); + new MockHalResultController(sensorId, context, handler, scheduler); return new Fingerprint21UdfpsMock(context, scheduler, handler, sensorId, strength, lockoutResetDispatcher, controller); } diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java index d27cb16ecc51..668142b7e8ee 100644 --- a/services/core/java/com/android/server/content/SyncStorageEngine.java +++ b/services/core/java/com/android/server/content/SyncStorageEngine.java @@ -1804,10 +1804,8 @@ public class SyncStorageEngine { int id = -1; try { id = parser.getAttributeInt(null, "id"); - } catch (NumberFormatException e) { + } catch (XmlPullParserException e) { Slog.e(TAG, "error parsing the id of the authority", e); - } catch (NullPointerException e) { - Slog.e(TAG, "the id of the authority is null", e); } if (id >= 0) { String authorityName = parser.getAttributeValue(null, "authority"); @@ -1920,28 +1918,26 @@ public class SyncStorageEngine { private void parseExtra(TypedXmlPullParser parser, Bundle extras) { String name = parser.getAttributeValue(null, "name"); String type = parser.getAttributeValue(null, "type"); - String value1 = parser.getAttributeValue(null, "value1"); - String value2 = parser.getAttributeValue(null, "value2"); try { if ("long".equals(type)) { - extras.putLong(name, Long.parseLong(value1)); + extras.putLong(name, parser.getAttributeLong(null, "value1")); } else if ("integer".equals(type)) { - extras.putInt(name, Integer.parseInt(value1)); + extras.putInt(name, parser.getAttributeInt(null, "value1")); } else if ("double".equals(type)) { - extras.putDouble(name, Double.parseDouble(value1)); + extras.putDouble(name, parser.getAttributeDouble(null, "value1")); } else if ("float".equals(type)) { - extras.putFloat(name, Float.parseFloat(value1)); + extras.putFloat(name, parser.getAttributeFloat(null, "value1")); } else if ("boolean".equals(type)) { - extras.putBoolean(name, Boolean.parseBoolean(value1)); + extras.putBoolean(name, parser.getAttributeBoolean(null, "value1")); } else if ("string".equals(type)) { - extras.putString(name, value1); + extras.putString(name, parser.getAttributeValue(null, "value1")); } else if ("account".equals(type)) { + final String value1 = parser.getAttributeValue(null, "value1"); + final String value2 = parser.getAttributeValue(null, "value2"); extras.putParcelable(name, new Account(value1, value2)); } - } catch (NumberFormatException e) { - Slog.e(TAG, "error parsing bundle value", e); - } catch (NullPointerException e) { + } catch (XmlPullParserException e) { Slog.e(TAG, "error parsing bundle value", e); } } diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java index 2a0e21919704..7a8ba9f4380c 100644 --- a/services/core/java/com/android/server/display/BrightnessTracker.java +++ b/services/core/java/com/android/server/display/BrightnessTracker.java @@ -668,12 +668,11 @@ public class BrightnessTracker { builder.setUserBrightnessPoint( parser.getAttributeBoolean(null, ATTR_USER_POINT, false)); - String colorSampleDurationString = - parser.getAttributeValue(null, ATTR_COLOR_SAMPLE_DURATION); + long colorSampleDuration = + parser.getAttributeLong(null, ATTR_COLOR_SAMPLE_DURATION, -1); String colorValueBucketsString = parser.getAttributeValue(null, ATTR_COLOR_VALUE_BUCKETS); - if (colorSampleDurationString != null && colorValueBucketsString != null) { - long colorSampleDuration = Long.parseLong(colorSampleDurationString); + if (colorSampleDuration != -1 && colorValueBucketsString != null) { String[] buckets = colorValueBucketsString.split(","); long[] bucketValues = new long[buckets.length]; for (int i = 0; i < bucketValues.length; ++i) { diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java index b0820e81ec09..a62642b1f842 100644 --- a/services/core/java/com/android/server/display/PersistentDataStore.java +++ b/services/core/java/com/android/server/display/PersistentDataStore.java @@ -31,17 +31,12 @@ import android.util.Xml; import android.view.Display; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; import libcore.io.IoUtils; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -49,7 +44,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -628,15 +622,7 @@ final class PersistentDataStore { } String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME); - String timeStampString = parser.getAttributeValue(null, ATTR_TIME_STAMP); - long timeStamp = -1; - if (timeStampString != null) { - try { - timeStamp = Long.parseLong(timeStampString); - } catch (NumberFormatException nfe) { - // Ignore we will just not restore the timestamp. - } - } + long timeStamp = parser.getAttributeLong(null, ATTR_TIME_STAMP, -1); try { BrightnessConfiguration config = diff --git a/services/core/java/com/android/server/input/PersistentDataStore.java b/services/core/java/com/android/server/input/PersistentDataStore.java index a735a8f9d305..6cec2723b141 100644 --- a/services/core/java/com/android/server/input/PersistentDataStore.java +++ b/services/core/java/com/android/server/input/PersistentDataStore.java @@ -510,7 +510,7 @@ final class PersistentDataStore { serializer.startTag(null, "keyboard-layout"); serializer.attribute(null, "descriptor", layout); if (layout.equals(mCurrentKeyboardLayout)) { - serializer.attribute(null, "current", "true"); + serializer.attributeBoolean(null, "current", true); } serializer.endTag(null, "keyboard-layout"); } diff --git a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java index b4bcd7b10c42..fd8cf707e697 100644 --- a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java +++ b/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java @@ -55,7 +55,7 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider { @Override void onInitialize() { - mProxy.setListener(new LocationTimeZoneProviderProxy.Listener() { + mProxy.initialize(new LocationTimeZoneProviderProxy.Listener() { @Override public void onReportLocationTimeZoneEvent( @NonNull LocationTimeZoneEvent locationTimeZoneEvent) { diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java index b7c7476844a5..c8a1db6681ff 100644 --- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java +++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java @@ -19,6 +19,7 @@ package com.android.server.location.timezone; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.content.res.Resources; import android.os.Binder; import android.os.ResultReceiver; import android.os.ShellCallback; @@ -27,6 +28,7 @@ import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Slog; +import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.DumpUtils; import com.android.server.FgThread; @@ -179,36 +181,45 @@ public class LocationTimeZoneManagerService extends Binder { } private LocationTimeZoneProvider createPrimaryProvider() { + Resources resources = mContext.getResources(); + if (!resources.getBoolean(R.bool.config_enablePrimaryLocationTimeZoneProvider)) { + return new NullLocationTimeZoneProvider(mThreadingDomain, PRIMARY_PROVIDER_NAME); + } + LocationTimeZoneProviderProxy proxy; if (isInSimulationMode(PRIMARY_PROVIDER_NAME)) { proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain); } else { - proxy = RealLocationTimeZoneProviderProxy.createAndRegister( + proxy = new RealLocationTimeZoneProviderProxy( mContext, mThreadingDomain, PRIMARY_LOCATION_TIME_ZONE_SERVICE_ACTION, - com.android.internal.R.bool.config_enablePrimaryLocationTimeZoneOverlay, - com.android.internal.R.string.config_primaryLocationTimeZoneProviderPackageName + R.bool.config_enablePrimaryLocationTimeZoneOverlay, + R.string.config_primaryLocationTimeZoneProviderPackageName ); } - return createLocationTimeZoneProvider(PRIMARY_PROVIDER_NAME, proxy); + return new BinderLocationTimeZoneProvider(mThreadingDomain, PRIMARY_PROVIDER_NAME, proxy); } private LocationTimeZoneProvider createSecondaryProvider() { + Resources resources = mContext.getResources(); + if (!resources.getBoolean(R.bool.config_enableSecondaryLocationTimeZoneProvider)) { + return new NullLocationTimeZoneProvider(mThreadingDomain, SECONDARY_PROVIDER_NAME); + } + LocationTimeZoneProviderProxy proxy; if (isInSimulationMode(SECONDARY_PROVIDER_NAME)) { proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain); } else { - proxy = RealLocationTimeZoneProviderProxy.createAndRegister( + proxy = new RealLocationTimeZoneProviderProxy( mContext, mThreadingDomain, SECONDARY_LOCATION_TIME_ZONE_SERVICE_ACTION, - com.android.internal.R.bool.config_enableSecondaryLocationTimeZoneOverlay, - com.android.internal.R.string - .config_secondaryLocationTimeZoneProviderPackageName + R.bool.config_enableSecondaryLocationTimeZoneOverlay, + R.string.config_secondaryLocationTimeZoneProviderPackageName ); } - return createLocationTimeZoneProvider(SECONDARY_PROVIDER_NAME, proxy); + return new BinderLocationTimeZoneProvider(mThreadingDomain, SECONDARY_PROVIDER_NAME, proxy); } private boolean isInSimulationMode(String providerName) { @@ -216,21 +227,6 @@ public class LocationTimeZoneManagerService extends Binder { SIMULATION_MODE_SYSTEM_PROPERTY_PREFIX + providerName, false); } - private LocationTimeZoneProvider createLocationTimeZoneProvider( - @NonNull String providerName, @NonNull LocationTimeZoneProviderProxy proxy) { - LocationTimeZoneProvider provider; - if (proxy != null) { - debugLog("LocationTimeZoneProvider found for providerName=" + providerName); - provider = new BinderLocationTimeZoneProvider(mThreadingDomain, - providerName, proxy); - } else { - debugLog("No LocationTimeZoneProvider found for providerName=" + providerName - + ": stubbing"); - provider = new NullLocationTimeZoneProvider(mThreadingDomain, providerName); - } - return provider; - } - @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java index 1b4706f8801e..8a0259d12a6c 100644 --- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java +++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java @@ -70,9 +70,11 @@ abstract class LocationTimeZoneProviderProxy implements Dumpable { } /** - * Sets the listener. The listener can expect to receive all events after this point. + * Initializes the proxy. The supplied listener can expect to receive all events after this + * point. This method also calls {@link #onInitialize()} for subclasses to handle their own + * initialization. */ - void setListener(@NonNull Listener listener) { + void initialize(@NonNull Listener listener) { Objects.requireNonNull(listener); synchronized (mSharedLock) { if (mListener != null) { @@ -80,9 +82,15 @@ abstract class LocationTimeZoneProviderProxy implements Dumpable { } this.mListener = listener; } + onInitialize(); } /** + * Initializes the proxy. This is called after {@link #mListener} is set. + */ + abstract void onInitialize(); + + /** * Sets a new request for the provider. */ abstract void setRequest(@NonNull LocationTimeZoneProviderRequest request); diff --git a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java index cd6d3592af0e..1a012882305f 100644 --- a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java +++ b/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java @@ -41,23 +41,6 @@ import java.util.Objects; */ class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy { - /** - * Creates and registers this proxy. If no suitable service is available for the proxy, returns - * null. - */ - @Nullable - static LocationTimeZoneProviderProxy createAndRegister( - @NonNull Context context, @NonNull ThreadingDomain threadingDomain, - @NonNull String action, int enableOverlayResId, int nonOverlayPackageResId) { - RealLocationTimeZoneProviderProxy proxy = new RealLocationTimeZoneProviderProxy( - context, threadingDomain, action, enableOverlayResId, nonOverlayPackageResId); - if (proxy.register()) { - return proxy; - } else { - return null; - } - } - @NonNull private final ServiceWatcher mServiceWatcher; @GuardedBy("mProxyLock") @@ -66,7 +49,7 @@ class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy { @GuardedBy("mProxyLock") @NonNull private LocationTimeZoneProviderRequest mRequest; - private RealLocationTimeZoneProviderProxy( + RealLocationTimeZoneProviderProxy( @NonNull Context context, @NonNull ThreadingDomain threadingDomain, @NonNull String action, int enableOverlayResId, int nonOverlayPackageResId) { @@ -77,6 +60,13 @@ class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy { enableOverlayResId, nonOverlayPackageResId); } + @Override + void onInitialize() { + if (!register()) { + throw new IllegalStateException("Unable to register binder proxy"); + } + } + private boolean register() { boolean resolves = mServiceWatcher.checkServiceResolves(); if (resolves) { diff --git a/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java index 604ff74e71ac..5e66a99a93fa 100644 --- a/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java +++ b/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java @@ -49,6 +49,11 @@ class SimulatedLocationTimeZoneProviderProxy extends LocationTimeZoneProviderPro mRequest = LocationTimeZoneProviderRequest.EMPTY_REQUEST; } + @Override + void onInitialize() { + // No-op - nothing to do for the simulated provider. + } + void simulate(@NonNull SimulatedBinderProviderEvent event) { mThreadingDomain.assertCurrentThread(); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index ee860e3ac6d7..9efbb6964aeb 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -277,6 +277,7 @@ import com.android.server.pm.PackageManagerService; import com.android.server.policy.PhoneWindowManager; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.uri.UriGrantsManagerInternal; +import com.android.server.utils.quota.MultiRateLimiter; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.BackgroundActivityStartCallback; import com.android.server.wm.WindowManagerInternal; @@ -298,6 +299,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -371,6 +373,20 @@ public class NotificationManagerService extends SystemService { RoleManager.ROLE_EMERGENCY }; + // Used for rate limiting toasts by package. + static final String TOAST_QUOTA_TAG = "toast_quota_tag"; + + // This constant defines rate limits applied to showing toasts. The numbers are set in a way + // such that an aggressive toast showing strategy would result in a roughly 1.5x longer wait + // time (before the package is allowed to show toasts again) each time the toast rate limit is + // reached. It's meant to protect the user against apps spamming them with toasts (either + // accidentally or on purpose). + private static final MultiRateLimiter.RateLimit[] TOAST_RATE_LIMITS = { + MultiRateLimiter.RateLimit.create(3, Duration.ofSeconds(20)), + MultiRateLimiter.RateLimit.create(5, Duration.ofSeconds(42)), + MultiRateLimiter.RateLimit.create(6, Duration.ofSeconds(68)), + }; + // When #matchesCallFilter is called from the ringer, wait at most // 3s to resolve the contacts. This timeout is required since // ContactsProvider might take a long time to start up. @@ -422,6 +438,16 @@ public class NotificationManagerService extends SystemService { @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R) private static final long NOTIFICATION_TRAMPOLINE_BLOCK = 167676448L; + /** + * Rate limit showing toasts, on a per package basis. + * + * It limits the effects of {@link android.widget.Toast#show()} calls to prevent overburdening + * the user with too many toasts in a limited time. Any attempt to show more toasts than allowed + * in a certain time frame will result in the toast being discarded. + */ + @ChangeId + private static final long RATE_LIMIT_TOASTS = 154198299L; + private IActivityManager mAm; private ActivityTaskManagerInternal mAtm; private ActivityManager mActivityManager; @@ -500,6 +526,9 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mToastQueue") private boolean mIsCurrentToastShown = false; + // Used for rate limiting toasts by package. + private MultiRateLimiter mToastRateLimiter; + // The last key in this list owns the hardware. ArrayList<String> mLights = new ArrayList<>(); @@ -1662,6 +1691,11 @@ public class NotificationManagerService extends SystemService { = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_HISTORY_ENABLED); private final Uri NOTIFICATION_SHOW_MEDIA_ON_QUICK_SETTINGS_URI = Settings.Global.getUriFor(Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS); + private final Uri LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS + = Settings.Secure.getUriFor( + Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + private final Uri LOCK_SCREEN_SHOW_NOTIFICATIONS + = Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS); SettingsObserver(Handler handler) { super(handler); @@ -1681,6 +1715,11 @@ public class NotificationManagerService extends SystemService { false, this, UserHandle.USER_ALL); resolver.registerContentObserver(NOTIFICATION_SHOW_MEDIA_ON_QUICK_SETTINGS_URI, false, this, UserHandle.USER_ALL); + + resolver.registerContentObserver(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, + false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(LOCK_SCREEN_SHOW_NOTIFICATIONS, + false, this, UserHandle.USER_ALL); update(null); } @@ -1722,6 +1761,12 @@ public class NotificationManagerService extends SystemService { if (uri == null || NOTIFICATION_SHOW_MEDIA_ON_QUICK_SETTINGS_URI.equals(uri)) { mPreferencesHelper.updateMediaNotificationFilteringEnabled(); } + if (uri == null || LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS.equals(uri)) { + mPreferencesHelper.updateLockScreenPrivateNotifications(); + } + if (uri == null || LOCK_SCREEN_SHOW_NOTIFICATIONS.equals(uri)) { + mPreferencesHelper.updateLockScreenShowNotifications(); + } } } @@ -1915,7 +1960,8 @@ public class NotificationManagerService extends SystemService { DevicePolicyManagerInternal dpm, IUriGrantsManager ugm, UriGrantsManagerInternal ugmInternal, AppOpsManager appOps, UserManager userManager, NotificationHistoryManager historyManager, StatsManager statsManager, - TelephonyManager telephonyManager, ActivityManagerInternal ami) { + TelephonyManager telephonyManager, ActivityManagerInternal ami, + MultiRateLimiter toastRateLimiter) { mHandler = handler; Resources resources = getContext().getResources(); mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(), @@ -2107,6 +2153,8 @@ public class NotificationManagerService extends SystemService { com.android.internal.R.array.config_notificationMsgPkgsAllowedAsConvos)); mStatsManager = statsManager; + mToastRateLimiter = toastRateLimiter; + // register for various Intents. // If this is called within a test, make sure to unregister the intent receivers by // calling onDestroy() @@ -2217,7 +2265,8 @@ public class NotificationManagerService extends SystemService { mStatsManager = (StatsManager) getContext().getSystemService( Context.STATS_MANAGER), getContext().getSystemService(TelephonyManager.class), - LocalServices.getService(ActivityManagerInternal.class)); + LocalServices.getService(ActivityManagerInternal.class), + createToastRateLimiter()); publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL); @@ -2855,6 +2904,10 @@ public class NotificationManagerService extends SystemService { return mInternalService; } + private MultiRateLimiter createToastRateLimiter() { + return new MultiRateLimiter.Builder(getContext()).addRateLimits(TOAST_RATE_LIMITS).build(); + } + @VisibleForTesting final IBinder mService = new INotificationManager.Stub() { // Toasts @@ -3591,8 +3644,9 @@ public class NotificationManagerService extends SystemService { public ParceledListSlice<ConversationChannelWrapper> getConversations( boolean onlyImportant) { enforceSystemOrSystemUI("getConversations"); + IntArray userIds = mUserProfiles.getCurrentProfileIds(); ArrayList<ConversationChannelWrapper> conversations = - mPreferencesHelper.getConversations(onlyImportant); + mPreferencesHelper.getConversations(userIds, onlyImportant); for (ConversationChannelWrapper conversation : conversations) { if (mShortcutHelper == null) { conversation.setShortcutInfo(null); @@ -7329,10 +7383,21 @@ public class NotificationManagerService extends SystemService { ToastRecord record = mToastQueue.get(0); while (record != null) { - if (record.show()) { + int userId = UserHandle.getUserId(record.uid); + boolean rateLimitingEnabled = + CompatChanges.isChangeEnabled(RATE_LIMIT_TOASTS, record.uid); + boolean isWithinQuota = + mToastRateLimiter.isWithinQuota(userId, record.pkg, TOAST_QUOTA_TAG); + if ((!rateLimitingEnabled || isWithinQuota) && record.show()) { scheduleDurationReachedLocked(record); mIsCurrentToastShown = true; + if (rateLimitingEnabled) { + mToastRateLimiter.noteEvent(userId, record.pkg, TOAST_QUOTA_TAG); + } return; + } else if (rateLimitingEnabled && !isWithinQuota) { + Slog.w(TAG, "Package " + record.pkg + " is above allowed toast quota, the " + + "following toast was blocked and discarded: " + record); } int index = mToastQueue.indexOf(record); if (index >= 0) { diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 1c0349d1b51f..cbd973aeaf6a 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -52,6 +52,7 @@ import android.service.notification.RankingHelperProto; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.IntArray; import android.util.Pair; import android.util.Slog; import android.util.SparseBooleanArray; @@ -170,6 +171,8 @@ public class PreferencesHelper implements RankingConfig { private final AppOpsManager mAppOps; private SparseBooleanArray mBadgingEnabled; + private SparseBooleanArray mLockScreenShowNotifications; + private SparseBooleanArray mLockScreenPrivateNotifications; private boolean mBubblesEnabledGlobally = DEFAULT_GLOBAL_ALLOW_BUBBLE; private boolean mIsMediaNotificationFilteringEnabled = DEFAULT_MEDIA_NOTIFICATION_FILTERING; private boolean mAreChannelsBypassingDnd; @@ -1379,36 +1382,39 @@ public class PreferencesHelper implements RankingConfig { return null; } - public ArrayList<ConversationChannelWrapper> getConversations(boolean onlyImportant) { + public ArrayList<ConversationChannelWrapper> getConversations(IntArray userIds, + boolean onlyImportant) { synchronized (mPackagePreferences) { ArrayList<ConversationChannelWrapper> conversations = new ArrayList<>(); - for (PackagePreferences p : mPackagePreferences.values()) { - int N = p.channels.size(); - for (int i = 0; i < N; i++) { - final NotificationChannel nc = p.channels.valueAt(i); - if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted() - && !nc.isDemoted() - && (nc.isImportantConversation() || !onlyImportant)) { - ConversationChannelWrapper conversation = new ConversationChannelWrapper(); - conversation.setPkg(p.pkg); - conversation.setUid(p.uid); - conversation.setNotificationChannel(nc); - conversation.setParentChannelLabel( - p.channels.get(nc.getParentChannelId()).getName()); - boolean blockedByGroup = false; - if (nc.getGroup() != null) { - NotificationChannelGroup group = p.groups.get(nc.getGroup()); - if (group != null) { - if (group.isBlocked()) { - blockedByGroup = true; - } else { - conversation.setGroupLabel(group.getName()); + if (userIds.binarySearch(UserHandle.getUserId(p.uid)) >= 0) { + int N = p.channels.size(); + for (int i = 0; i < N; i++) { + final NotificationChannel nc = p.channels.valueAt(i); + if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted() + && !nc.isDemoted() + && (nc.isImportantConversation() || !onlyImportant)) { + ConversationChannelWrapper conversation = + new ConversationChannelWrapper(); + conversation.setPkg(p.pkg); + conversation.setUid(p.uid); + conversation.setNotificationChannel(nc); + conversation.setParentChannelLabel( + p.channels.get(nc.getParentChannelId()).getName()); + boolean blockedByGroup = false; + if (nc.getGroup() != null) { + NotificationChannelGroup group = p.groups.get(nc.getGroup()); + if (group != null) { + if (group.isBlocked()) { + blockedByGroup = true; + } else { + conversation.setGroupLabel(group.getName()); + } } } - } - if (!blockedByGroup) { - conversations.add(conversation); + if (!blockedByGroup) { + conversations.add(conversation); + } } } } @@ -2401,6 +2407,60 @@ public class PreferencesHelper implements RankingConfig { return mBadgingEnabled.get(userId, DEFAULT_SHOW_BADGE); } + public void updateLockScreenPrivateNotifications() { + if (mLockScreenPrivateNotifications == null) { + mLockScreenPrivateNotifications = new SparseBooleanArray(); + } + boolean changed = false; + // update the cached values + for (int index = 0; index < mLockScreenPrivateNotifications.size(); index++) { + int userId = mLockScreenPrivateNotifications.keyAt(index); + final boolean oldValue = mLockScreenPrivateNotifications.get(userId); + final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, userId) != 0; + mLockScreenPrivateNotifications.put(userId, newValue); + changed |= oldValue != newValue; + } + if (changed) { + updateConfig(); + } + } + + public void updateLockScreenShowNotifications() { + if (mLockScreenShowNotifications == null) { + mLockScreenShowNotifications = new SparseBooleanArray(); + } + boolean changed = false; + // update the cached values + for (int index = 0; index < mLockScreenShowNotifications.size(); index++) { + int userId = mLockScreenShowNotifications.keyAt(index); + final boolean oldValue = mLockScreenShowNotifications.get(userId); + final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, userId) != 0; + mLockScreenShowNotifications.put(userId, newValue); + changed |= oldValue != newValue; + } + if (changed) { + updateConfig(); + } + } + + @Override + public boolean canShowNotificationsOnLockscreen(int userId) { + if (mLockScreenShowNotifications == null) { + mLockScreenShowNotifications = new SparseBooleanArray(); + } + return mLockScreenShowNotifications.get(userId, true); + } + + @Override + public boolean canShowPrivateNotificationsOnLockScreen(int userId) { + if (mLockScreenPrivateNotifications == null) { + mLockScreenPrivateNotifications = new SparseBooleanArray(); + } + return mLockScreenPrivateNotifications.get(userId, true); + } + public void unlockAllNotificationChannels() { synchronized (mPackagePreferences) { final int numPackagePreferences = mPackagePreferences.size(); diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java index b2827bae40bc..8991ced21fbd 100644 --- a/services/core/java/com/android/server/notification/RankingConfig.java +++ b/services/core/java/com/android/server/notification/RankingConfig.java @@ -34,6 +34,8 @@ public interface RankingConfig { /** Returns true when feature is enabled that shows media notifications in quick settings. */ boolean isMediaNotificationFilteringEnabled(); boolean isGroupBlocked(String packageName, int uid, String groupId); + boolean canShowNotificationsOnLockscreen(int userId); + boolean canShowPrivateNotificationsOnLockScreen(int userId); Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg, int uid); diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java index b5ca2abf8afe..2122b9c19805 100644 --- a/services/core/java/com/android/server/notification/SnoozeHelper.java +++ b/services/core/java/com/android/server/notification/SnoozeHelper.java @@ -558,8 +558,8 @@ public class SnoozeHelper { if (value < currentTime) { return; } - out.attribute(null, XML_SNOOZED_NOTIFICATION_TIME, - value.toString()); + out.attributeLong(null, XML_SNOOZED_NOTIFICATION_TIME, + value); }); writeXml(out, mPersistedSnoozedNotificationsWithContext, XML_SNOOZED_NOTIFICATION_CONTEXT, diff --git a/services/core/java/com/android/server/notification/VisibilityExtractor.java b/services/core/java/com/android/server/notification/VisibilityExtractor.java index db548465d4b8..a363601b56e9 100644 --- a/services/core/java/com/android/server/notification/VisibilityExtractor.java +++ b/services/core/java/com/android/server/notification/VisibilityExtractor.java @@ -15,8 +15,11 @@ */ package com.android.server.notification; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.admin.DevicePolicyManager; import android.content.Context; -import android.service.notification.NotificationListenerService; +import android.os.UserHandle; import android.util.Slog; /** @@ -27,9 +30,11 @@ public class VisibilityExtractor implements NotificationSignalExtractor { private static final boolean DBG = false; private RankingConfig mConfig; + private DevicePolicyManager mDpm; public void initialize(Context ctx, NotificationUsageStats usageStats) { if (DBG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + "."); + mDpm = ctx.getSystemService(DevicePolicyManager.class); } public RankingReconsideration process(NotificationRecord record) { @@ -43,7 +48,38 @@ public class VisibilityExtractor implements NotificationSignalExtractor { return null; } - record.setPackageVisibilityOverride(record.getChannel().getLockscreenVisibility()); + int userId = record.getUserId(); + + if (userId == UserHandle.USER_ALL) { + record.setPackageVisibilityOverride(record.getChannel().getLockscreenVisibility()); + } else { + boolean userCanShowNotifications = + mConfig.canShowNotificationsOnLockscreen(userId); + boolean dpmCanShowNotifications = adminAllowsKeyguardFeature(userId, + DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS); + boolean channelCanShowNotifications = record.getChannel().getLockscreenVisibility() + != Notification.VISIBILITY_SECRET; + + if (!userCanShowNotifications || !dpmCanShowNotifications + || !channelCanShowNotifications) { + record.setPackageVisibilityOverride(Notification.VISIBILITY_SECRET); + } else { + // notifications are allowed but should they be redacted? + + boolean userCanShowContents = + mConfig.canShowPrivateNotificationsOnLockScreen(userId); + boolean dpmCanShowContents = adminAllowsKeyguardFeature(userId, + DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); + boolean channelCanShowContents = record.getChannel().getLockscreenVisibility() + != Notification.VISIBILITY_PRIVATE; + + if (!userCanShowContents || !dpmCanShowContents || !channelCanShowContents) { + record.setPackageVisibilityOverride(Notification.VISIBILITY_PRIVATE); + } else { + record.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE); + } + } + } return null; } @@ -57,4 +93,13 @@ public class VisibilityExtractor implements NotificationSignalExtractor { public void setZenHelper(ZenModeHelper helper) { } + + private boolean adminAllowsKeyguardFeature(int userHandle, int feature) { + if (userHandle == UserHandle.USER_ALL) { + return true; + } + final int dpmFlags = mDpm.getKeyguardDisabledFeatures(null /* admin */, userHandle); + return (dpmFlags & feature) == 0; + } + } diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 94f46ba6bc60..5cd22e0bd72d 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -81,6 +81,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; +import com.android.internal.util.XmlUtils; import com.android.server.LocalServices; import libcore.io.IoUtils; @@ -1165,7 +1166,7 @@ public class ZenModeHelper { try { parser = resources.getXml(R.xml.default_zen_mode_config); while (parser.next() != XmlPullParser.END_DOCUMENT) { - final ZenModeConfig config = ZenModeConfig.readXml(parser); + final ZenModeConfig config = ZenModeConfig.readXml(XmlUtils.makeTyped(parser)); if (config != null) return config; } } catch (Exception e) { diff --git a/services/core/java/com/android/server/pm/DefaultAppProvider.java b/services/core/java/com/android/server/pm/DefaultAppProvider.java new file mode 100644 index 000000000000..cc11fb2c00bd --- /dev/null +++ b/services/core/java/com/android/server/pm/DefaultAppProvider.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.role.RoleManager; +import android.os.Binder; +import android.os.UserHandle; +import android.util.Slog; + +import com.android.internal.infra.AndroidFuture; +import com.android.internal.util.CollectionUtils; +import com.android.server.FgThread; +import com.android.server.pm.permission.PermissionManagerServiceInternal; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * Interacts with {@link RoleManager} to provide and manage default apps. + */ +public class DefaultAppProvider { + @NonNull + private final Supplier<RoleManager> mRoleManagerSupplier; + @NonNull + private final PermissionManagerServiceInternal mPermissionManager; + + /** + * Create a new instance of this class + * + * @param roleManagerSupplier the supplier for {@link RoleManager} + * @param permissionManager the {@link PermissionManagerServiceInternal} + */ + public DefaultAppProvider(@NonNull Supplier<RoleManager> roleManagerSupplier, @NonNull + PermissionManagerServiceInternal permissionManager) { + mRoleManagerSupplier = roleManagerSupplier; + mPermissionManager = permissionManager; + } + + /** + * 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 + public String getDefaultBrowser(@UserIdInt int userId) { + return getRoleHolder(RoleManager.ROLE_BROWSER, userId); + } + + /** + * Set the package name of the default browser. + * + * @param packageName package name of the default browser, or {@code null} to unset + * @param async whether the operation should be asynchronous + * @param doGrant whether to grant default permissions + * @param userId the user ID + * @return whether the default browser was successfully set. + */ + public boolean setDefaultBrowser(@Nullable String packageName, boolean async, boolean doGrant, + @UserIdInt int userId) { + if (userId == UserHandle.USER_ALL) { + return false; + } + final RoleManager roleManager = mRoleManagerSupplier.get(); + if (roleManager == null) { + return false; + } + final UserHandle user = UserHandle.of(userId); + final Executor executor = FgThread.getExecutor(); + final AndroidFuture<Void> future = new AndroidFuture<>(); + final Consumer<Boolean> callback = successful -> { + if (successful) { + future.complete(null); + } else { + future.completeExceptionally(new RuntimeException()); + } + }; + final long identity = Binder.clearCallingIdentity(); + try { + if (packageName != null) { + roleManager.addRoleHolderAsUser(RoleManager.ROLE_BROWSER, packageName, 0, user, + executor, callback); + } else { + roleManager.clearRoleHoldersAsUser(RoleManager.ROLE_BROWSER, 0, user, executor, + callback); + } + if (!async) { + try { + future.get(5, TimeUnit.SECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + Slog.e(PackageManagerService.TAG, "Exception while setting default browser: " + + packageName, e); + return false; + } + } + if (doGrant && packageName != null) { + mPermissionManager.grantDefaultPermissionsToDefaultBrowser(packageName, userId); + } + } finally { + Binder.restoreCallingIdentity(identity); + } + return true; + } + + /** + * Get the package name of the default dialer. + * + * @param userId the user ID + * @return the package name of the default dialer, or {@code null} if none + */ + @Nullable + public String getDefaultDialer(@NonNull int userId) { + return getRoleHolder(RoleManager.ROLE_DIALER, userId); + } + + /** + * Get the package name of the default home. + * + * @param userId the user ID + * @return the package name of the default home, or {@code null} if none + */ + @Nullable + public String getDefaultHome(@NonNull int userId) { + return getRoleHolder(RoleManager.ROLE_HOME, userId); + } + + /** + * Set the package name of the default home. + * + * @param packageName package name of the default home + * @param userId the user ID + * @param executor the {@link Executor} to execute callback on + * @param callback the callback made after the default home as been updated + * @return whether the default home was set + */ + public boolean setDefaultHome(@NonNull String packageName, @UserIdInt int userId, + @NonNull Executor executor, @NonNull Consumer<Boolean> callback) { + final RoleManager roleManager = mRoleManagerSupplier.get(); + if (roleManager == null) { + return false; + } + final long identity = Binder.clearCallingIdentity(); + try { + roleManager.addRoleHolderAsUser(RoleManager.ROLE_HOME, packageName, 0, + UserHandle.of(userId), executor, callback); + } finally { + Binder.restoreCallingIdentity(identity); + } + return true; + } + + @Nullable + private String getRoleHolder(@NonNull String roleName, @NonNull int userId) { + final RoleManager roleManager = mRoleManagerSupplier.get(); + if (roleManager == null) { + return null; + } + final long identity = Binder.clearCallingIdentity(); + try { + return CollectionUtils.firstOrNull(roleManager.getRoleHoldersAsUser(roleName, + UserHandle.of(userId))); + } finally { + Binder.restoreCallingIdentity(identity); + } + } +} diff --git a/services/core/java/com/android/server/pm/PackageAbiHelper.java b/services/core/java/com/android/server/pm/PackageAbiHelper.java index 30b1c2c93a45..24a3e520fe1b 100644 --- a/services/core/java/com/android/server/pm/PackageAbiHelper.java +++ b/services/core/java/com/android/server/pm/PackageAbiHelper.java @@ -55,7 +55,7 @@ public interface PackageAbiHelper { * If {@code extractLibs} is true, native libraries are extracted from the app if required. */ Pair<Abis, NativeLibraryPaths> derivePackageAbi(AndroidPackage pkg, boolean isUpdatedSystemApp, - String cpuAbiOverride) throws PackageManagerException; + String cpuAbiOverride, File appLib32InstallDir) throws PackageManagerException; /** * Calculates adjusted ABIs for a set of packages belonging to a shared user so that they all diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java index da4ea16d0bfd..71b99bd4ced2 100644 --- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java +++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java @@ -296,7 +296,7 @@ final class PackageAbiHelperImpl implements PackageAbiHelper { @Override public Pair<Abis, NativeLibraryPaths> derivePackageAbi(AndroidPackage pkg, - boolean isUpdatedSystemApp, String cpuAbiOverride) + boolean isUpdatedSystemApp, String cpuAbiOverride, File appLib32InstallDir) throws PackageManagerException { // Give ourselves some initial paths; we'll come back for another // pass once we've determined ABI below. @@ -304,7 +304,7 @@ final class PackageAbiHelperImpl implements PackageAbiHelper { String pkgRawSecondaryCpuAbi = AndroidPackageUtils.getRawSecondaryCpuAbi(pkg); final NativeLibraryPaths initialLibraryPaths = deriveNativeLibraryPaths( new Abis(pkgRawPrimaryCpuAbi, pkgRawSecondaryCpuAbi), - PackageManagerService.sAppLib32InstallDir, pkg.getPath(), + appLib32InstallDir, pkg.getPath(), pkg.getBaseApkPath(), pkg.isSystem(), isUpdatedSystemApp); @@ -452,7 +452,7 @@ final class PackageAbiHelperImpl implements PackageAbiHelper { final Abis abis = new Abis(primaryCpuAbi, secondaryCpuAbi); return new Pair<>(abis, - deriveNativeLibraryPaths(abis, PackageManagerService.sAppLib32InstallDir, + deriveNativeLibraryPaths(abis, appLib32InstallDir, pkg.getPath(), pkg.getBaseApkPath(), pkg.isSystem(), isUpdatedSystemApp)); } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index ade0b42664d9..0aebe721b103 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -104,6 +104,7 @@ import android.os.ParcelableException; import android.os.Process; import android.os.RemoteException; import android.os.RevocableFileDescriptor; +import android.os.SELinux; import android.os.SystemProperties; import android.os.UserHandle; import android.os.incremental.IStorageHealthListener; @@ -1088,6 +1089,31 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } + @Override + public void stageViaHardLink(String path) { + final int callingUid = Binder.getCallingUid(); + if (callingUid != Process.SYSTEM_UID) { + throw new SecurityException("link() can only be run by the system"); + } + + try { + final File target = new File(path); + final File source = new File(stageDir, target.getName()); + try { + Os.link(path, source.getAbsolutePath()); + // Grant READ access for APK to be read successfully + Os.chmod(source.getAbsolutePath(), 0644); + } catch (ErrnoException e) { + e.rethrowAsIOException(); + } + if (!SELinux.restorecon(source)) { + throw new IOException("Can't relabel file: " + source); + } + } catch (IOException e) { + throw ExceptionUtils.wrap(e); + } + } + private ParcelFileDescriptor openTargetInternal(String path, int flags, int mode) throws IOException, ErrnoException { // TODO: this should delegate to DCS so the system process avoids diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 9a0be06b914e..6aa4589cad98 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -152,6 +152,7 @@ import android.app.ResourcesManager; import android.app.admin.IDevicePolicyManager; import android.app.admin.SecurityLog; import android.app.backup.IBackupManager; +import android.app.role.RoleManager; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.content.BroadcastReceiver; @@ -448,7 +449,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; -import java.util.function.Supplier; /** * Keep track of all those APKs everywhere. @@ -732,8 +732,6 @@ public class PackageManagerService extends IPackageManager.Stub private static final int INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS = 7000; private static final int INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS = 60000; - final ServiceThread mHandlerThread; - final Handler mHandler; private final ProcessLoggingHandler mProcessLoggingHandler; @@ -761,12 +759,14 @@ public class PackageManagerService extends IPackageManager.Stub final Installer mInstaller; /** Directory where installed applications are stored */ - private static final File sAppInstallDir = - new File(Environment.getDataDirectory(), "app"); + private final File mAppInstallDir; /** Directory where installed application's 32-bit native libraries are copied. */ @VisibleForTesting - static final File sAppLib32InstallDir = - new File(Environment.getDataDirectory(), "app-lib"); + final File mAppLib32InstallDir; + + private static File getAppLib32InstallDir() { + return new File(Environment.getDataDirectory(), "app-lib"); + } // ---------------------------------------------------------------- @@ -832,6 +832,11 @@ public class PackageManagerService extends IPackageManager.Stub boolean mFirstBoot; + private final boolean mIsEngBuild; + private final boolean mIsUserDebugBuild; + private final String mIncrementalVersion; + + PackageManagerInternal.ExternalSourcesPolicy mExternalSourcesPolicy; @GuardedBy("mAvailableFeatures") @@ -866,6 +871,8 @@ public class PackageManagerService extends IPackageManager.Stub private final Injector mInjector; + private final SystemWrapper mSystemWrapper; + /** * The list of all system partitions that may contain packages in ascending order of * specificity (the more generic, the earlier in the list a partition appears). @@ -896,6 +903,10 @@ public class PackageManagerService extends IPackageManager.Stub T produce(Injector injector, PackageManagerService packageManager); } + interface ProducerWithArgument<T, R> { + T produce(Injector injector, PackageManagerService packageManager, R argument); + } + interface ServiceProducer { <T> T produce(Class<T> c); } @@ -924,6 +935,8 @@ public class PackageManagerService extends IPackageManager.Stub private final Object mInstallLock; private final Handler mBackgroundHandler; private final Executor mBackgroundExecutor; + private final List<ScanPartition> mSystemPartitions; + // ----- producers ----- private final Singleton<ComponentResolver> mComponentResolverProducer; @@ -940,13 +953,23 @@ public class PackageManagerService extends IPackageManager.Stub private final Singleton<ViewCompiler> mViewCompilerProducer; private final Singleton<IPermissionManager> mPermissionManagerProducer; private final Singleton<IncrementalManager> mIncrementalManagerProducer; + private final Singleton<DefaultAppProvider> mDefaultAppProviderProducer; + private final Singleton<DisplayMetrics> mDisplayMetricsProducer; + private final Producer<PackageParser2> mScanningCachingPackageParserProducer; + private final Producer<PackageParser2> mScanningPackageParserProducer; + private final Producer<PackageParser2> mPreparingPackageParserProducer; + private final Singleton<PackageInstallerService> mPackageInstallerServiceProducer; + private final ProducerWithArgument<InstantAppResolverConnection, ComponentName> + mInstantAppResolverConnectionProducer; private final SystemWrapper mSystemWrapper; private final ServiceProducer mGetLocalServiceProducer; private final ServiceProducer mGetSystemServiceProducer; + private final Singleton<ModuleInfoProvider> mModuleInfoProviderProducer; Injector(Context context, Object lock, Installer installer, Object installLock, PackageAbiHelper abiHelper, Handler backgroundHandler, + List<ScanPartition> systemPartitions, Producer<ComponentResolver> componentResolverProducer, Producer<PermissionManagerServiceInternal> permissionManagerServiceProducer, Producer<UserManagerService> userManagerProducer, @@ -961,6 +984,15 @@ public class PackageManagerService extends IPackageManager.Stub Producer<IPermissionManager> permissionManagerProducer, Producer<ViewCompiler> viewCompilerProducer, Producer<IncrementalManager> incrementalManagerProducer, + Producer<DefaultAppProvider> defaultAppProviderProducer, + Producer<DisplayMetrics> displayMetricsProducer, + Producer<PackageParser2> scanningCachingPackageParserProducer, + Producer<PackageParser2> scanningPackageParserProducer, + Producer<PackageParser2> preparingPackageParserProducer, + Producer<PackageInstallerService> packageInstallerServiceProducer, + ProducerWithArgument<InstantAppResolverConnection, ComponentName> + instantAppResolverConnectionProducer, + Producer<ModuleInfoProvider> moduleInfoProviderProducer, SystemWrapper systemWrapper, ServiceProducer getLocalServiceProducer, ServiceProducer getSystemServiceProducer) { @@ -971,6 +1003,7 @@ public class PackageManagerService extends IPackageManager.Stub mInstallLock = installLock; mBackgroundHandler = backgroundHandler; mBackgroundExecutor = new HandlerExecutor(backgroundHandler); + mSystemPartitions = systemPartitions; mComponentResolverProducer = new Singleton<>(componentResolverProducer); mPermissionManagerServiceProducer = new Singleton<>(permissionManagerServiceProducer); mUserManagerProducer = new Singleton<>(userManagerProducer); @@ -985,6 +1018,14 @@ public class PackageManagerService extends IPackageManager.Stub mPermissionManagerProducer = new Singleton<>(permissionManagerProducer); mViewCompilerProducer = new Singleton<>(viewCompilerProducer); mIncrementalManagerProducer = new Singleton<>(incrementalManagerProducer); + mDefaultAppProviderProducer = new Singleton<>(defaultAppProviderProducer); + mDisplayMetricsProducer = new Singleton<>(displayMetricsProducer); + mScanningCachingPackageParserProducer = scanningCachingPackageParserProducer; + mScanningPackageParserProducer = scanningPackageParserProducer; + mPreparingPackageParserProducer = preparingPackageParserProducer; + mPackageInstallerServiceProducer = new Singleton<>(packageInstallerServiceProducer); + mInstantAppResolverConnectionProducer = instantAppResolverConnectionProducer; + mModuleInfoProviderProducer = new Singleton<>(moduleInfoProviderProducer); mSystemWrapper = systemWrapper; mGetLocalServiceProducer = getLocalServiceProducer; mGetSystemServiceProducer = getSystemServiceProducer; @@ -1010,6 +1051,10 @@ public class PackageManagerService extends IPackageManager.Stub return mInstallLock; } + public List<ScanPartition> getSystemPartitions() { + return mSystemPartitions; + } + public UserManagerService getUserManagerService() { return mUserManagerProducer.get(this, mPackageManager); } @@ -1082,6 +1127,10 @@ public class PackageManagerService extends IPackageManager.Stub return mBackgroundExecutor; } + public DisplayMetrics getDisplayMetrics() { + return mDisplayMetricsProducer.get(this, mPackageManager); + } + public <T> T getLocalService(Class<T> c) { return mGetLocalServiceProducer.produce(c); } @@ -1097,53 +1146,63 @@ public class PackageManagerService extends IPackageManager.Stub public IncrementalManager getIncrementalManager() { return mIncrementalManagerProducer.get(this, mPackageManager); } - } - /** Provides an abstraction to static access to system state. */ - public interface SystemWrapper { - /** @see SystemProperties#get(String) */ - String getProperty(String key); - /** @see SystemProperties#getInt(String, int) */ - int getPropertyInt(String key, int defValue); - /** @see SystemProperties#getBoolean(String, boolean) */ - boolean getPropertyBoolean(String key, boolean defValue); - /** @see SystemProperties#digestOf(String...) */ - String digestOfProperties(@NonNull String... keys); - /** @see SystemProperties#set(String, String) */ - void setProperty(String key, String value); - /** @see Build.VERSION#SDK_INT */ - int getSdkInt(); - } + public DefaultAppProvider getDefaultAppProvider() { + return mDefaultAppProviderProducer.get(this, mPackageManager); + } - private static class DefaultSystemWrapper implements SystemWrapper { - @Override - public String getProperty(String key) { - return SystemProperties.get(key); + public PackageParser2 getScanningCachingPackageParser() { + return mScanningCachingPackageParserProducer.produce(this, mPackageManager); + } + public PackageParser2 getScanningPackageParser() { + return mScanningPackageParserProducer.produce(this, mPackageManager); + } + public PackageParser2 getPreparingPackageParser() { + return mPreparingPackageParserProducer.produce(this, mPackageManager); } - @Override - public int getPropertyInt(String key, int defValue) { - return SystemProperties.getInt(key, defValue); + public PackageInstallerService getPackageInstallerService() { + return mPackageInstallerServiceProducer.get(this, mPackageManager); } - @Override - public boolean getPropertyBoolean(String key, boolean defValue) { - return SystemProperties.getBoolean(key, defValue); + public InstantAppResolverConnection getInstantAppResolverConnection( + ComponentName instantAppResolverComponent) { + return mInstantAppResolverConnectionProducer.produce( + this, mPackageManager, instantAppResolverComponent); } - @Override - public String digestOfProperties(String... keys) { - return SystemProperties.digestOf(keys); + public ModuleInfoProvider getModuleInfoProvider() { + return mModuleInfoProviderProducer.get(this, mPackageManager); } + } + + /** Provides an abstraction to static access to system state. */ + public interface SystemWrapper { + void disablePackageCaches(); + void enablePackageCaches(); + } + + private static class DefaultSystemWrapper implements SystemWrapper { @Override - public void setProperty(String key, String value) { - SystemProperties.set(key, value); + public void disablePackageCaches() { + // disable all package caches that shouldn't apply within system server + PackageManager.disableApplicationInfoCache(); + PackageManager.disablePackageInfoCache(); + ApplicationPackageManager.invalidateGetPackagesForUidCache(); + ApplicationPackageManager.disableGetPackagesForUidCache(); + ApplicationPackageManager.invalidateHasSystemFeatureCache(); + + // Avoid invalidation-thrashing by preventing cache invalidations from causing property + // writes if the cache isn't enabled yet. We re-enable writes later when we're + // done initializing. + PackageManager.corkPackageInfoCache(); } @Override - public int getSdkInt() { - return Build.VERSION.SDK_INT; + public void enablePackageCaches() { + // Uncork cache invalidations and allow clients to cache package information. + PackageManager.uncorkPackageInfoCache(); } } @@ -1154,13 +1213,13 @@ public class PackageManagerService extends IPackageManager.Stub public ArtManagerService artManagerService; public @Nullable String configuratorPackage; public int defParseFlags; + public DefaultAppProvider defaultAppProvider; public DexManager dexManager; public List<ScanPartition> dirsToScanAsSystem; public @Nullable String documenterPackage; public boolean factoryTest; public ArrayMap<String, FeatureInfo> availableFeatures; public Handler handler; - public ServiceThread handlerThread; public @Nullable String incidentReportApproverPackage; public IncrementalManager incrementalManager; public PackageInstallerService installerService; @@ -1206,6 +1265,13 @@ public class PackageManagerService extends IPackageManager.Stub public ArrayMap<String, AndroidPackage> packages; public boolean enableFreeCacheV2; public int sdkVersion; + public SystemWrapper systemWrapper; + public File appInstallDir; + public File appLib32InstallDir; + public boolean isEngBuild; + public boolean isUserDebugBuild; + public int sdkInt = Build.VERSION.SDK_INT; + public String incrementalVersion = Build.VERSION.INCREMENTAL; } private final AppsFilter mAppsFilter; @@ -1307,6 +1373,8 @@ public class PackageManagerService extends IPackageManager.Stub private final IncrementalManager mIncrementalManager; + private final DefaultAppProvider mDefaultAppProvider; + private final PackageProperty mPackageProperty = new PackageProperty(); private static class IFVerificationParams { @@ -1803,7 +1871,7 @@ public class PackageManagerService extends IPackageManager.Stub for (int index = 0; i < size && index < numComponents; index++) { packages[i] = componentsToBroadcast.keyAt(index); components[i] = componentsToBroadcast.valueAt(index); - final PackageSetting ps = mSettings.mPackages.get(packages[i]); + final PackageSetting ps = mSettings.getPackageLPr(packages[i]); uids[i] = (ps != null) ? UserHandle.getUid(packageUserId, ps.appId) : -1; @@ -2310,7 +2378,7 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { newBroadcastAllowList = mAppsFilter.getVisibilityAllowList( getPackageSettingInternal(res.name, Process.SYSTEM_UID), - updateUserIds, mSettings.mPackages); + updateUserIds, mSettings.getPackagesLocked()); } sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, 0 /*flags*/, @@ -2747,6 +2815,7 @@ public class PackageManagerService extends IPackageManager.Stub Injector injector = new Injector( context, lock, installer, installLock, new PackageAbiHelperImpl(), backgroundHandler, + SYSTEM_PARTITIONS, (i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mPmInternal, lock), (i, pm) -> PermissionManagerService.create(context), (i, pm) -> new UserManagerService(context, pm, @@ -2768,12 +2837,37 @@ public class PackageManagerService extends IPackageManager.Stub (i, pm) -> (IPermissionManager) ServiceManager.getService("permissionmgr"), (i, pm) -> new ViewCompiler(i.getInstallLock(), i.getInstaller()), (i, pm) -> (IncrementalManager) - pm.mContext.getSystemService(Context.INCREMENTAL_SERVICE), + i.getContext().getSystemService(Context.INCREMENTAL_SERVICE), + (i, pm) -> new DefaultAppProvider(() -> context.getSystemService(RoleManager.class), + i.getPermissionManagerServiceInternal()), + (i, pm) -> new DisplayMetrics(), + (i, pm) -> new PackageParser2(pm.mSeparateProcesses, pm.mOnlyCore, + i.getDisplayMetrics(), pm.mCacheDir, + pm.mPackageParserCallback) /* scanningCachingPackageParserProducer */, + (i, pm) -> new PackageParser2(pm.mSeparateProcesses, pm.mOnlyCore, + i.getDisplayMetrics(), null, + pm.mPackageParserCallback) /* scanningPackageParserProducer */, + (i, pm) -> new PackageParser2(pm.mSeparateProcesses, false, i.getDisplayMetrics(), + null, pm.mPackageParserCallback) /* preparingPackageParserProducer */, + // Prepare a supplier of package parser for the staging manager to parse apex file + // during the staging installation. + (i, pm) -> new PackageInstallerService( + i.getContext(), pm, i::getScanningPackageParser), + (i, pm, cn) -> new InstantAppResolverConnection( + i.getContext(), cn, Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE), + (i, pm) -> new ModuleInfoProvider(i.getContext(), pm), new DefaultSystemWrapper(), LocalServices::getService, context::getSystemService); - PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest); + + if (Build.VERSION.SDK_INT <= 0) { + Slog.w(TAG, "**** ro.build.version.sdk not set!"); + } + + PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest, + Build.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG, Build.VERSION.SDK_INT, + Build.VERSION.INCREMENTAL); t.traceEnd(); // "create package manager" final CompatChange.ChangeListener selinuxChangeListener = packageName -> { @@ -2833,11 +2927,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - private static void getDefaultDisplayMetrics( - DisplayManager displayManager, DisplayMetrics metrics) { - displayManager.getDisplay(Display.DEFAULT_DISPLAY).getMetrics(metrics); - } - /** * Requests that files preopted on a secondary system partition be copied to the data partition * if possible. Note that the actual copying of the files is accomplished by init for security @@ -2847,14 +2936,13 @@ public class PackageManagerService extends IPackageManager.Stub private static void requestCopyPreoptedFiles(Injector injector) { final int WAIT_TIME_MS = 100; final String CP_PREOPT_PROPERTY = "sys.cppreopt"; - if (injector.getSystemWrapper().getPropertyInt("ro.cp_system_other_odex", 0) == 1) { - injector.getSystemWrapper().setProperty(CP_PREOPT_PROPERTY, "requested"); + if (SystemProperties.getInt("ro.cp_system_other_odex", 0) == 1) { + SystemProperties.set(CP_PREOPT_PROPERTY, "requested"); // We will wait for up to 100 seconds. final long timeStart = SystemClock.uptimeMillis(); final long timeEnd = timeStart + 100 * 1000; long timeNow = timeStart; - while (!injector.getSystemWrapper() - .getProperty(CP_PREOPT_PROPERTY).equals("finished")) { + while (!SystemProperties.get(CP_PREOPT_PROPERTY).equals("finished")) { try { Thread.sleep(WAIT_TIME_MS); } catch (InterruptedException e) { @@ -2862,7 +2950,7 @@ public class PackageManagerService extends IPackageManager.Stub } timeNow = SystemClock.uptimeMillis(); if (timeNow > timeEnd) { - injector.getSystemWrapper().setProperty(CP_PREOPT_PROPERTY, "timed-out"); + SystemProperties.set(CP_PREOPT_PROPERTY, "timed-out"); Slog.wtf(TAG, "cppreopt did not finish!"); break; } @@ -2939,16 +3027,15 @@ public class PackageManagerService extends IPackageManager.Stub mPermissionManager = injector.getPermissionManagerServiceInternal(); mSettings = injector.getSettings(); mUserManager = injector.getUserManagerService(); - mApexManager = testParams.apexManager; mArtManagerService = testParams.artManagerService; mAvailableFeatures = testParams.availableFeatures; mDefParseFlags = testParams.defParseFlags; + mDefaultAppProvider = testParams.defaultAppProvider; mDexManager = testParams.dexManager; mDirsToScanAsSystem = testParams.dirsToScanAsSystem; mFactoryTest = testParams.factoryTest; mHandler = testParams.handler; - mHandlerThread = testParams.handlerThread; mIncrementalManager = testParams.incrementalManager; mInstallerService = testParams.installerService; mInstantAppRegistry = testParams.instantAppRegistry; @@ -2993,49 +3080,46 @@ public class PackageManagerService extends IPackageManager.Stub mServicesExtensionPackageName = testParams.servicesExtensionPackageName; mSharedSystemSharedLibraryPackageName = testParams.sharedSystemSharedLibraryPackageName; mOverlayConfigSignaturePackage = testParams.overlayConfigSignaturePackage; - mResolveComponentName = testParams.resolveComponentName; mPackages.putAll(testParams.packages); mEnableFreeCacheV2 = testParams.enableFreeCacheV2; mSdkVersion = testParams.sdkVersion; - } - - public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) { - PackageManager.disableApplicationInfoCache(); - PackageManager.disablePackageInfoCache(); - ApplicationPackageManager.invalidateGetPackagesForUidCache(); - ApplicationPackageManager.disableGetPackagesForUidCache(); - ApplicationPackageManager.invalidateHasSystemFeatureCache(); - - // Avoid invalidation-thrashing by preventing cache invalidations from causing property - // writes if the cache isn't enabled yet. We re-enable writes later when we're - // done initializing. - PackageManager.corkPackageInfoCache(); + mSystemWrapper = testParams.systemWrapper; + mAppInstallDir = testParams.appInstallDir; + mAppLib32InstallDir = testParams.appLib32InstallDir; + mIsEngBuild = testParams.isEngBuild; + mIsUserDebugBuild = testParams.isUserDebugBuild; + mIncrementalVersion = testParams.incrementalVersion; + } + + public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest, + final String buildFingerprint, final boolean isEngBuild, + final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) { + mIsEngBuild = isEngBuild; + mIsUserDebugBuild = isUserDebugBuild; + mSdkVersion = sdkVersion; + mIncrementalVersion = incrementalVersion; + mInjector = injector; + mInjector.getSystemWrapper().disablePackageCaches(); final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing", Trace.TRACE_TAG_PACKAGE_MANAGER); mPendingBroadcasts = new PendingPackageBroadcasts(); - mInjector = injector; mInjector.bootstrap(this); mLock = injector.getLock(); mInstallLock = injector.getInstallLock(); LockGuard.installLock(mLock, LockGuard.INDEX_PACKAGES); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START, SystemClock.uptimeMillis()); - mSdkVersion = injector.getSystemWrapper().getSdkInt(); - - if (mSdkVersion <= 0) { - Slog.w(TAG, "**** ro.build.version.sdk not set!"); - } + mSystemWrapper = injector.getSystemWrapper(); mContext = injector.getContext(); mFactoryTest = factoryTest; mOnlyCore = onlyCore; - mMetrics = new DisplayMetrics(); + mMetrics = injector.getDisplayMetrics(); mInstaller = injector.getInstaller(); - mEnableFreeCacheV2 = - injector.getSystemWrapper().getPropertyBoolean("fw.free_cache_v2", true); + mEnableFreeCacheV2 = SystemProperties.getBoolean("fw.free_cache_v2", true); // Create sub-components that provide services / data. Order here is important. t.traceBegin("createSubComponents"); @@ -3051,6 +3135,7 @@ public class PackageManagerService extends IPackageManager.Stub mSettings = injector.getSettings(); mPermissionManagerService = injector.getPermissionManagerService(); mIncrementalManager = mInjector.getIncrementalManager(); + mDefaultAppProvider = mInjector.getDefaultAppProvider(); PlatformCompat platformCompat = mInjector.getCompatibility(); mPackageParserCallback = new PackageParser2.Callback() { @Override @@ -3086,8 +3171,8 @@ public class PackageManagerService extends IPackageManager.Stub ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); t.traceEnd(); - String separateProcesses = - injector.getSystemWrapper().getProperty("debug.separate_processes"); + String separateProcesses = SystemProperties.get("debug.separate_processes"); + if (separateProcesses != null && separateProcesses.length() > 0) { if ("*".equals(separateProcesses)) { mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES; @@ -3110,7 +3195,8 @@ public class PackageManagerService extends IPackageManager.Stub mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper()); mViewCompiler = injector.getViewCompiler(); - getDefaultDisplayMetrics(mInjector.getSystemService(DisplayManager.class), mMetrics); + mContext.getSystemService(DisplayManager.class) + .getDisplay(Display.DEFAULT_DISPLAY).getMetrics(mMetrics); t.traceBegin("get system config"); SystemConfig systemConfig = injector.getSystemConfig(); @@ -3132,18 +3218,21 @@ public class PackageManagerService extends IPackageManager.Stub } mDirsToScanAsSystem = new ArrayList<>(); - mDirsToScanAsSystem.addAll(SYSTEM_PARTITIONS); + mDirsToScanAsSystem.addAll(injector.getSystemPartitions()); mDirsToScanAsSystem.addAll(scanPartitions); Slog.d(TAG, "Directories scanned as system partitions: " + mDirsToScanAsSystem); + mAppInstallDir = new File(Environment.getDataDirectory(), "app"); + mAppLib32InstallDir = getAppLib32InstallDir(); + // CHECKSTYLE:OFF IndentationCheck synchronized (mInstallLock) { // writer synchronized (mLock) { - mHandlerThread = new ServiceThread(TAG, + HandlerThread handlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/); - mHandlerThread.start(); - mHandler = new PackageHandler(mHandlerThread.getLooper()); + handlerThread.start(); + mHandler = new PackageHandler(handlerThread.getLooper()); mProcessLoggingHandler = new ProcessLoggingHandler(); Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT); mInstantAppRegistry = new InstantAppRegistry(this, mPermissionManager); @@ -3188,12 +3277,13 @@ public class PackageManagerService extends IPackageManager.Stub // Clean up orphaned packages for which the code path doesn't exist // and they are an update to a system app - caused by bug/32321269 - final int packageSettingCount = mSettings.mPackages.size(); + final ArrayMap<String, PackageSetting> packageSettings = mSettings.getPackagesLocked(); + final int packageSettingCount = packageSettings.size(); for (int i = packageSettingCount - 1; i >= 0; i--) { - PackageSetting ps = mSettings.mPackages.valueAt(i); + PackageSetting ps = packageSettings.valueAt(i); if (!isExternal(ps) && (ps.getPath() == null || !ps.getPath().exists()) && mSettings.getDisabledSystemPkgLPr(ps.name) != null) { - mSettings.mPackages.removeAt(i); + packageSettings.removeAt(i); mSettings.enableSystemPackageLPw(ps.name); } } @@ -3228,9 +3318,10 @@ public class PackageManagerService extends IPackageManager.Stub File frameworkDir = new File(Environment.getRootDirectory(), "framework"); final VersionInfo ver = mSettings.getInternalVersion(); - mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint); + mIsUpgrade = + !buildFingerprint.equals(ver.fingerprint); if (mIsUpgrade) { - logCriticalInfo(Log.INFO, + PackageManagerServiceUtils.logCriticalInfo(Log.INFO, "Upgrading from " + ver.fingerprint + " to " + Build.FINGERPRINT); } @@ -3248,13 +3339,13 @@ public class PackageManagerService extends IPackageManager.Stub // Save the names of pre-existing packages prior to scanning, so we can determine // which system packages are completely new due to an upgrade. if (isDeviceUpgrading()) { - mExistingPackages = new ArraySet<>(mSettings.mPackages.size()); - for (PackageSetting ps : mSettings.mPackages.values()) { + mExistingPackages = new ArraySet<>(packageSettings.size()); + for (PackageSetting ps : packageSettings.values()) { mExistingPackages.add(ps.name); } } - mCacheDir = preparePackageParserCache(injector); + mCacheDir = preparePackageParserCache(mIsEngBuild); // Set flag to monitor and not change apk file paths when // scanning install directories. @@ -3267,8 +3358,7 @@ public class PackageManagerService extends IPackageManager.Stub final int systemParseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR; final int systemScanFlags = scanFlags | SCAN_AS_SYSTEM; - PackageParser2 packageParser = new PackageParser2(mSeparateProcesses, mOnlyCore, - mMetrics, mCacheDir, mPackageParserCallback); + PackageParser2 packageParser = injector.getScanningCachingPackageParser(); ExecutorService executorService = ParallelPackageParser.makeExecutorService(); // Prepare apex package info before scanning APKs, these information are needed when @@ -3331,8 +3421,8 @@ public class PackageManagerService extends IPackageManager.Stub // Iterates PackageSettings in reversed order because the item could be removed // during the iteration. - for (int index = mSettings.mPackages.size() - 1; index >= 0; index--) { - final PackageSetting ps = mSettings.mPackages.valueAt(index); + for (int index = packageSettings.size() - 1; index >= 0; index--) { + final PackageSetting ps = packageSettings.valueAt(index); /* * If this is not a system app, it can't be a @@ -3413,7 +3503,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!mOnlyCore) { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START, SystemClock.uptimeMillis()); - scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0, + scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0, packageParser, executorService); } @@ -3470,7 +3560,7 @@ public class PackageManagerService extends IPackageManager.Stub // previously scanned and known to the system], but, we don't have // a package [ie. there was an error scanning it from the /data // partition], completely remove the package data. - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null && mPackages.get(packageName) == null) { removePackageDataLIF(ps, userIds, null, 0, false); @@ -3596,7 +3686,7 @@ public class PackageManagerService extends IPackageManager.Stub // Now that we know all the packages we are keeping, // read and update their last usage times. - mPackageUsage.read(mSettings.mPackages); + mPackageUsage.read(packageSettings); mCompilerStats.read(); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END, @@ -3689,8 +3779,8 @@ public class PackageManagerService extends IPackageManager.Stub // profile compilation (without waiting to collect a fresh set of profiles). if (mIsUpgrade && !mOnlyCore) { Slog.i(TAG, "Build fingerprint changed; clearing code caches"); - for (int i = 0; i < mSettings.mPackages.size(); i++) { - final PackageSetting ps = mSettings.mPackages.valueAt(i); + for (int i = 0; i < packageSettings.size(); i++) { + final PackageSetting ps = packageSettings.valueAt(i); if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) { // No apps are running this early, so no need to freeze clearAppDataLIF(ps.pkg, UserHandle.USER_ALL, @@ -3706,9 +3796,9 @@ public class PackageManagerService extends IPackageManager.Stub // their icons in launcher. if (!mOnlyCore && mIsPreQUpgrade) { Slog.i(TAG, "Whitelisting all existing apps to hide their icons"); - int size = mSettings.mPackages.size(); + int size = packageSettings.size(); for (int i = 0; i < size; i++) { - final PackageSetting ps = mSettings.mPackages.valueAt(i); + final PackageSetting ps = packageSettings.valueAt(i); if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) { continue; } @@ -3777,23 +3867,16 @@ public class PackageManagerService extends IPackageManager.Stub } } - // Prepare a supplier of package parser for the staging manager to parse apex file - // during the staging installation. - final Supplier<PackageParser2> apexParserSupplier = () -> new PackageParser2( - mSeparateProcesses, mOnlyCore, mMetrics, null /* cacheDir */, - mPackageParserCallback); - mInstallerService = new PackageInstallerService(mContext, this, apexParserSupplier); - final Pair<ComponentName, String> instantAppResolverComponent = - getInstantAppResolverLPr(); + mInstallerService = mInjector.getPackageInstallerService(); + final ComponentName instantAppResolverComponent = getInstantAppResolverLPr(); if (instantAppResolverComponent != null) { if (DEBUG_INSTANT) { Slog.d(TAG, "Set ephemeral resolver: " + instantAppResolverComponent); } - mInstantAppResolverConnection = new InstantAppResolverConnection( - mContext, instantAppResolverComponent.first, - instantAppResolverComponent.second); + mInstantAppResolverConnection = + mInjector.getInstantAppResolverConnection(instantAppResolverComponent); mInstantAppResolverSettingsComponent = - getInstantAppResolverSettingsLPr(instantAppResolverComponent.first); + getInstantAppResolverSettingsLPr(instantAppResolverComponent); } else { mInstantAppResolverConnection = null; mInstantAppResolverSettingsComponent = null; @@ -3823,10 +3906,8 @@ public class PackageManagerService extends IPackageManager.Stub } // synchronized (mInstallLock) // CHECKSTYLE:ON IndentationCheck - mModuleInfoProvider = new ModuleInfoProvider(mContext, this); - - // Uncork cache invalidations and allow clients to cache package information. - PackageManager.uncorkPackageInfoCache(); + mModuleInfoProvider = mInjector.getModuleInfoProvider(); + mInjector.getSystemWrapper().enablePackageCaches(); // Now after opening every single application zip, make sure they // are all flushed. Not really needed, but keeps things nice and @@ -3874,7 +3955,7 @@ public class PackageManagerService extends IPackageManager.Stub continue; } // skip if the package has been disabled by the user - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null) { final int enabledState = ps.getEnabled(UserHandle.USER_SYSTEM); if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { @@ -3899,7 +3980,7 @@ public class PackageManagerService extends IPackageManager.Stub // disable any stub still left; these failed to install the full application for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) { final String pkgName = systemStubPackageNames.get(i); - final PackageSetting ps = mSettings.mPackages.get(pkgName); + final PackageSetting ps = mSettings.getPackageLPr(pkgName); ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DISABLED, UserHandle.USER_SYSTEM, "android"); logCriticalInfo(Log.ERROR, "Stub disabled; pkg: " + pkgName); @@ -3956,7 +4037,7 @@ public class PackageManagerService extends IPackageManager.Stub } finally { // Disable the package; the stub by itself is not runnable synchronized (mLock) { - final PackageSetting stubPs = mSettings.mPackages.get( + final PackageSetting stubPs = mSettings.getPackageLPr( stubPkg.getPackageName()); if (stubPs != null) { stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, @@ -4076,19 +4157,18 @@ public class PackageManagerService extends IPackageManager.Stub setUpInstantAppInstallerActivityLP(getInstantAppInstallerLPr()); } - private static @Nullable File preparePackageParserCache(Injector injector) { + private @Nullable File preparePackageParserCache(boolean forEngBuild) { if (!FORCE_PACKAGE_PARSED_CACHE_ENABLED) { if (!DEFAULT_PACKAGE_PARSER_CACHE_ENABLED) { return null; } // Disable package parsing on eng builds to allow for faster incremental development. - if (Build.IS_ENG) { + if (forEngBuild) { return null; } - if (injector.getSystemWrapper() - .getPropertyBoolean("pm.boot.disable_package_cache", false)) { + if (SystemProperties.getBoolean("pm.boot.disable_package_cache", false)) { Slog.i(TAG, "Disabling package parser cache due to system property."); return null; } @@ -4104,8 +4184,7 @@ public class PackageManagerService extends IPackageManager.Stub // identify cached items. In particular, changing the value of certain // feature flags should cause us to invalidate any caches. final String cacheName = FORCE_PACKAGE_PARSED_CACHE_ENABLED ? "debug" - : injector.getSystemWrapper().digestOfProperties( - "ro.build.fingerprint"); + : SystemProperties.digestOf("ro.build.fingerprint"); // Reconcile cache directories, keeping only what we'd actually use. for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) { @@ -4134,14 +4213,15 @@ public class PackageManagerService extends IPackageManager.Stub // NOTE: When no BUILD_NUMBER is set by the build system, it defaults to a build // that starts with "eng." to signify that this is an engineering build and not // destined for release. - if (Build.IS_USERDEBUG && Build.VERSION.INCREMENTAL.startsWith("eng.")) { + if (mIsUserDebugBuild && mIncrementalVersion.startsWith("eng.")) { Slog.w(TAG, "Wiping cache directory because the system partition changed."); // Heuristic: If the /system directory has been modified recently due to an "adb sync" // or a regular make, then blow away the cache. Note that mtimes are *NOT* reliable // in general and should not be used for production changes. In this specific case, // we know that they will work. - File frameworkDir = new File(Environment.getRootDirectory(), "framework"); + File frameworkDir = + new File(Environment.getRootDirectory(), "framework"); if (cacheDir.lastModified() < frameworkDir.lastModified()) { FileUtils.deleteContents(cacheBaseDir); cacheDir = FileUtils.createDir(cacheBaseDir, cacheName); @@ -4167,7 +4247,7 @@ public class PackageManagerService extends IPackageManager.Stub public boolean isDeviceUpgrading() { // allow instant applications // The system property allows testing ota flow when upgraded to the same image. - return mIsUpgrade || mInjector.getSystemWrapper().getPropertyBoolean( + return mIsUpgrade || SystemProperties.getBoolean( "persist.pm.mock-upgrade", false /* default */); } @@ -4301,15 +4381,15 @@ public class PackageManagerService extends IPackageManager.Stub return null; } synchronized (mLock) { - final Pair<ComponentName, String> instantAppResolver = getInstantAppResolverLPr(); + final ComponentName instantAppResolver = getInstantAppResolverLPr(); if (instantAppResolver == null) { return null; } - return instantAppResolver.first; + return instantAppResolver; } } - private @Nullable Pair<ComponentName, String> getInstantAppResolverLPr() { + private @Nullable ComponentName getInstantAppResolverLPr() { final String[] packageArray = mContext.getResources().getStringArray(R.array.config_ephemeralResolverPackage); if (packageArray.length == 0 && !Build.IS_DEBUGGABLE) { @@ -4324,8 +4404,7 @@ public class PackageManagerService extends IPackageManager.Stub MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0); - String actionName = Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE; - final Intent resolverIntent = new Intent(actionName); + final Intent resolverIntent = new Intent(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE); List<ResolveInfo> resolvers = queryIntentServicesInternal(resolverIntent, null, resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/); final int N = resolvers.size(); @@ -4357,7 +4436,7 @@ public class PackageManagerService extends IPackageManager.Stub Slog.v(TAG, "Ephemeral resolver found;" + " pkg: " + packageName + ", info:" + info); } - return new Pair<>(new ComponentName(packageName, info.serviceInfo.name), actionName); + return new ComponentName(packageName, info.serviceInfo.name); } if (DEBUG_INSTANT) { Slog.v(TAG, "Ephemeral resolver NOT found"); @@ -4367,7 +4446,7 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mLock") private @Nullable ActivityInfo getInstantAppInstallerLPr() { - String[] orderedActions = Build.IS_ENG + String[] orderedActions = mIsEngBuild ? new String[]{ Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE + "_TEST", Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE} @@ -4378,7 +4457,7 @@ public class PackageManagerService extends IPackageManager.Stub MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | Intent.FLAG_IGNORE_EPHEMERAL - | (!Build.IS_ENG ? MATCH_SYSTEM_ONLY : 0); + | (mIsEngBuild ? 0 : MATCH_SYSTEM_ONLY); final Intent intent = new Intent(); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE); @@ -4398,8 +4477,9 @@ public class PackageManagerService extends IPackageManager.Stub Iterator<ResolveInfo> iter = matches.iterator(); while (iter.hasNext()) { final ResolveInfo rInfo = iter.next(); - if (checkPermission(Manifest.permission.INSTALL_PACKAGES, - rInfo.activityInfo.packageName, 0) == PERMISSION_GRANTED || Build.IS_ENG) { + if (checkPermission( + Manifest.permission.INSTALL_PACKAGES, + rInfo.activityInfo.packageName, 0) == PERMISSION_GRANTED || mIsEngBuild) { continue; } iter.remove(); @@ -4627,7 +4707,7 @@ public class PackageManagerService extends IPackageManager.Stub enforceCrossUserPermission(callingUid, userId, false, false, "checkPackageStartable"); final boolean userKeyUnlocked = StorageManager.isUserKeyUnlocked(userId); synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) { throw new SecurityException("Package " + packageName + " was not found!"); } @@ -4743,7 +4823,7 @@ public class PackageManagerService extends IPackageManager.Stub return generatePackageInfo(ps, flags, userId); } if (!matchFactoryOnly && (flags & MATCH_KNOWN_PACKAGES) != 0) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null) return null; if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) { return null; @@ -4951,7 +5031,7 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUserId = UserHandle.getUserId(callingUid); final boolean canViewInstantApps = canViewInstantApps(callingUid, callingUserId); for (int i=names.length-1; i>=0; i--) { - final PackageSetting ps = mSettings.mPackages.get(names[i]); + final PackageSetting ps = mSettings.getPackageLPr(names[i]); boolean translateName = false; if (ps != null && ps.realName != null) { final boolean targetIsInstantApp = ps.getInstantApp(callingUserId); @@ -4981,7 +5061,7 @@ public class PackageManagerService extends IPackageManager.Stub final String cur = mSettings.getRenamedPackageLPr(names[i]); boolean translateName = false; if (cur != null) { - final PackageSetting ps = mSettings.mPackages.get(names[i]); + final PackageSetting ps = mSettings.getPackageLPr(names[i]); final boolean targetIsInstantApp = ps != null && ps.getInstantApp(callingUserId); translateName = !targetIsInstantApp @@ -5017,7 +5097,7 @@ public class PackageManagerService extends IPackageManager.Stub return UserHandle.getUid(userId, p.getUid()); } if ((flags & MATCH_KNOWN_PACKAGES) != 0) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null && ps.isMatch(flags) && !shouldFilterApplicationLocked(ps, callingUid, userId)) { return UserHandle.getUid(userId, ps.appId); @@ -5049,7 +5129,7 @@ public class PackageManagerService extends IPackageManager.Stub return mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId)); } if ((flags & MATCH_KNOWN_PACKAGES) != 0) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null && ps.isMatch(flags) && !shouldFilterApplicationLocked(ps, callingUid, userId)) { return mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId)); @@ -5075,7 +5155,7 @@ public class PackageManagerService extends IPackageManager.Stub private ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags, int filterCallingUid, int userId) { if (!mUserManager.exists(userId)) return null; - PackageSetting ps = mSettings.mPackages.get(packageName); + PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null) { if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) { return null; @@ -5133,7 +5213,7 @@ public class PackageManagerService extends IPackageManager.Stub TAG, "getApplicationInfo " + packageName + ": " + p); if (p != null) { - PackageSetting ps = mSettings.mPackages.get(packageName); + PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null) return null; if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) { return null; @@ -5602,7 +5682,7 @@ public class PackageManagerService extends IPackageManager.Stub if (a == null) { return false; } - PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); + PackageSetting ps = mSettings.getPackageLPr(component.getPackageName()); if (ps == null) { return false; } @@ -5642,7 +5722,7 @@ public class PackageManagerService extends IPackageManager.Stub } if (mSettings.isEnabledAndMatchLPr(pkg, a, flags, userId)) { - PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); + PackageSetting ps = mSettings.getPackageLPr(component.getPackageName()); if (ps == null) return null; if (shouldFilterApplicationLocked( ps, callingUid, component, TYPE_RECEIVER, userId)) { @@ -5802,9 +5882,9 @@ public class PackageManagerService extends IPackageManager.Stub private List<VersionedPackage> getPackagesUsingSharedLibraryLPr( SharedLibraryInfo libInfo, int flags, int userId) { List<VersionedPackage> versionedPackages = null; - final int packageCount = mSettings.mPackages.size(); + final int packageCount = mSettings.getPackagesLocked().size(); for (int i = 0; i < packageCount; i++) { - PackageSetting ps = mSettings.mPackages.valueAt(i); + PackageSetting ps = mSettings.getPackagesLocked().valueAt(i); if (ps == null) { continue; @@ -5863,7 +5943,7 @@ public class PackageManagerService extends IPackageManager.Stub AndroidPackage pkg = mPackages.get(s.getPackageName()); if (mSettings.isEnabledAndMatchLPr(pkg, s, flags, userId)) { - PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); + PackageSetting ps = mSettings.getPackageLPr(component.getPackageName()); if (ps == null) return null; if (shouldFilterApplicationLocked( ps, callingUid, component, TYPE_SERVICE, userId)) { @@ -5897,7 +5977,7 @@ public class PackageManagerService extends IPackageManager.Stub } if (mSettings.isEnabledAndMatchLPr(pkg, p, flags, userId)) { - PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); + PackageSetting ps = mSettings.getPackageLPr(component.getPackageName()); if (ps == null) return null; if (shouldFilterApplicationLocked( ps, callingUid, component, TYPE_PROVIDER, userId)) { @@ -6034,7 +6114,7 @@ public class PackageManagerService extends IPackageManager.Stub final String packageName = changedPackages.get(i); if (packageName != null) { // Filter out the changes if the calling package should not be able to see it. - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (shouldFilterApplicationLocked(ps, callingUid, userId)) { continue; } @@ -6055,7 +6135,7 @@ public class PackageManagerService extends IPackageManager.Stub res.addAll(mAvailableFeatures.values()); } final FeatureInfo fi = new FeatureInfo(); - fi.reqGlEsVersion = mInjector.getSystemWrapper().getPropertyInt("ro.opengles.version", + fi.reqGlEsVersion = SystemProperties.getInt("ro.opengles.version", FeatureInfo.GL_ES_VERSION_UNDEFINED); res.add(fi); @@ -6781,7 +6861,7 @@ public class PackageManagerService extends IPackageManager.Stub for (int n = 0; n < count; n++) { final ResolveInfo info = resolvedActivities.get(n); final String packageName = info.activityInfo.packageName; - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null) { // only check domain verification status if the app is not a browser if (!info.handleAllWebDataURI) { @@ -6863,7 +6943,7 @@ public class PackageManagerService extends IPackageManager.Stub // If we have an ephemeral app, use it if (ri.activityInfo.applicationInfo.isInstantApp()) { final String packageName = ri.activityInfo.packageName; - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); final long packedStatus = getDomainVerificationStatusLPr(ps, userId); final int status = (int)(packedStatus >> 32); if (status != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) { @@ -7297,7 +7377,7 @@ public class PackageManagerService extends IPackageManager.Stub private List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent, String resolvedType, int userId) { - CrossProfileIntentResolver resolver = mSettings.getCrossProfileIntentResolvers(userId); + CrossProfileIntentResolver resolver = mSettings.getCrossProfileIntentResolver(userId); if (resolver != null) { return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId); } @@ -7545,7 +7625,7 @@ public class PackageManagerService extends IPackageManager.Stub for (int i = instantApps.size() - 1; i >= 0; --i) { final ResolveInfo info = instantApps.get(i); final String packageName = info.activityInfo.packageName; - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps.getInstantApp(userId)) { final long packedStatus = getDomainVerificationStatusLPr(ps, userId); final int status = (int)(packedStatus >> 32); @@ -7600,7 +7680,7 @@ public class PackageManagerService extends IPackageManager.Stub if (intent.isWebIntent() && auxiliaryResponse == null) { return result; } - final PackageSetting ps = mSettings.mPackages.get(mInstantAppInstallerActivity.packageName); + final PackageSetting ps = mSettings.getPackageLPr(mInstantAppInstallerActivity.packageName); if (ps == null || !ps.readUserState(userId).isEnabled(mInstantAppInstallerActivity, 0)) { return result; @@ -7660,7 +7740,7 @@ public class PackageManagerService extends IPackageManager.Stub continue; } String packageName = riTargetUser.activityInfo.packageName; - PackageSetting ps = mSettings.mPackages.get(packageName); + PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null) { continue; } @@ -7885,7 +7965,7 @@ public class PackageManagerService extends IPackageManager.Stub for (int n=0; n<count; n++) { ResolveInfo info = candidates.get(n); String packageName = info.activityInfo.packageName; - PackageSetting ps = mSettings.mPackages.get(packageName); + PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null) { // Add to the special match all list (Browser use case) if (info.handleAllWebDataURI) { @@ -7964,8 +8044,8 @@ public class PackageManagerService extends IPackageManager.Stub } else { // Browser/generic handling case. If there's a default browser, go straight // to that (but only if there is no other higher-priority match). - final String defaultBrowserPackageName = - mPermissionManager.getDefaultBrowser(userId); + final String defaultBrowserPackageName = mDefaultAppProvider.getDefaultBrowser( + userId); int maxMatchPrio = 0; ResolveInfo defaultBrowserMatch = null; final int numCandidates = matchAllList.size(); @@ -8749,8 +8829,8 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { ArrayList<PackageInfo> list; if (listUninstalled) { - list = new ArrayList<>(mSettings.mPackages.size()); - for (PackageSetting ps : mSettings.mPackages.values()) { + list = new ArrayList<>(mSettings.getPackagesLocked().size()); + for (PackageSetting ps : mSettings.getPackagesLocked().values()) { if (listFactory) { if (!ps.isSystem()) { continue; @@ -8862,7 +8942,7 @@ public class PackageManagerService extends IPackageManager.Stub ArrayList<PackageInfo> list = new ArrayList<>(); boolean[] tmpBools = new boolean[permissions.length]; if (listUninstalled) { - for (PackageSetting ps : mSettings.mPackages.values()) { + for (PackageSetting ps : mSettings.getPackagesLocked().values()) { addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags, userId); } @@ -8907,8 +8987,8 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { ArrayList<ApplicationInfo> list; if (listUninstalled) { - list = new ArrayList<>(mSettings.mPackages.size()); - for (PackageSetting ps : mSettings.mPackages.values()) { + list = new ArrayList<>(mSettings.getPackagesLocked().size()); + for (PackageSetting ps : mSettings.getPackagesLocked().values()) { ApplicationInfo ai; int effectiveFlags = flags; if (ps.isSystem()) { @@ -9000,7 +9080,7 @@ public class PackageManagerService extends IPackageManager.Stub if (Process.isIsolated(callingUid)) { callingUid = mIsolatedOwners.get(callingUid); } - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); final boolean returnAllowed = ps != null && (isCallerSameApp(packageName, callingUid) @@ -9099,7 +9179,7 @@ public class PackageManagerService extends IPackageManager.Stub if (p.isPersistent() && (!mSafeMode || p.isSystem()) && (matchesUnaware || matchesAware)) { - PackageSetting ps = mSettings.mPackages.get(p.getPackageName()); + PackageSetting ps = mSettings.getPackageLPr(p.getPackageName()); if (ps != null) { ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(p, flags, ps.readUserState(userId), userId, ps); @@ -9144,7 +9224,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { return null; } - final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName); + final PackageSetting ps = mSettings.getPackageLPr(providerInfo.packageName); final ComponentName component = new ComponentName(providerInfo.packageName, providerInfo.name); if (shouldFilterApplicationLocked(ps, callingUid, component, TYPE_PROVIDER, userId)) { @@ -9184,7 +9264,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { continue; } - final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName); + final PackageSetting ps = mSettings.getPackageLPr(providerInfo.packageName); final ComponentName component = new ComponentName(providerInfo.packageName, providerInfo.name); if (shouldFilterApplicationLocked( @@ -9213,7 +9293,7 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); String packageName = component.getPackageName(); - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); AndroidPackage pkg = mPackages.get(packageName); if (ps == null || pkg == null) return null; if (shouldFilterApplicationLocked( @@ -9445,8 +9525,7 @@ public class PackageManagerService extends IPackageManager.Stub Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage"); final ParsedPackage parsedPackage; - try (PackageParser2 pp = new PackageParser2(mSeparateProcesses, mOnlyCore, mMetrics, null, - mPackageParserCallback)) { + try (PackageParser2 pp = mInjector.getScanningPackageParser()) { parsedPackage = pp.parsePackage(scanFile, parseFlags, false); } catch (PackageParserException e) { throw PackageManagerException.from(e); @@ -9740,7 +9819,7 @@ public class PackageManagerService extends IPackageManager.Stub pkgName, getSettingsVersionForPackage(parsedPackage)), Collections.singletonMap(pkgName, getSharedLibLatestVersionSetting(scanResult))), - mSettings.mKeySetManagerService, mInjector); + mSettings.getKeySetManagerService(), mInjector); appIdCreated = optimisticallyRegisterAppId(scanResult); commitReconciledScanResultLocked( reconcileResult.get(pkgName), mUserManager.getUserIds()); @@ -10051,7 +10130,7 @@ public class PackageManagerService extends IPackageManager.Stub List<PackageSetting> pkgSettings; synchronized (mLock) { pkgSettings = PackageManagerServiceUtils.getPackagesForDexopt( - mSettings.mPackages.values(), this); + mSettings.getPackagesLocked().values(), this); } List<AndroidPackage> pkgs = new ArrayList<>(pkgSettings.size()); @@ -10196,7 +10275,7 @@ public class PackageManagerService extends IPackageManager.Stub pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT; } - if (mInjector.getSystemWrapper().getPropertyBoolean(PRECOMPILE_LAYOUTS, false)) { + if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) { mArtManagerService.compileLayouts(pkg); } @@ -10409,7 +10488,7 @@ public class PackageManagerService extends IPackageManager.Stub // Package could not be found. Report failure. return PackageDexOptimizer.DEX_OPT_FAILED; } - mPackageUsage.maybeWriteAsync(mSettings.mPackages); + mPackageUsage.maybeWriteAsync(mSettings.getPackagesLocked()); mCompilerStats.maybeWriteAsync(); } final long callingId = Binder.clearCallingIdentity(); @@ -10639,7 +10718,7 @@ public class PackageManagerService extends IPackageManager.Stub PackageWatchdog.getInstance(mContext).writeNow(); synchronized (mLock) { - mPackageUsage.writeNow(mSettings.mPackages); + mPackageUsage.writeNow(mSettings.getPackagesLocked()); // This is the last chance to write out pending restriction settings if (mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) { @@ -10769,7 +10848,7 @@ public class PackageManagerService extends IPackageManager.Stub private void clearAppDataLeafLIF(AndroidPackage pkg, int userId, int flags) { final PackageSetting ps; synchronized (mLock) { - ps = mSettings.mPackages.get(pkg.getPackageName()); + ps = mSettings.getPackageLPr(pkg.getPackageName()); } for (int realUserId : resolveUserIds(userId)) { final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0; @@ -10793,7 +10872,7 @@ public class PackageManagerService extends IPackageManager.Stub private void destroyAppDataLeafLIF(AndroidPackage pkg, int userId, int flags) { final PackageSetting ps; synchronized (mLock) { - ps = mSettings.mPackages.get(pkg.getPackageName()); + ps = mSettings.getPackageLPr(pkg.getPackageName()); } for (int realUserId : resolveUserIds(userId)) { final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0; @@ -11173,7 +11252,7 @@ public class PackageManagerService extends IPackageManager.Stub } private int getVendorPartitionVersion() { - final String version = mInjector.getSystemWrapper().getProperty("ro.vndk.version"); + final String version = SystemProperties.get("ro.vndk.version"); if (!version.isEmpty()) { try { return Integer.parseInt(version); @@ -11384,7 +11463,7 @@ public class PackageManagerService extends IPackageManager.Stub // to allowlist their privileged permissions just like other // priv-apps. synchronized (mLock) { - PackageSetting platformPkgSetting = mSettings.mPackages.get("android"); + PackageSetting platformPkgSetting = mSettings.getPackageLPr("android"); if ((compareSignatures(platformPkgSetting.signatures.mSigningDetails.signatures, pkg.getSigningDetails().signatures) != PackageManager.SIGNATURE_MATCH)) { @@ -11506,6 +11585,7 @@ public class PackageManagerService extends IPackageManager.Stub parsedPackage.setVersionCode(mSdkVersion) .setVersionCodeMajor(0); } + final AndroidPackage oldPkg = request.oldPkg; final @ParseFlags int parseFlags = request.parseFlags; final @ScanFlags int scanFlags = request.scanFlags; @@ -11545,7 +11625,7 @@ public class PackageManagerService extends IPackageManager.Stub if (reconciledPkg.installArgs != null) { InstallSource installSource = reconciledPkg.installArgs.installSource; if (installSource.initiatingPackageName != null) { - final PackageSetting ips = mSettings.mPackages.get( + final PackageSetting ips = mSettings.getPackageLPr( installSource.initiatingPackageName); if (ips != null) { installSource = installSource.setInitiatingPackageSignatures( @@ -11575,7 +11655,7 @@ public class PackageManagerService extends IPackageManager.Stub reconciledPkg.collectedSharedLibraryInfos, allUsers); } - final KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.getKeySetManagerService(); if (reconciledPkg.removeAppKeySetData) { ksms.removeAppKeySetDataLPw(pkg.getPackageName()); } @@ -11937,12 +12017,13 @@ public class PackageManagerService extends IPackageManager.Stub final String cpuAbiOverride = deriveAbiOverride(request.cpuAbiOverride); final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp(); + final File appLib32InstallDir = getAppLib32InstallDir(); if ((scanFlags & SCAN_NEW_INSTALL) == 0) { if (needToDeriveAbi) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi"); final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi = packageAbiHelper.derivePackageAbi(parsedPackage, isUpdatedSystemApp, - cpuAbiOverride); + cpuAbiOverride, appLib32InstallDir); derivedAbi.first.applyTo(parsedPackage); derivedAbi.second.applyTo(parsedPackage); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); @@ -11960,7 +12041,7 @@ public class PackageManagerService extends IPackageManager.Stub abis.applyTo(pkgSetting); final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths = packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, - isUpdatedSystemApp, sAppLib32InstallDir); + isUpdatedSystemApp, appLib32InstallDir); nativeLibraryPaths.applyTo(parsedPackage); } } else { @@ -11972,7 +12053,7 @@ public class PackageManagerService extends IPackageManager.Stub final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths = packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, - isUpdatedSystemApp, sAppLib32InstallDir); + isUpdatedSystemApp, appLib32InstallDir); nativeLibraryPaths.applyTo(parsedPackage); if (DEBUG_ABI_SELECTION) { @@ -11998,7 +12079,7 @@ public class PackageManagerService extends IPackageManager.Stub // package path (after the rename away from the stage path). final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths = packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, isUpdatedSystemApp, - sAppLib32InstallDir); + appLib32InstallDir); nativeLibraryPaths.applyTo(parsedPackage); } @@ -12293,7 +12374,7 @@ public class PackageManagerService extends IPackageManager.Stub } // Make sure we're not adding any bogus keyset info - final KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.getKeySetManagerService(); ksms.assertScannedPackageValid(pkg); synchronized (mLock) { @@ -12511,7 +12592,7 @@ public class PackageManagerService extends IPackageManager.Stub } if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) { // Exempt SharedUsers signed with the platform key. - PackageSetting platformPkgSetting = mSettings.mPackages.get("android"); + PackageSetting platformPkgSetting = mSettings.getPackageLPr("android"); if (!comparePackageSignatures(platformPkgSetting, pkg.getSigningDetails().signatures)) { throw new PackageManagerException("Apps that share a user with a " + @@ -12685,7 +12766,7 @@ public class PackageManagerService extends IPackageManager.Stub continue; } for (VersionedPackage dependentPackage : dependents) { - final PackageSetting ps = mSettings.mPackages.get( + final PackageSetting ps = mSettings.getPackageLPr( dependentPackage.getPackageName()); if (ps != null) { ps.setOverlayPathsForLibrary(libraryInfo.getName(), null, currentUserId); @@ -12844,7 +12925,6 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { // We don't expect installation to fail beyond this point - // Add the new setting to mSettings mSettings.insertPackageSettingLPw(pkgSetting, pkg); // Add the new setting to mPackages @@ -12854,7 +12934,7 @@ public class PackageManagerService extends IPackageManager.Stub } // Add the package's KeySets to the global KeySetManagerService - KeySetManagerService ksms = mSettings.mKeySetManagerService; + KeySetManagerService ksms = mSettings.getKeySetManagerService(); ksms.addScannedPackageLPw(pkg); mComponentResolver.addAllComponents(pkg, chatty); @@ -13329,7 +13409,7 @@ public class PackageManagerService extends IPackageManager.Stub packageName, extras, 0, null, null, userIds, instantUserIds, mAppsFilter.getVisibilityAllowList( getPackageSettingInternal(packageName, Process.SYSTEM_UID), - userIds, mSettings.mPackages)); + userIds, mSettings.getPackagesLocked())); if (sendBootCompleted && !ArrayUtils.isEmpty(userIds)) { mHandler.post(() -> { for (int userId : userIds) { @@ -13400,7 +13480,7 @@ public class PackageManagerService extends IPackageManager.Stub boolean sendRemoved = false; // writer synchronized (mLock) { - pkgSetting = mSettings.mPackages.get(packageName); + pkgSetting = mSettings.getPackageLPr(packageName); if (pkgSetting == null) { return false; } @@ -13466,7 +13546,7 @@ public class PackageManagerService extends IPackageManager.Stub } synchronized (mLock) { - final PackageSetting pkgSetting = mSettings.mPackages.get(packageName); + final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName); if (pkgSetting == null || !pkgSetting.isSystem()) { return; } @@ -13493,7 +13573,7 @@ public class PackageManagerService extends IPackageManager.Stub } synchronized (mLock) { - final PackageSetting pkgSetting = mSettings.mPackages.get(packageName); + final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName); // The target app should always be in system if (pkgSetting == null || !pkgSetting.isSystem()) { return false; @@ -13580,7 +13660,7 @@ public class PackageManagerService extends IPackageManager.Stub try { // writer synchronized (mLock) { - ps = mSettings.mPackages.get(packageName); + ps = mSettings.getPackageLPr(packageName); if (ps == null) { return true; } @@ -13641,7 +13721,7 @@ public class PackageManagerService extends IPackageManager.Stub // writer synchronized (mLock) { - pkgSetting = mSettings.mPackages.get(packageName); + pkgSetting = mSettings.getPackageLPr(packageName); if (pkgSetting == null) { return PackageManager.INSTALL_FAILED_INVALID_URI; } @@ -13791,7 +13871,7 @@ public class PackageManagerService extends IPackageManager.Stub final String packageName = packageNames[i]; final PackageSetting pkgSetting; synchronized (mLock) { - pkgSetting = mSettings.mPackages.get(packageName); + pkgSetting = mSettings.getPackageLPr(packageName); if (pkgSetting == null || shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) { Slog.w(TAG, "Could not find package setting for package: " + packageName @@ -13889,7 +13969,7 @@ public class PackageManagerService extends IPackageManager.Stub } final PackageSetting pkgSetting; synchronized (mLock) { - pkgSetting = mSettings.mPackages.get(packageName); + pkgSetting = mSettings.getPackageLPr(packageName); if (pkgSetting == null || shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) { Slog.w(TAG, "Could not find package setting for package: " + packageName @@ -13942,7 +14022,7 @@ public class PackageManagerService extends IPackageManager.Stub private Bundle getSuspendedPackageAppExtrasInternal(String packageName, int userId) { synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null) { return null; } @@ -13997,7 +14077,7 @@ public class PackageManagerService extends IPackageManager.Stub enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "isPackageSuspendedForUser for user " + userId); synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) { throw new IllegalArgumentException("Unknown target package: " + packageName); } @@ -14015,7 +14095,7 @@ public class PackageManagerService extends IPackageManager.Stub boolean isSuspendingAnyPackages(String suspendingPackage, int userId) { synchronized (mLock) { - for (final PackageSetting ps : mSettings.mPackages.values()) { + for (final PackageSetting ps : mSettings.getPackagesLocked().values()) { if (ps.isSuspendedBy(suspendingPackage, userId)) { return true; } @@ -14041,7 +14121,7 @@ public class PackageManagerService extends IPackageManager.Stub final IntArray unsuspendedUids = new IntArray(); synchronized (mLock) { for (String packageName : packagesToChange) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null && ps.getSuspended(userId)) { ps.removeSuspension(suspendingPackagePredicate, userId); if (!ps.getSuspended(userId)) { @@ -14082,7 +14162,7 @@ public class PackageManagerService extends IPackageManager.Stub final IntArray changedUids = new IntArray(); synchronized (mLock) { for (String packageName : packagesToChange) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null && ps.getDistractionFlags(userId) != 0) { ps.setDistractionFlags(0, userId); changedPackages.add(ps.name); @@ -14138,7 +14218,7 @@ public class PackageManagerService extends IPackageManager.Stub continue; } synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageNames[i]); + final PackageSetting ps = mSettings.getPackageLPr(packageNames[i]); if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) { Slog.w(TAG, "Could not find package setting for package: " + packageNames[i]); unactionablePackages.add(packageNames[i]); @@ -14162,8 +14242,8 @@ public class PackageManagerService extends IPackageManager.Stub final boolean isCallerOwner = isCallerDeviceOrProfileOwner(userId); final long callingId = Binder.clearCallingIdentity(); try { - final String activeLauncherPackageName = mPermissionManager.getDefaultHome(userId); - final String dialerPackageName = mPermissionManager.getDefaultDialer(userId); + final String activeLauncherPackageName = mDefaultAppProvider.getDefaultHome(userId); + final String dialerPackageName = mDefaultAppProvider.getDefaultDialer(userId); for (int i = 0; i < packageNames.length; i++) { canSuspend[i] = false; final String packageName = packageNames[i]; @@ -14485,7 +14565,7 @@ public class PackageManagerService extends IPackageManager.Stub // Check if the developer wants to skip verification for ADB installs if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) { synchronized (mLock) { - if (mSettings.mPackages.get(pkgInfoLite.packageName) == null) { + if (mSettings.getPackageLPr(pkgInfoLite.packageName) == null) { // Always verify fresh install return true; } @@ -14552,7 +14632,7 @@ public class PackageManagerService extends IPackageManager.Stub return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; } synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null || shouldFilterApplicationLocked( ps, callingUid, UserHandle.getUserId(callingUid))) { @@ -14569,7 +14649,7 @@ public class PackageManagerService extends IPackageManager.Stub boolean result = false; synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (shouldFilterApplicationLocked( ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) { return false; @@ -14590,7 +14670,7 @@ public class PackageManagerService extends IPackageManager.Stub return ParceledListSlice.emptyList(); } synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (shouldFilterApplicationLocked(ps, callingUid, UserHandle.getUserId(callingUid))) { return ParceledListSlice.emptyList(); } @@ -14655,7 +14735,7 @@ public class PackageManagerService extends IPackageManager.Stub } // writer synchronized (mLock) { - PackageSetting targetPackageSetting = mSettings.mPackages.get(targetPackage); + PackageSetting targetPackageSetting = mSettings.getPackageLPr(targetPackage); if (targetPackageSetting == null || shouldFilterApplicationLocked( targetPackageSetting, callingUid, UserHandle.getUserId(callingUid))) { @@ -14664,7 +14744,7 @@ public class PackageManagerService extends IPackageManager.Stub PackageSetting installerPackageSetting; if (installerPackageName != null) { - installerPackageSetting = mSettings.mPackages.get(installerPackageName); + installerPackageSetting = mSettings.getPackageLPr(installerPackageName); if (installerPackageSetting == null) { throw new IllegalArgumentException("Unknown installer package: " + installerPackageName); @@ -14706,7 +14786,7 @@ public class PackageManagerService extends IPackageManager.Stub String targetInstallerPackageName = targetPackageSetting.installSource.installerPackageName; PackageSetting targetInstallerPkgSetting = targetInstallerPackageName == null ? null : - mSettings.mPackages.get(targetInstallerPackageName); + mSettings.getPackageLPr(targetInstallerPackageName); if (targetInstallerPkgSetting != null) { if (compareSignatures(callerSignature, @@ -14757,7 +14837,7 @@ public class PackageManagerService extends IPackageManager.Stub mInjector.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(), callerPackageName); synchronized (mLock) { - PackageSetting ps = mSettings.mPackages.get(packageName); + PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null) { throw new IllegalArgumentException("Unknown target package " + packageName); } @@ -16498,7 +16578,7 @@ public class PackageManagerService extends IPackageManager.Stub // For system-bundled packages, we assume that installing an upgraded version // of the package implies that the user actually wants to run that new code, // so we enable the package. - final PackageSetting ps = mSettings.mPackages.get(pkgName); + final PackageSetting ps = mSettings.getPackageLPr(pkgName); final int userId = installArgs.user.getIdentifier(); if (ps != null) { if (pkg.isSystem()) { @@ -16543,7 +16623,7 @@ public class PackageManagerService extends IPackageManager.Stub // TODO(146804378): Support overlaying static shared libraries continue; } - final PackageSetting libPs = mSettings.mPackages.get( + final PackageSetting libPs = mSettings.getPackageLPr( sharedLib.getPackageName()); if (libPs == null) { continue; @@ -16956,8 +17036,7 @@ public class PackageManagerService extends IPackageManager.Stub && compareSignatures(sharedUserSignatures, parsedPackage.getSigningDetails().signatures) != PackageManager.SIGNATURE_MATCH) { - if (injector.getSystemWrapper() - .getPropertyInt("ro.product.first_api_level", 0) <= 29) { + if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) { // Mismatched signatures is an error and silently skipping system // packages will likely break the device in unforeseen ways. // However, we allow the device to boot anyway because, prior to Q, @@ -17147,7 +17226,7 @@ public class PackageManagerService extends IPackageManager.Stub reconciledPkg.pkgSetting.lastUpdateTime = System.currentTimeMillis(); res.removedInfo.broadcastAllowList = mAppsFilter.getVisibilityAllowList( - reconciledPkg.pkgSetting, request.mAllUsers, mSettings.mPackages); + reconciledPkg.pkgSetting, request.mAllUsers, mSettings.getPackagesLocked()); if (reconciledPkg.prepareResult.system) { // Remove existing system package removePackageLI(oldPackage, true); @@ -17171,7 +17250,7 @@ public class PackageManagerService extends IPackageManager.Stub executeDeletePackageLIF(reconciledPkg.deletePackageAction, packageName, true, request.mAllUsers, false, parsedPackage); } catch (SystemDeleteException e) { - if (Build.IS_ENG) { + if (mIsEngBuild) { throw new RuntimeException("Unexpected failure", e); // ignore; not possible for non-system app } @@ -17192,7 +17271,7 @@ public class PackageManagerService extends IPackageManager.Stub } // Update the in-memory copy of the previous code paths. - PackageSetting ps1 = mSettings.mPackages.get( + PackageSetting ps1 = mSettings.getPackageLPr( reconciledPkg.prepareResult.existingPackage.getPackageName()); if ((reconciledPkg.installArgs.installFlags & PackageManager.DONT_KILL_APP) == 0) { @@ -17221,7 +17300,7 @@ public class PackageManagerService extends IPackageManager.Stub AndroidPackage pkg = commitReconciledScanResultLocked(reconciledPkg, request.mAllUsers); updateSettingsLI(pkg, reconciledPkg.installArgs, request.mAllUsers, res); - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null) { res.newUsers = ps.queryInstalledUsers(mUserManager.getUserIds(), true); ps.setUpdateAvailable(false /*updateAvailable*/); @@ -17331,7 +17410,7 @@ public class PackageManagerService extends IPackageManager.Stub try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages"); reconciledPackages = reconcilePackagesLocked( - reconcileRequest, mSettings.mKeySetManagerService, mInjector); + reconcileRequest, mSettings.getKeySetManagerService(), mInjector); } catch (ReconcileFailure e) { for (InstallRequest request : requests) { request.installResult.setError("Reconciliation failed...", e); @@ -17466,7 +17545,7 @@ public class PackageManagerService extends IPackageManager.Stub if (performDexopt) { // Compile the layout resources. - if (mInjector.getSystemWrapper().getPropertyBoolean(PRECOMPILE_LAYOUTS, false)) { + if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "compileLayouts"); mViewCompiler.compileLayouts(pkg); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); @@ -17809,7 +17888,7 @@ public class PackageManagerService extends IPackageManager.Stub final SigningDetails sourceSigningDetails = (sourcePackageSetting == null ? SigningDetails.UNKNOWN : sourcePackageSetting.getSigningDetails()); - final KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.getKeySetManagerService(); if (sourcePackageName.equals(parsedPackage.getPackageName()) && (ksms.shouldCheckUpgradeKeySetLocked( sourcePackageSetting, scanFlags))) { @@ -17893,8 +17972,7 @@ public class PackageManagerService extends IPackageManager.Stub Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage"); final ParsedPackage parsedPackage; - try (PackageParser2 pp = new PackageParser2(mSeparateProcesses, false, mMetrics, null, - mPackageParserCallback)) { + try (PackageParser2 pp = mInjector.getPreparingPackageParser()) { parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false); AndroidPackageUtils.validatePackageDexMetadata(parsedPackage); } catch (PackageParserException e) { @@ -17943,8 +18021,8 @@ public class PackageManagerService extends IPackageManager.Stub if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) { parsedPackage.setSigningDetails(args.signingDetails); } else { - parsedPackage.setSigningDetails( - ParsingPackageUtils.getSigningDetails(parsedPackage, false /* skipVerify */)); + parsedPackage.setSigningDetails(ParsingPackageUtils.getSigningDetails( + parsedPackage, false /* skipVerify */)); } } catch (PackageParserException e) { throw new PrepareFailure("Failed collect during installPackageLI", e); @@ -18008,7 +18086,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - PackageSetting ps = mSettings.mPackages.get(pkgName); + PackageSetting ps = mSettings.getPackageLPr(pkgName); if (ps != null) { if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps); @@ -18027,7 +18105,7 @@ public class PackageManagerService extends IPackageManager.Stub // Quick validity check that we're signed correctly if updating; // we'll check this again later when scanning, but we want to // bail early here before tripping over redefined permissions. - final KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.getKeySetManagerService(); if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) { if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) { throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " @@ -18208,7 +18286,7 @@ public class PackageManagerService extends IPackageManager.Stub scanFlags |= SCAN_MOVE; synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(pkgName); + final PackageSetting ps = mSettings.getPackageLPr(pkgName); if (ps == null) { res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Missing settings for moved package " + pkgName); @@ -18237,7 +18315,7 @@ public class PackageManagerService extends IPackageManager.Stub final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi = mInjector.getAbiHelper().derivePackageAbi(parsedPackage, isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred, - abiOverride); + abiOverride, mAppLib32InstallDir); derivedAbi.first.applyTo(parsedPackage); derivedAbi.second.applyTo(parsedPackage); } catch (PackageManagerException pme) { @@ -18306,11 +18384,11 @@ public class PackageManagerService extends IPackageManager.Stub "replacePackageLI: new=" + parsedPackage + ", old=" + oldPackage); } - ps = mSettings.mPackages.get(pkgName11); + ps = mSettings.getPackageLPr(pkgName11); disabledPs = mSettings.getDisabledSystemPkgLPr(ps); // verify signatures are valid - final KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.getKeySetManagerService(); if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) { if (!ksms.checkUpgradeKeySetLocked(ps, parsedPackage)) { throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, @@ -18513,7 +18591,7 @@ public class PackageManagerService extends IPackageManager.Stub ArrayMap<String, String> fsverityCandidates = new ArrayMap<>(); if (legacyMode) { synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(pkg.getPackageName()); + final PackageSetting ps = mSettings.getPackageLPr(pkg.getPackageName()); if (ps != null && ps.isPrivileged()) { fsverityCandidates.put(pkg.getBaseApkPath(), null); if (pkg.getSplitCodePaths() != null) { @@ -18931,7 +19009,7 @@ public class PackageManagerService extends IPackageManager.Stub // Queue up an async operation since the package deletion may take a little while. mHandler.post(() -> { int returnCode; - final PackageSetting ps = mSettings.mPackages.get(internalPackageName); + final PackageSetting ps = mSettings.getPackageLPr(internalPackageName); boolean doDeletePackage = true; if (ps != null) { final boolean targetIsInstantApp = @@ -19209,7 +19287,7 @@ public class PackageManagerService extends IPackageManager.Stub int[] allUsers; /** enabled state of the uninstalled application */ synchronized (mLock) { - uninstalledPs = mSettings.mPackages.get(packageName); + uninstalledPs = mSettings.getPackageLPr(packageName); if (uninstalledPs == null) { Slog.w(TAG, "Not removing non-existent package " + packageName); return PackageManager.DELETE_FAILED_INTERNAL_ERROR; @@ -19508,7 +19586,7 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL, true); clearDefaultBrowserIfNeeded(packageName); - mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName); + mSettings.getKeySetManagerService().removeAppKeySetDataLPw(packageName); mAppsFilter.removePackage(getPackageSetting(packageName)); removedAppId = mSettings.removePackageLPw(packageName); if (outInfo != null) { @@ -19658,7 +19736,7 @@ public class PackageManagerService extends IPackageManager.Stub // We've re-installed the stub; make sure it's disabled here. If package was // originally enabled, we'll install the compressed version of the application // and re-enable it afterward. - final PackageSetting stubPs = mSettings.mPackages.get(deletedPkg.getPackageName()); + final PackageSetting stubPs = mSettings.getPackageLPr(deletedPkg.getPackageName()); if (stubPs != null) { int userId = action.user == null ? UserHandle.USER_ALL : action.user.getIdentifier(); @@ -19714,7 +19792,7 @@ public class PackageManagerService extends IPackageManager.Stub // writer synchronized (mLock) { - PackageSetting ps = mSettings.mPackages.get(pkg.getPackageName()); + PackageSetting ps = mSettings.getPackageLPr(pkg.getPackageName()); final boolean applyUserRestrictions = origUserHandles != null; if (applyUserRestrictions) { @@ -19807,7 +19885,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean getBlockUninstallForUser(String packageName, int userId) { synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null || shouldFilterApplicationLocked(ps, Binder.getCallingUid(), userId)) { return false; } @@ -19819,7 +19897,7 @@ public class PackageManagerService extends IPackageManager.Stub public boolean setRequiredForSystemUser(String packageName, boolean systemUserApp) { enforceSystemOrRoot("setRequiredForSystemUser can only be run by the system or root"); synchronized (mLock) { - PackageSetting ps = mSettings.mPackages.get(packageName); + PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null) { Log.w(TAG, "Package doesn't exist: " + packageName); return false; @@ -19887,7 +19965,7 @@ public class PackageManagerService extends IPackageManager.Stub ParsedPackage replacingPackage) { final DeletePackageAction action; synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps); action = mayDeletePackageLocked(outInfo, ps, disabledPs, flags, user); } @@ -20177,7 +20255,7 @@ public class PackageManagerService extends IPackageManager.Stub PackageSetting ps; synchronized (mLock) { pkg = mPackages.get(packageName); - ps = mSettings.mPackages.get(packageName); + ps = mSettings.getPackageLPr(packageName); if (pkg == null) { if (ps != null) { pkg = ps.pkg; @@ -20314,7 +20392,7 @@ public class PackageManagerService extends IPackageManager.Stub private boolean getPackageSizeInfoLI(String packageName, int userId, PackageStats stats) { final PackageSetting ps; synchronized (mLock) { - ps = mSettings.mPackages.get(packageName); + ps = mSettings.getPackageLPr(packageName); if (ps == null) { Slog.w(TAG, "Failed to find settings for " + packageName); return false; @@ -20621,10 +20699,10 @@ public class PackageManagerService extends IPackageManager.Stub } private void clearDefaultBrowserIfNeededForUser(String packageName, int userId) { - final String defaultBrowserPackageName = mPermissionManager.getDefaultBrowser(userId); + final String defaultBrowserPackageName = mDefaultAppProvider.getDefaultBrowser(userId); if (!TextUtils.isEmpty(defaultBrowserPackageName)) { if (packageName.equals(defaultBrowserPackageName)) { - mPermissionManager.setDefaultBrowser(null, true, true, userId); + mDefaultAppProvider.setDefaultBrowser(null, true, true, userId); } } } @@ -20639,7 +20717,7 @@ public class PackageManagerService extends IPackageManager.Stub // If this browser is restored from user's backup, do not clear // default-browser state for this user if (installReason != PackageManager.INSTALL_REASON_DEVICE_RESTORE) { - mPermissionManager.setDefaultBrowser(null, true, true, userId); + mDefaultAppProvider.setDefaultBrowser(null, true, true, userId); } } @@ -20678,7 +20756,7 @@ public class PackageManagerService extends IPackageManager.Stub // 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. - mPermissionManager.setDefaultBrowser(null, true, true, userId); + mDefaultAppProvider.setDefaultBrowser(null, true, true, userId); resetNetworkPolicies(userId); synchronized (mLock) { scheduleWritePackageRestrictionsLocked(userId); @@ -20912,8 +20990,8 @@ public class PackageManagerService extends IPackageManager.Stub defaultBrowser = mSettings.removeDefaultBrowserPackageNameLPw(userId1); } if (defaultBrowser != null) { - mPermissionManager - .setDefaultBrowser(defaultBrowser, false, false, userId1); + mDefaultAppProvider.setDefaultBrowser(defaultBrowser, false, false, + userId1); } }); } catch (Exception e) { @@ -21153,7 +21231,7 @@ public class PackageManagerService extends IPackageManager.Stub } allHomeCandidates.addAll(resolveInfos); - final String packageName = mPermissionManager.getDefaultHome(userId); + final String packageName = mDefaultAppProvider.getDefaultHome(userId); if (packageName == null) { return null; } @@ -21207,7 +21285,7 @@ public class PackageManagerService extends IPackageManager.Stub final String packageName = preferredResolveInfo != null && preferredResolveInfo.activityInfo != null ? preferredResolveInfo.activityInfo.packageName : null; - final String currentPackageName = mPermissionManager.getDefaultHome(userId); + final String currentPackageName = mDefaultAppProvider.getDefaultHome(userId); if (TextUtils.equals(currentPackageName, packageName)) { return false; } @@ -21222,12 +21300,12 @@ public class PackageManagerService extends IPackageManager.Stub // Keep the default home package in RoleManager. return false; } - mPermissionManager.setDefaultHome(packageName, userId, (successful) -> { - if (successful) { - postPreferredActivityChangedBroadcast(userId); - } - }); - return true; + return mDefaultAppProvider.setDefaultHome(packageName, userId, mContext.getMainExecutor(), + successful -> { + if (successful) { + postPreferredActivityChangedBroadcast(userId); + } + }); } @Override @@ -21490,7 +21568,7 @@ public class PackageManagerService extends IPackageManager.Stub public void setUpdateAvailable(String packageName, boolean updateAvailable) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null); synchronized (mLock) { - final PackageSetting pkgSetting = mSettings.mPackages.get(packageName); + final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName); if (pkgSetting != null) { pkgSetting.setUpdateAvailable(updateAvailable); } @@ -21615,7 +21693,7 @@ public class PackageManagerService extends IPackageManager.Stub // reader synchronized (mLock) { - pkgSetting = mSettings.mPackages.get(packageName); + pkgSetting = mSettings.getPackageLPr(packageName); if (pkgSetting == null) { if (!isCallerInstantApp) { if (className == null) { @@ -21879,7 +21957,7 @@ public class PackageManagerService extends IPackageManager.Stub return; } broadcastAllowList = isInstantApp ? null : mAppsFilter.getVisibilityAllowList(setting, - userIds, mSettings.mPackages); + userIds, mSettings.getPackagesLocked()); } sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, flags, null, null, userIds, instantUserIds, broadcastAllowList); @@ -21899,7 +21977,7 @@ public class PackageManagerService extends IPackageManager.Stub true /* checkShell */, "stop package"); // writer synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (!shouldFilterApplicationLocked(ps, callingUid, userId) && mSettings.setPackageStoppedStateLPw(this, packageName, stopped, allowedByPermission, callingUid, userId)) { @@ -21918,7 +21996,7 @@ public class PackageManagerService extends IPackageManager.Stub } String installerPackageName = installSource.installerPackageName; if (installerPackageName != null) { - final PackageSetting ps = mSettings.mPackages.get(installerPackageName); + final PackageSetting ps = mSettings.getPackageLPr(installerPackageName); if (ps == null || shouldFilterApplicationLocked(ps, callingUid, UserHandle.getUserId(callingUid))) { installerPackageName = null; @@ -21947,7 +22025,7 @@ public class PackageManagerService extends IPackageManager.Stub installerPackageName = installSource.installerPackageName; if (installerPackageName != null) { - final PackageSetting ps = mSettings.mPackages.get(installerPackageName); + final PackageSetting ps = mSettings.getPackageLPr(installerPackageName); if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) { installerPackageName = null; } @@ -21972,7 +22050,7 @@ public class PackageManagerService extends IPackageManager.Stub initiatingPackageName = installerPackageName; } else { initiatingPackageName = installSource.initiatingPackageName; - final PackageSetting ps = mSettings.mPackages.get(initiatingPackageName); + final PackageSetting ps = mSettings.getPackageLPr(initiatingPackageName); if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) { initiatingPackageName = null; } @@ -21981,7 +22059,7 @@ public class PackageManagerService extends IPackageManager.Stub originatingPackageName = installSource.originatingPackageName; if (originatingPackageName != null) { - final PackageSetting ps = mSettings.mPackages.get(originatingPackageName); + final PackageSetting ps = mSettings.getPackageLPr(originatingPackageName); if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) { originatingPackageName = null; } @@ -22014,7 +22092,7 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mLock") @Nullable private InstallSource getInstallSourceLocked(String packageName, int callingUid) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); // Installer info for Apex is not stored in PackageManager if (ps == null && mApexManager.isApexPackage(packageName)) { @@ -22677,13 +22755,14 @@ public class PackageManagerService extends IPackageManager.Stub && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED) && packageName == null) { pw.println(); - int count = mSettings.mPackages.size(); + int count = mSettings.getPackagesLocked().size(); if (count == 0) { pw.println("No applications!"); pw.println(); } else { final String prefix = " "; - Collection<PackageSetting> allPackageSettings = mSettings.mPackages.values(); + Collection<PackageSetting> allPackageSettings = + mSettings.getPackagesLocked().values(); if (allPackageSettings.size() == 0) { pw.println("No domain preferred apps!"); pw.println(); @@ -22740,7 +22819,7 @@ public class PackageManagerService extends IPackageManager.Stub } if (!checkin && dumpState.isDumping(DumpState.DUMP_KEYSETS)) { - mSettings.mKeySetManagerService.dumpLPr(pw, packageName, dumpState); + mSettings.getKeySetManagerService().dumpLPr(pw, packageName, dumpState); } if (dumpState.isDumping(DumpState.DUMP_PACKAGES)) { @@ -22871,7 +22950,7 @@ public class PackageManagerService extends IPackageManager.Stub if (ArrayUtils.isEmpty(apkList)) { return; } - String sku = mInjector.getSystemWrapper().getProperty("ro.boot.hardware.sku"); + String sku = SystemProperties.get("ro.boot.hardware.sku"); if (!TextUtils.isEmpty(sku) && ArrayUtils.contains(skuArray, sku)) { return; } @@ -22967,7 +23046,7 @@ public class PackageManagerService extends IPackageManager.Stub ipw.increaseIndent(); Collection<PackageSetting> pkgSettings; if (packageName != null) { - PackageSetting targetPkgSetting = mSettings.mPackages.get(packageName); + PackageSetting targetPkgSetting = mSettings.getPackageLPr(packageName); if (targetPkgSetting != null) { pkgSettings = Collections.singletonList(targetPkgSetting); } else { @@ -22975,7 +23054,7 @@ public class PackageManagerService extends IPackageManager.Stub return; } } else { - pkgSettings = mSettings.mPackages.values(); + pkgSettings = mSettings.getPackagesLocked().values(); } for (PackageSetting pkgSetting : pkgSettings) { @@ -23276,7 +23355,7 @@ public class PackageManagerService extends IPackageManager.Stub // Normalize package name to handle renamed packages packageName = normalizePackageNameLPr(packageName); - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null) { throw new PackageManagerException("Package " + packageName + " is unknown"); } else if (!TextUtils.equals(volumeUuid, ps.volumeUuid)) { @@ -23293,9 +23372,9 @@ public class PackageManagerService extends IPackageManager.Stub private List<String> collectAbsoluteCodePaths() { synchronized (mLock) { List<String> codePaths = new ArrayList<>(); - final int packageCount = mSettings.mPackages.size(); + final int packageCount = mSettings.getPackagesLocked().size(); for (int i = 0; i < packageCount; i++) { - final PackageSetting ps = mSettings.mPackages.valueAt(i); + final PackageSetting ps = mSettings.getPackagesLocked().valueAt(i); codePaths.add(ps.getPath().getAbsolutePath()); } return codePaths; @@ -23497,7 +23576,7 @@ public class PackageManagerService extends IPackageManager.Stub private void prepareAppDataAfterInstallLIF(AndroidPackage pkg) { final PackageSetting ps; synchronized (mLock) { - ps = mSettings.mPackages.get(pkg.getPackageName()); + ps = mSettings.getPackageLPr(pkg.getPackageName()); mSettings.writeKernelMappingLPr(ps); } @@ -23574,7 +23653,7 @@ public class PackageManagerService extends IPackageManager.Stub final PackageSetting ps; synchronized (mLock) { - ps = mSettings.mPackages.get(pkg.getPackageName()); + ps = mSettings.getPackageLPr(pkg.getPackageName()); } final String volumeUuid = pkg.getVolumeUuid(); final String packageName = pkg.getPackageName(); @@ -23769,7 +23848,7 @@ public class PackageManagerService extends IPackageManager.Stub mPackageName = packageName; mWeFroze = mFrozenPackages.add(mPackageName); - final PackageSetting ps = mSettings.mPackages.get(mPackageName); + final PackageSetting ps = mSettings.getPackageLPr(mPackageName); if (ps != null) { killApplication(ps.name, ps.appId, userId, killReason); } @@ -23851,7 +23930,7 @@ public class PackageManagerService extends IPackageManager.Stub // reader synchronized (mLock) { final AndroidPackage pkg = mPackages.get(packageName); - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (pkg == null || ps == null || shouldFilterApplicationLocked(ps, callingUid, user.getIdentifier())) { @@ -24182,9 +24261,9 @@ public class PackageManagerService extends IPackageManager.Stub private void removeUnusedPackagesLPw(UserManagerService userManager, final int userId) { final boolean DEBUG_CLEAN_APKS = false; int [] users = userManager.getUserIds(); - final int numPackages = mSettings.mPackages.size(); + final int numPackages = mSettings.getPackagesLocked().size(); for (int index = 0; index < numPackages; index++) { - final PackageSetting ps = mSettings.mPackages.valueAt(index); + final PackageSetting ps = mSettings.getPackagesLocked().valueAt(index); if (ps.pkg == null) { continue; } @@ -24353,7 +24432,7 @@ public class PackageManagerService extends IPackageManager.Stub Slog.w(TAG, "KeySet requested for filtered package: " + packageName); throw new IllegalArgumentException("Unknown package: " + packageName); } - final KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.getKeySetManagerService(); return new KeySet(ksms.getKeySetByAliasAndPackageNameLPr(packageName, alias)); } } @@ -24382,7 +24461,7 @@ public class PackageManagerService extends IPackageManager.Stub && Process.SYSTEM_UID != callingUid) { throw new SecurityException("May not access signing KeySet of other apps."); } - final KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.getKeySetManagerService(); return new KeySet(ksms.getSigningKeySetByPackageNameLPr(packageName)); } } @@ -24406,7 +24485,7 @@ public class PackageManagerService extends IPackageManager.Stub } IBinder ksh = ks.getToken(); if (ksh instanceof KeySetHandle) { - final KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.getKeySetManagerService(); return ksms.packageIsSignedByLPr(packageName, (KeySetHandle) ksh); } return false; @@ -24432,7 +24511,7 @@ public class PackageManagerService extends IPackageManager.Stub } IBinder ksh = ks.getToken(); if (ksh instanceof KeySetHandle) { - final KeySetManagerService ksms = mSettings.mKeySetManagerService; + final KeySetManagerService ksms = mSettings.getKeySetManagerService(); return ksms.packageIsSignedByExactlyLPr(packageName, (KeySetHandle) ksh); } return false; @@ -24441,7 +24520,7 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mLock") private void deletePackageIfUnusedLPr(final String packageName) { - PackageSetting ps = mSettings.mPackages.get(packageName); + PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps == null) { return; } @@ -24777,7 +24856,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean isPlatformSigned(String packageName) { - PackageSetting packageSetting = mSettings.mPackages.get(packageName); + PackageSetting packageSetting = mSettings.getPackageLPr(packageName); if (packageSetting == null) { return false; } @@ -24977,7 +25056,7 @@ public class PackageManagerService extends IPackageManager.Stub private String[] getKnownPackageNamesInternal(int knownPackage, int userId) { switch (knownPackage) { case PackageManagerInternal.PACKAGE_BROWSER: - return new String[]{mPermissionManager.getDefaultBrowser(userId)}; + return new String[] { mDefaultAppProvider.getDefaultBrowser(userId) }; case PackageManagerInternal.PACKAGE_INSTALLER: return filterOnlySystemPackages(mRequiredInstallerPackage); case PackageManagerInternal.PACKAGE_SETUP_WIZARD: @@ -25066,7 +25145,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public long getCeDataInode(String packageName, int userId) { synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null) { return ps.getCeDataInode(userId); } @@ -25077,7 +25156,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public Bundle getSuspendedPackageLauncherExtras(String packageName, int userId) { synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); final Bundle allExtras = new Bundle(); if (ps != null) { final PackageUserState pus = ps.readUserState(userId); @@ -25099,7 +25178,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean isPackageSuspended(String packageName, int userId) { synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); return (ps != null) ? ps.getSuspended(userId) : false; } } @@ -25144,7 +25223,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public String getSuspendingPackage(String suspendedPackage, int userId) { synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(suspendedPackage); + final PackageSetting ps = mSettings.getPackageLPr(suspendedPackage); if (ps != null) { final PackageUserState pus = ps.readUserState(userId); if (pus.suspended) { @@ -25166,7 +25245,7 @@ public class PackageManagerService extends IPackageManager.Stub public SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, String suspendingPackage, int userId) { synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(suspendedPackage); + final PackageSetting ps = mSettings.getPackageLPr(suspendedPackage); if (ps != null) { final PackageUserState pus = ps.readUserState(userId); if (pus.suspended) { @@ -25182,7 +25261,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public int getDistractingPackageRestrictions(String packageName, int userId) { synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); return (ps != null) ? ps.getDistractionFlags(userId) : RESTRICTION_NONE; } } @@ -25278,7 +25357,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean isPackageEphemeral(int userId, String packageName) { synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); return ps != null ? ps.getInstantApp(userId) : false; } } @@ -25469,7 +25548,7 @@ public class PackageManagerService extends IPackageManager.Stub continue; } for (VersionedPackage dependent : dependents) { - final PackageSetting ps = mSettings.mPackages.get( + final PackageSetting ps = mSettings.getPackageLPr( dependent.getPackageName()); if (ps == null) { continue; @@ -25480,7 +25559,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - final PackageSetting ps = mSettings.mPackages.get(targetPackageName); + final PackageSetting ps = mSettings.getPackageLPr(targetPackageName); ps.setOverlayPaths(overlayPaths, userId); outUpdatedPackageNames.add(targetPackageName); @@ -25550,7 +25629,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean canAccessComponent(int callingUid, ComponentName component, int userId) { synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); + final PackageSetting ps = mSettings.getPackageLPr(component.getPackageName()); return ps != null && !PackageManagerService.this.shouldFilterApplicationLocked( ps, callingUid, component, TYPE_UNKNOWN, userId); } @@ -25616,8 +25695,8 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void forEachPackageSetting(Consumer<PackageSetting> actionLocked) { synchronized (mLock) { - for (int index = 0; index < mSettings.mPackages.size(); index++) { - actionLocked.accept(mSettings.mPackages.valueAt(index)); + for (int index = 0; index < mSettings.getPackagesLocked().size(); index++) { + actionLocked.accept(mSettings.getPackagesLocked().valueAt(index)); } } } @@ -25793,7 +25872,7 @@ public class PackageManagerService extends IPackageManager.Stub return false; } final PackageSetting installerPackageSetting = - mSettings.mPackages.get(packageSetting.installSource.installerPackageName); + mSettings.getPackageLPr(packageSetting.installSource.installerPackageName); return installerPackageSetting != null && UserHandle.isSameApp(installerPackageSetting.appId, callingUid); } @@ -25894,7 +25973,7 @@ public class PackageManagerService extends IPackageManager.Stub PackageManagerInternal.InstalledLoadingProgressCallback callback) { final PackageSetting ps; synchronized (mLock) { - ps = mSettings.mPackages.get(packageName); + ps = mSettings.getPackageLPr(packageName); if (ps == null) { Slog.w(TAG, "Failed unregistering loading progress callback. Package " + packageName + " is not installed"); @@ -25957,7 +26036,7 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mLock") @NonNull private String[] getSharedUserPackagesForPackageLocked(String packageName, int userId) { - final PackageSetting packageSetting = mSettings.mPackages.get(packageName); + final PackageSetting packageSetting = mSettings.getPackageLPr(packageName); if (packageSetting == null || !packageSetting.isSharedUser()) { return EmptyArray.STRING; } @@ -26034,7 +26113,7 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { packageName = resolveInternalPackageNameInternalLocked( packageName, PackageManager.VERSION_CODE_HIGHEST, callingUid); - return mSettings.mPackages.get(packageName); + return mSettings.getPackageLPr(packageName); } } @@ -26125,7 +26204,7 @@ public class PackageManagerService extends IPackageManager.Stub enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "get install reason"); synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting ps = mSettings.getPackageLPr(packageName); if (shouldFilterApplicationLocked(ps, callingUid, userId)) { return PackageManager.INSTALL_REASON_UNKNOWN; } @@ -26298,7 +26377,7 @@ public class PackageManagerService extends IPackageManager.Stub long currentTimeInMillis = System.currentTimeMillis(); synchronized (mLock) { for (AndroidPackage pkg : mPackages.values()) { - PackageSetting ps = mSettings.mPackages.get(pkg.getPackageName()); + PackageSetting ps = mSettings.getPackageLPr(pkg.getPackageName()); if (ps == null) { continue; } @@ -26410,7 +26489,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void setMimeGroup(String packageName, String mimeGroup, List<String> mimeTypes) { - boolean changed = mSettings.mPackages.get(packageName) + boolean changed = mSettings.getPackageLPr(packageName) .setMimeGroup(mimeGroup, mimeTypes); if (changed) { @@ -26420,7 +26499,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public List<String> getMimeGroup(String packageName, String mimeGroup) { - return mSettings.mPackages.get(packageName).getMimeGroup(mimeGroup); + return mSettings.getPackageLPr(packageName).getMimeGroup(mimeGroup); } /** diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index f47b4b46fdd4..cb4c6a9fd564 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -475,6 +475,14 @@ public final class Settings { return mPackages.get(pkgName); } + ArrayMap<String, PackageSetting> getPackagesLocked() { + return mPackages; + } + + KeySetManagerService getKeySetManagerService() { + return mKeySetManagerService; + } + String getRenamedPackageLPr(String pkgName) { return mRenamedPackages.get(pkgName); } @@ -1969,28 +1977,28 @@ public final class Settings { serializer.attributeLong(null, ATTR_CE_DATA_INODE, ustate.ceDataInode); } if (!ustate.installed) { - serializer.attribute(null, ATTR_INSTALLED, "false"); + serializer.attributeBoolean(null, ATTR_INSTALLED, false); } if (ustate.stopped) { - serializer.attribute(null, ATTR_STOPPED, "true"); + serializer.attributeBoolean(null, ATTR_STOPPED, true); } if (ustate.notLaunched) { - serializer.attribute(null, ATTR_NOT_LAUNCHED, "true"); + serializer.attributeBoolean(null, ATTR_NOT_LAUNCHED, true); } if (ustate.hidden) { - serializer.attribute(null, ATTR_HIDDEN, "true"); + serializer.attributeBoolean(null, ATTR_HIDDEN, true); } if (ustate.distractionFlags != 0) { serializer.attributeInt(null, ATTR_DISTRACTION_FLAGS, ustate.distractionFlags); } if (ustate.suspended) { - serializer.attribute(null, ATTR_SUSPENDED, "true"); + serializer.attributeBoolean(null, ATTR_SUSPENDED, true); } if (ustate.instantApp) { - serializer.attribute(null, ATTR_INSTANT_APP, "true"); + serializer.attributeBoolean(null, ATTR_INSTANT_APP, true); } if (ustate.virtualPreload) { - serializer.attribute(null, ATTR_VIRTUAL_PRELOAD, "true"); + serializer.attributeBoolean(null, ATTR_VIRTUAL_PRELOAD, true); } if (ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT) { serializer.attributeInt(null, ATTR_ENABLED, ustate.enabled); @@ -2731,7 +2739,7 @@ public final class Settings { serializer.attributeInt(null, "sharedUserId", pkg.appId); } if (pkg.uidError) { - serializer.attribute(null, "uidError", "true"); + serializer.attributeBoolean(null, "uidError", true); } InstallSource installSource = pkg.installSource; if (installSource.installerPackageName != null) { @@ -2742,13 +2750,13 @@ public final class Settings { installSource.installerAttributionTag); } if (installSource.isOrphaned) { - serializer.attribute(null, "isOrphaned", "true"); + serializer.attributeBoolean(null, "isOrphaned", true); } if (installSource.initiatingPackageName != null) { serializer.attribute(null, "installInitiator", installSource.initiatingPackageName); } if (installSource.isInitiatingPackageUninstalled) { - serializer.attribute(null, "installInitiatorUninstalled", "true"); + serializer.attributeBoolean(null, "installInitiatorUninstalled", true); } if (installSource.originatingPackageName != null) { serializer.attribute(null, "installOriginator", installSource.originatingPackageName); @@ -2760,16 +2768,16 @@ public final class Settings { serializer.attributeInt(null, "categoryHint", pkg.categoryHint); } if (pkg.updateAvailable) { - serializer.attribute(null, "updateAvailable", "true"); + serializer.attributeBoolean(null, "updateAvailable", true); } if (pkg.forceQueryableOverride) { - serializer.attribute(null, "forceQueryable", "true"); + serializer.attributeBoolean(null, "forceQueryable", true); } if (pkg.isPackageStartable()) { - serializer.attribute(null, "isStartable", "true"); + serializer.attributeBoolean(null, "isStartable", true); } if (pkg.isPackageLoading()) { - serializer.attribute(null, "isLoading", "true"); + serializer.attributeBoolean(null, "isLoading", true); } writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions); @@ -3500,14 +3508,14 @@ public final class Settings { String systemStr = null; String installerPackageName = null; String installerAttributionTag = null; - String isOrphaned = null; + boolean isOrphaned = false; String installOriginatingPackageName = null; String installInitiatingPackageName = null; - String installInitiatorUninstalled = null; + boolean installInitiatorUninstalled = false; String volumeUuid = null; - String updateAvailable = null; + boolean updateAvailable = false; int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED; - String uidError = null; + boolean uidError = false; int pkgFlags = 0; int pkgPrivateFlags = 0; long timeStamp = 0; @@ -3515,14 +3523,14 @@ public final class Settings { long lastUpdateTime = 0; PackageSetting packageSetting = null; long versionCode = 0; - String installedForceQueryable = null; - String isStartable = null; - String isLoading = null; + boolean installedForceQueryable = false; + boolean isStartable = false; + boolean isLoading = false; try { name = parser.getAttributeValue(null, ATTR_NAME); realName = parser.getAttributeValue(null, "realName"); userId = parser.getAttributeInt(null, "userId", 0); - uidError = parser.getAttributeValue(null, "uidError"); + uidError = parser.getAttributeBoolean(null, "uidError", false); sharedUserId = parser.getAttributeInt(null, "sharedUserId", 0); codePathStr = parser.getAttributeValue(null, "codePath"); @@ -3532,10 +3540,10 @@ public final class Settings { primaryCpuAbiString = parser.getAttributeValue(null, "primaryCpuAbi"); secondaryCpuAbiString = parser.getAttributeValue(null, "secondaryCpuAbi"); cpuAbiOverrideString = parser.getAttributeValue(null, "cpuAbiOverride"); - updateAvailable = parser.getAttributeValue(null, "updateAvailable"); - installedForceQueryable = parser.getAttributeValue(null, "forceQueryable"); - isStartable = parser.getAttributeValue(null, "isStartable"); - isLoading = parser.getAttributeValue(null, "isLoading"); + updateAvailable = parser.getAttributeBoolean(null, "updateAvailable", false); + installedForceQueryable = parser.getAttributeBoolean(null, "forceQueryable", false); + isStartable = parser.getAttributeBoolean(null, "isStartable", false); + isLoading = parser.getAttributeBoolean(null, "isLoading", false); if (primaryCpuAbiString == null && legacyCpuAbiString != null) { primaryCpuAbiString = legacyCpuAbiString; @@ -3544,11 +3552,11 @@ public final class Settings { versionCode = parser.getAttributeLong(null, "version", 0); installerPackageName = parser.getAttributeValue(null, "installer"); installerAttributionTag = parser.getAttributeValue(null, "installerAttributionTag"); - isOrphaned = parser.getAttributeValue(null, "isOrphaned"); + isOrphaned = parser.getAttributeBoolean(null, "isOrphaned", false); installInitiatingPackageName = parser.getAttributeValue(null, "installInitiator"); installOriginatingPackageName = parser.getAttributeValue(null, "installOriginator"); - installInitiatorUninstalled = parser.getAttributeValue(null, - "installInitiatorUninstalled"); + installInitiatorUninstalled = parser.getAttributeBoolean(null, + "installInitiatorUninstalled", false); volumeUuid = parser.getAttributeValue(null, "volumeUuid"); categoryHint = parser.getAttributeInt(null, "categoryHint", ApplicationInfo.CATEGORY_UNDEFINED); @@ -3670,21 +3678,20 @@ public final class Settings { + userId + " at " + parser.getPositionDescription()); } if (packageSetting != null) { - packageSetting.uidError = "true".equals(uidError); + packageSetting.uidError = uidError; InstallSource installSource = InstallSource.create( installInitiatingPackageName, installOriginatingPackageName, - installerPackageName, installerAttributionTag, "true".equals(isOrphaned), - "true".equals(installInitiatorUninstalled)); + installerPackageName, installerAttributionTag, isOrphaned, + installInitiatorUninstalled); packageSetting.installSource = installSource; packageSetting.volumeUuid = volumeUuid; packageSetting.categoryHint = categoryHint; packageSetting.legacyNativeLibraryPathString = legacyNativeLibraryPathStr; packageSetting.primaryCpuAbiString = primaryCpuAbiString; packageSetting.secondaryCpuAbiString = secondaryCpuAbiString; - packageSetting.updateAvailable = "true".equals(updateAvailable); - packageSetting.forceQueryableOverride = "true".equals(installedForceQueryable); - packageSetting.incrementalStates = new IncrementalStates("true".equals(isStartable), - "true".equals(isLoading)); + packageSetting.updateAvailable = updateAvailable; + packageSetting.forceQueryableOverride = installedForceQueryable; + packageSetting.incrementalStates = new IncrementalStates(isStartable, isLoading); // Handle legacy string here for single-user mode final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED); if (enabledStr != null) { @@ -3914,7 +3921,7 @@ public final class Settings { { name = parser.getAttributeValue(null, ATTR_NAME); int userId = parser.getAttributeInt(null, "userId", 0); - if ("true".equals(parser.getAttributeValue(null, "system"))) { + if (parser.getAttributeBoolean(null, "system", false)) { pkgFlags |= ApplicationInfo.FLAG_SYSTEM; } if (name == null) { @@ -5554,7 +5561,8 @@ public final class Settings { return mPreferredActivities.get(userId); } - CrossProfileIntentResolver getCrossProfileIntentResolvers(int userId) { + @Nullable + CrossProfileIntentResolver getCrossProfileIntentResolver(int userId) { return mCrossProfileIntentResolvers.get(userId); } diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING index 7d48d0a6e266..e913829ea9e2 100644 --- a/services/core/java/com/android/server/pm/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/TEST_MAPPING @@ -30,6 +30,14 @@ ] }, { + "name": "FrameworksMockingServicesTests", + "options": [ + { + "include-filter": "com.android.server.pm." + } + ] + }, + { "name": "CtsContentTestCases", "options": [ { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index cc814bcc7760..62ac57062ac6 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -2918,16 +2918,16 @@ public class UserManagerService extends IUserManager.Stub { serializer.attribute(null, ATTR_ICON_PATH, userInfo.iconPath); } if (userInfo.partial) { - serializer.attribute(null, ATTR_PARTIAL, "true"); + serializer.attributeBoolean(null, ATTR_PARTIAL, true); } if (userInfo.preCreated) { - serializer.attribute(null, ATTR_PRE_CREATED, "true"); + serializer.attributeBoolean(null, ATTR_PRE_CREATED, true); } if (userInfo.convertedFromPreCreated) { - serializer.attribute(null, ATTR_CONVERTED_FROM_PRE_CREATED, "true"); + serializer.attributeBoolean(null, ATTR_CONVERTED_FROM_PRE_CREATED, true); } if (userInfo.guestToRemove) { - serializer.attribute(null, ATTR_GUEST_TO_REMOVE, "true"); + serializer.attributeBoolean(null, ATTR_GUEST_TO_REMOVE, true); } if (userInfo.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID) { serializer.attributeInt(null, ATTR_PROFILE_GROUP_ID, userInfo.profileGroupId); @@ -3121,22 +3121,10 @@ public class UserManagerService extends IUserManager.Stub { profileBadge = parser.getAttributeInt(null, ATTR_PROFILE_BADGE, 0); restrictedProfileParentId = parser.getAttributeInt(null, ATTR_RESTRICTED_PROFILE_PARENT_ID, UserInfo.NO_PROFILE_GROUP_ID); - String valueString = parser.getAttributeValue(null, ATTR_PARTIAL); - if ("true".equals(valueString)) { - partial = true; - } - valueString = parser.getAttributeValue(null, ATTR_PRE_CREATED); - if ("true".equals(valueString)) { - preCreated = true; - } - valueString = parser.getAttributeValue(null, ATTR_CONVERTED_FROM_PRE_CREATED); - if ("true".equals(valueString)) { - converted = true; - } - valueString = parser.getAttributeValue(null, ATTR_GUEST_TO_REMOVE); - if ("true".equals(valueString)) { - guestToRemove = true; - } + partial = parser.getAttributeBoolean(null, ATTR_PARTIAL, false); + preCreated = parser.getAttributeBoolean(null, ATTR_PRE_CREATED, false); + converted = parser.getAttributeBoolean(null, ATTR_CONVERTED_FROM_PRE_CREATED, false); + guestToRemove = parser.getAttributeBoolean(null, ATTR_GUEST_TO_REMOVE, false); seedAccountName = parser.getAttributeValue(null, ATTR_SEED_ACCOUNT_NAME); seedAccountType = parser.getAttributeValue(null, ATTR_SEED_ACCOUNT_TYPE); @@ -4257,10 +4245,9 @@ public class UserManagerService extends IUserManager.Stub { if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_ENTRY)) { String key = parser.getAttributeValue(null, ATTR_KEY); String valType = parser.getAttributeValue(null, ATTR_VALUE_TYPE); - String multiple = parser.getAttributeValue(null, ATTR_MULTIPLE); - if (multiple != null) { + int count = parser.getAttributeInt(null, ATTR_MULTIPLE, -1); + if (count != -1) { values.clear(); - int count = Integer.parseInt(multiple); while (count > 0 && (type = parser.next()) != XmlPullParser.END_DOCUMENT) { if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_VALUE)) { diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index 0ac3030ba5dc..51dff4063850 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -344,7 +344,7 @@ public class UserRestrictionsUtils { } if (USER_RESTRICTIONS.contains(key)) { if (restrictions.getBoolean(key)) { - serializer.attribute(null, key, "true"); + serializer.attributeBoolean(null, key, true); } continue; } @@ -360,9 +360,9 @@ public class UserRestrictionsUtils { public static void readRestrictions(TypedXmlPullParser parser, Bundle restrictions) { restrictions.clear(); for (String key : USER_RESTRICTIONS) { - final String value = parser.getAttributeValue(null, key); - if (value != null) { - restrictions.putBoolean(key, Boolean.parseBoolean(value)); + final boolean value = parser.getAttributeBoolean(null, key, false); + if (value) { + restrictions.putBoolean(key, true); } } } diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermission.java b/services/core/java/com/android/server/pm/permission/LegacyPermission.java index 6a4eb63c8d7e..ca3a2e2e2da7 100644 --- a/services/core/java/com/android/server/pm/permission/LegacyPermission.java +++ b/services/core/java/com/android/server/pm/permission/LegacyPermission.java @@ -167,16 +167,12 @@ public final class LegacyPermission { private static int readInt(@NonNull TypedXmlPullParser parser, @Nullable String namespace, @NonNull String name, int defaultValue) { - final String value = parser.getAttributeValue(namespace, name); - if (value == null) { - return defaultValue; - } try { - return Integer.parseInt(value); - } catch (NumberFormatException e) { + return parser.getAttributeInt(namespace, name); + } catch (Exception ignored) { PackageManagerService.reportSettingsProblem(Log.WARN, "Error in package manager settings: attribute " + name - + " has bad integer value " + value + " at " + + " has bad integer value at " + parser.getPositionDescription()); return defaultValue; } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 48e18f1b8b38..7ed887d9680a 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -114,7 +114,6 @@ import android.permission.IPermissionManager; import android.permission.PermissionControllerManager; import android.permission.PermissionManager; import android.permission.PermissionManagerInternal; -import android.permission.PermissionManagerInternal.CheckPermissionDelegate; import android.permission.PermissionManagerInternal.OnRuntimePermissionStateChangedListener; import android.text.TextUtils; import android.util.ArrayMap; @@ -138,6 +137,7 @@ import com.android.internal.util.CollectionUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.IntPair; import com.android.internal.util.Preconditions; +import com.android.internal.util.function.TriFunction; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.FgThread; import com.android.server.LocalServices; @@ -151,9 +151,6 @@ import com.android.server.pm.UserManagerInternal; import com.android.server.pm.UserManagerService; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; -import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultBrowserProvider; -import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultDialerProvider; -import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultHomeProvider; import com.android.server.policy.PermissionPolicyInternal; import com.android.server.policy.SoftRestrictedPermissionPolicy; @@ -176,7 +173,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.function.Consumer; +import java.util.function.BiFunction; /** * Manages all permissions and handles permissions related tasks. @@ -304,15 +301,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { @NonNull private final OnPermissionChangeListeners mOnPermissionChangeListeners; - @GuardedBy("mLock") - private DefaultBrowserProvider mDefaultBrowserProvider; - - @GuardedBy("mLock") - private DefaultDialerProvider mDefaultDialerProvider; - - @GuardedBy("mLock") - private DefaultHomeProvider mDefaultHomeProvider; - // TODO: Take a look at the methods defined in the callback. // The callback was initially created to support the split between permission // manager and the package manager. However, it's started to be used for other @@ -2018,60 +2006,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override - public String getDefaultBrowser(int userId) { - final int callingUid = Binder.getCallingUid(); - if (UserHandle.getUserId(callingUid) != userId) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); - } - if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { - return null; - } - DefaultBrowserProvider provider; - synchronized (mLock) { - provider = mDefaultBrowserProvider; - } - return provider != null ? provider.getDefaultBrowser(userId) : null; - } - - @Override - public boolean setDefaultBrowser(String packageName, int userId) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); - if (UserHandle.getCallingUserId() != userId) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); - } - return setDefaultBrowserInternal(packageName, false, true, userId); - } - - private boolean setDefaultBrowserInternal(String packageName, boolean async, - boolean doGrant, int userId) { - if (userId == UserHandle.USER_ALL) { - return false; - } - DefaultBrowserProvider provider; - synchronized (mLock) { - provider = mDefaultBrowserProvider; - } - if (provider == null) { - return false; - } - if (async) { - provider.setDefaultBrowserAsync(packageName, userId); - } else { - if (!provider.setDefaultBrowser(packageName, userId)) { - return false; - } - } - if (doGrant && packageName != null) { - mDefaultPermissionGrantPolicy.grantDefaultPermissionsToDefaultBrowser(packageName, - userId); - } - return true; - } - - @Override public void grantDefaultPermissionsToEnabledCarrierApps(String[] packageNames, int userId) { final int callingUid = Binder.getCallingUid(); PackageManagerServiceUtils @@ -2360,6 +2294,32 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } + private void startShellPermissionIdentityDelegationInternal(int uid, + @NonNull String packageName, @Nullable List<String> permissionNames) { + synchronized (mLock) { + final CheckPermissionDelegate oldDelegate = mCheckPermissionDelegate; + if (oldDelegate != null && oldDelegate.getDelegatedUid() != uid) { + throw new SecurityException( + "Shell can delegate permissions only to one UID at a time"); + } + final ShellDelegate delegate = new ShellDelegate(uid, packageName, permissionNames); + setCheckPermissionDelegateLocked(delegate); + } + } + + private void stopShellPermissionIdentityDelegationInternal() { + synchronized (mLock) { + setCheckPermissionDelegateLocked(null); + } + } + + private void setCheckPermissionDelegateLocked(@Nullable CheckPermissionDelegate delegate) { + if (delegate != null || mCheckPermissionDelegate != null) { + PackageManager.invalidatePackageInfoCache(); + } + mCheckPermissionDelegate = delegate; + } + /** * If the app is updated, and has scoped storage permissions, then it is possible that the * app updated in an attempt to get unscoped storage. If so, revoke all storage permissions. @@ -5233,62 +5193,15 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override - public CheckPermissionDelegate getCheckPermissionDelegate() { - synchronized (mLock) { - return mCheckPermissionDelegate; - } - } - - @Override - public void setCheckPermissionDelegate(CheckPermissionDelegate delegate) { - synchronized (mLock) { - if (delegate != null || mCheckPermissionDelegate != null) { - PackageManager.invalidatePackageInfoCache(); - } - mCheckPermissionDelegate = delegate; - } - } - - @Override - public void setDefaultBrowserProvider(@NonNull DefaultBrowserProvider provider) { - synchronized (mLock) { - mDefaultBrowserProvider = provider; - } - } - - @Override - public void setDefaultBrowser(String packageName, boolean async, boolean doGrant, - int userId) { - setDefaultBrowserInternal(packageName, async, doGrant, userId); - } - - @Override - public void setDefaultDialerProvider(@NonNull DefaultDialerProvider provider) { - synchronized (mLock) { - mDefaultDialerProvider = provider; - } - } - - @Override - public void setDefaultHomeProvider(@NonNull DefaultHomeProvider provider) { - synchronized (mLock) { - mDefaultHomeProvider = provider; - } + public void startShellPermissionIdentityDelegation(int uid, @NonNull String packageName, + @Nullable List<String> permissionNames) { + Objects.requireNonNull(packageName, "packageName"); + startShellPermissionIdentityDelegationInternal(uid, packageName, permissionNames); } @Override - public void setDefaultHome(String packageName, int userId, Consumer<Boolean> callback) { - if (userId == UserHandle.USER_ALL) { - return; - } - DefaultHomeProvider provider; - synchronized (mLock) { - provider = mDefaultHomeProvider; - } - if (provider == null) { - return; - } - provider.setDefaultHomeAsync(packageName, userId, callback); + public void stopShellPermissionIdentityDelegation() { + stopShellPermissionIdentityDelegationInternal(); } @Override @@ -5332,30 +5245,10 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override - public String getDefaultBrowser(int userId) { - DefaultBrowserProvider provider; - synchronized (mLock) { - provider = mDefaultBrowserProvider; - } - return provider != null ? provider.getDefaultBrowser(userId) : null; - } - - @Override - public String getDefaultDialer(int userId) { - DefaultDialerProvider provider; - synchronized (mLock) { - provider = mDefaultDialerProvider; - } - return provider != null ? provider.getDefaultDialer(userId) : null; - } - - @Override - public String getDefaultHome(int userId) { - DefaultHomeProvider provider; - synchronized (mLock) { - provider = mDefaultHomeProvider; - } - return provider != null ? provider.getDefaultHome(userId) : null; + public void grantDefaultPermissionsToDefaultBrowser(@NonNull String packageName, + @UserIdInt int userId) { + mDefaultPermissionGrantPolicy.grantDefaultPermissionsToDefaultBrowser(packageName, + userId); } @Override @@ -5545,6 +5438,102 @@ public class PermissionManagerService extends IPermissionManager.Stub { } /** + * Interface to intercept permission checks and optionally pass through to the original + * implementation. + */ + private interface CheckPermissionDelegate { + /** + * Get the UID whose permission checks is being delegated. + * + * @return the UID + */ + int getDelegatedUid(); + + /** + * Check whether the given package has been granted the specified permission. + * + * @param permissionName the name of the permission to be checked + * @param packageName the name of the package to be checked + * @param userId the user ID + * @param superImpl the original implementation that can be delegated to + * @return {@link android.content.pm.PackageManager.PERMISSION_GRANTED} if the package has + * the permission, or {@link android.content.pm.PackageManager.PERMISSION_DENITED} otherwise + * + * @see android.content.pm.PackageManager#checkPermission(String, String) + */ + int checkPermission(@NonNull String permissionName, @NonNull String packageName, + @UserIdInt int userId, + @NonNull TriFunction<String, String, Integer, Integer> superImpl); + + /** + * Check whether the given UID has been granted the specified permission. + * + * @param permissionName the name of the permission to be checked + * @param uid the UID to be checked + * @param superImpl the original implementation that can be delegated to + * @return {@link android.content.pm.PackageManager.PERMISSION_GRANTED} if the package has + * the permission, or {@link android.content.pm.PackageManager.PERMISSION_DENITED} otherwise + */ + int checkUidPermission(@NonNull String permissionName, int uid, + BiFunction<String, Integer, Integer> superImpl); + } + + private class ShellDelegate implements CheckPermissionDelegate { + private final int mDelegatedUid; + @NonNull + private final String mDelegatedPackageName; + @Nullable + private final List<String> mDelegatedPermissionNames; + + public ShellDelegate(int delegatedUid, @NonNull String delegatedPackageName, + @Nullable List<String> delegatedPermissionNames) { + mDelegatedUid = delegatedUid; + mDelegatedPackageName = delegatedPackageName; + mDelegatedPermissionNames = delegatedPermissionNames; + } + + @Override + public int getDelegatedUid() { + return mDelegatedUid; + } + + @Override + public int checkPermission(@NonNull String permissionName, @NonNull String packageName, + int userId, @NonNull TriFunction<String, String, Integer, Integer> superImpl) { + if (mDelegatedPackageName.equals(packageName) + && isDelegatedPermission(permissionName)) { + final long identity = Binder.clearCallingIdentity(); + try { + return superImpl.apply(permissionName, "com.android.shell", userId); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + return superImpl.apply(permissionName, packageName, userId); + } + + @Override + public int checkUidPermission(@NonNull String permissionName, int uid, + @NonNull BiFunction<String, Integer, Integer> superImpl) { + if (uid == mDelegatedUid && isDelegatedPermission(permissionName)) { + final long identity = Binder.clearCallingIdentity(); + try { + return superImpl.apply(permissionName, Process.SHELL_UID); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + return superImpl.apply(permissionName, uid); + } + + private boolean isDelegatedPermission(@NonNull String permissionName) { + // null permissions means all permissions are targeted + return mDelegatedPermissionNames == null + || mDelegatedPermissionNames.contains(permissionName); + } + } + + /** * Allows injection of services and method responses to facilitate testing. * * <p>Test classes can create a mock of this class and pass it to the PermissionManagerService diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index f924651f1051..e006fa784cce 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -30,7 +30,6 @@ import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Set; -import java.util.function.Consumer; /** * Internal interfaces services. @@ -66,82 +65,6 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager String[] getPackages(String authority, int userId); } - /** - * 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); - - /** - * Set the package name of the default browser asynchronously. - * - * @param packageName package name of the default browser, or {@code null} to remove - * @param userId the user id - */ - void setDefaultBrowserAsync(@Nullable String packageName, @UserIdInt int userId); - } - - /** - * Provider for default dialer - */ - public interface DefaultDialerProvider { - - /** - * Get the package name of the default dialer. - * - * @param userId the user id - * - * @return the package name of the default dialer, or {@code null} if none - */ - @Nullable - String getDefaultDialer(@UserIdInt int userId); - } - - /** - * Provider for default home - */ - public interface DefaultHomeProvider { - - /** - * Get the package name of the default home. - * - * @param userId the user id - * - * @return the package name of the default home, or {@code null} if none - */ - @Nullable - String getDefaultHome(@UserIdInt int userId); - - /** - * Set the package name of the default home. - * - * @param packageName package name of the default home, or {@code null} to remove - * @param userId the user id - * @param callback the callback made after the default home as been updated - */ - void setDefaultHomeAsync(@Nullable String packageName, @UserIdInt int userId, - @NonNull Consumer<Boolean> callback); - } - public abstract void systemReady(); /** @@ -257,18 +180,24 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager @PermissionInfo.ProtectionFlags int protectionFlags); /** - * Returns the delegate used to influence permission checking. + * Start delegate the permission identity of the shell UID to the given UID. * - * @return The delegate instance. + * @param uid the UID to delegate shell permission identity to + * @param packageName the name of the package to delegate shell permission identity to + * @param permissionNames the names of the permissions to delegate shell permission identity + * for, or {@code null} for all permissions */ - public abstract @Nullable CheckPermissionDelegate getCheckPermissionDelegate(); + //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) + public abstract void startShellPermissionIdentityDelegation(int uid, + @NonNull String packageName, @Nullable List<String> permissionNames); /** - * Sets the delegate used to influence permission checking. + * Stop delegating the permission identity of the shell UID. * - * @param delegate A delegate instance or {@code null} to clear. + * @see #startShellPermissionIdentityDelegation(int, String, List) */ - public abstract void setCheckPermissionDelegate(@Nullable CheckPermissionDelegate delegate); + //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) + public abstract void stopShellPermissionIdentityDelegation(); /** * Sets the dialer application packages provider. @@ -319,69 +248,12 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager public abstract void setVoiceInteractionPackagesProvider(PackagesProvider provider); /** - * Sets the default browser provider. - * - * @param provider the provider - */ - public abstract void setDefaultBrowserProvider(@NonNull DefaultBrowserProvider provider); - - /** - * Sets the package name of the default browser provider for the given user. - * - * @param packageName The package name of the default browser or {@code null} - * to clear the default browser - * @param async If {@code true}, set the default browser asynchronously, - * otherwise set it synchronously - * @param doGrant If {@code true} and if {@code packageName} is not {@code null}, - * perform default permission grants on the browser, otherwise skip the - * default permission grants. - * @param userId The user to set the default browser for. - */ - public abstract void setDefaultBrowser(@Nullable String packageName, boolean async, - boolean doGrant, @UserIdInt int userId); - - /** - * Sets the default dialer provider. - * - * @param provider the provider - */ - public abstract void setDefaultDialerProvider(@NonNull DefaultDialerProvider provider); - - /** - * Sets the default home provider. - * - * @param provider the provider - */ - public abstract void setDefaultHomeProvider(@NonNull DefaultHomeProvider provider); - - /** - * Asynchronously sets the package name of the default home provider for the given user. - * - * @param packageName The package name of the default home or {@code null} - * to clear the default browser - * @param userId The user to set the default browser for - * @param callback Invoked after the default home has been set - */ - public abstract void setDefaultHome(@Nullable String packageName, @UserIdInt int userId, - @NonNull Consumer<Boolean> callback); - - /** - * Returns the default browser package name for the given user. - */ - @Nullable - public abstract String getDefaultBrowser(@UserIdInt int userId); - - /** - * Returns the default dialer package name for the given user. - */ - @Nullable - public abstract String getDefaultDialer(@UserIdInt int userId); - - /** - * Returns the default home package name for the given user. + * Requests granting of the default permissions to the current default browser. + * @param packageName The default browser package name. + * @param userId The user for which to grant the permissions. */ - @Nullable - public abstract String getDefaultHome(@UserIdInt int userId); + public abstract void grantDefaultPermissionsToDefaultBrowser( + @NonNull String packageName, @UserIdInt int userId); /** * Requests granting of the default permissions to the current default Use Open Wifi app. diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java index 321bb8c0251d..cc1f8d620d1f 100644 --- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java +++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java @@ -158,7 +158,6 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, // switch there is no need to register for a callback. boolean shouldListenToLidSwitch = false; - final SensorManager sensorManager = mContext.getSystemService(SensorManager.class); // The set of Sensor(s) that this instance should register to receive SensorEvent(s) from. final ArraySet<Sensor> sensorsToListenTo = new ArraySet<>(); @@ -182,19 +181,10 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, List<SensorCondition> sensorConditions = conditions.getSensor(); for (int j = 0; j < sensorConditions.size(); j++) { SensorCondition sensorCondition = sensorConditions.get(j); - final int expectedSensorType = sensorCondition.getType().intValue(); + final String expectedSensorType = sensorCondition.getType(); final String expectedSensorName = sensorCondition.getName(); - List<Sensor> sensors = sensorManager.getSensorList(expectedSensorType); - Sensor foundSensor = null; - for (int sensorIndex = 0; sensorIndex < sensors.size(); sensorIndex++) { - Sensor sensor = sensors.get(sensorIndex); - if (sensor.getName().equals(expectedSensorName)) { - foundSensor = sensor; - break; - } - } - + final Sensor foundSensor = findSensor(expectedSensorType, expectedSensorName); if (foundSensor == null) { throw new IllegalStateException("Failed to find Sensor with type: " + expectedSensorType + " and name: " + expectedSensorName); @@ -221,12 +211,33 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, inputManager.registerLidSwitchCallback(this); } + final SensorManager sensorManager = mContext.getSystemService(SensorManager.class); for (int i = 0; i < sensorsToListenTo.size(); i++) { Sensor sensor = sensorsToListenTo.valueAt(i); sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST); } } + @Nullable + private Sensor findSensor(String type, String name) { + final SensorManager sensorManager = mContext.getSystemService(SensorManager.class); + final List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL); + for (int sensorIndex = 0; sensorIndex < sensors.size(); sensorIndex++) { + final Sensor sensor = sensors.get(sensorIndex); + final String sensorType = sensor.getStringType(); + final String sensorName = sensor.getName(); + + if (sensorType == null || sensorName == null) { + continue; + } + + if (sensorType.equals(type) && sensorName.equals(name)) { + return sensor; + } + } + return null; + } + @Override public void setListener(Listener listener) { synchronized (mLock) { diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index ab6ada2f85f7..3d91a8502097 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -83,7 +83,6 @@ import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.function.Consumer; /** * Service for role management. @@ -162,12 +161,6 @@ public class RoleManagerService extends SystemService implements RoleUserState.C LocalServices.addService(RoleManagerInternal.class, new Internal()); - PermissionManagerServiceInternal permissionManagerInternal = - LocalServices.getService(PermissionManagerServiceInternal.class); - permissionManagerInternal.setDefaultBrowserProvider(new DefaultBrowserProvider()); - permissionManagerInternal.setDefaultDialerProvider(new DefaultDialerProvider()); - permissionManagerInternal.setDefaultHomeProvider(new DefaultHomeProvider()); - registerUserRemovedReceiver(); } @@ -657,12 +650,84 @@ public class RoleManagerService extends SystemService implements RoleUserState.C resultReceiver); } + @Nullable @Override - public String getDefaultSmsPackage(int userId) { + public String getBrowserRoleHolder(@UserIdInt int userId) { + final int callingUid = Binder.getCallingUid(); + if (UserHandle.getUserId(callingUid) != userId) { + getContext().enforceCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); + } + final PackageManagerInternal packageManager = LocalServices.getService( + PackageManagerInternal.class); + if (packageManager.getInstantAppPackageName(callingUid) != null) { + return null; + } + final long identity = Binder.clearCallingIdentity(); try { - return CollectionUtils.firstOrNull( - getRoleHoldersAsUser(RoleManager.ROLE_SMS, userId)); + return CollectionUtils.firstOrNull(getRoleHoldersAsUser(RoleManager.ROLE_BROWSER, + userId)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public boolean setBrowserRoleHolder(@Nullable String packageName, @UserIdInt int userId) { + final Context context = getContext(); + context.enforceCallingOrSelfPermission( + android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); + if (UserHandle.getCallingUserId() != userId) { + context.enforceCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); + } + + if (!mUserManagerInternal.exists(userId)) { + return false; + } + + final AndroidFuture<Void> future = new AndroidFuture<>(); + final RemoteCallback callback = new RemoteCallback(result -> { + boolean successful = result != null; + if (successful) { + future.complete(null); + } else { + future.completeExceptionally(new RuntimeException()); + } + }); + final long identity = Binder.clearCallingIdentity(); + try { + if (packageName != null) { + addRoleHolderAsUser(RoleManager.ROLE_BROWSER, packageName, 0, userId, callback); + } else { + clearRoleHoldersAsUser(RoleManager.ROLE_BROWSER, 0, userId, callback); + } + try { + future.get(5, TimeUnit.SECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + Slog.e(LOG_TAG, "Exception while setting default browser: " + packageName, e); + return false; + } + + if (packageName != null) { + final PermissionManagerServiceInternal permissionManager = + LocalServices.getService(PermissionManagerServiceInternal.class); + permissionManager.grantDefaultPermissionsToDefaultBrowser(packageName, userId); + } + } finally { + Binder.restoreCallingIdentity(identity); + } + + return true; + } + + @Override + public String getSmsRoleHolder(int userId) { + final long identity = Binder.clearCallingIdentity(); + try { + return CollectionUtils.firstOrNull(getRoleHoldersAsUser(RoleManager.ROLE_SMS, + userId)); } finally { Binder.restoreCallingIdentity(identity); } @@ -718,100 +783,4 @@ public class RoleManagerService extends SystemService implements RoleUserState.C return getOrCreateUserState(userId).getRolesAndHolders(); } } - - private class DefaultBrowserProvider implements - PermissionManagerServiceInternal.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) { - AndroidFuture<Void> future = new AndroidFuture<>(); - RemoteCallback callback = new RemoteCallback(result -> { - boolean successful = result != null; - if (successful) { - future.complete(null); - } else { - future.completeExceptionally(new RuntimeException()); - } - }); - if (packageName != null) { - getOrCreateController(userId).onAddRoleHolder(RoleManager.ROLE_BROWSER, - packageName, 0, callback); - } else { - getOrCreateController(userId).onClearRoleHolders(RoleManager.ROLE_BROWSER, 0, - 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; - } - } - - @Override - public void setDefaultBrowserAsync(@Nullable String packageName, @UserIdInt int userId) { - RemoteCallback callback = new RemoteCallback(result -> { - boolean successful = result != null; - if (!successful) { - Slog.e(LOG_TAG, "Failed to set default browser: " + packageName); - } - }); - if (packageName != null) { - getOrCreateController(userId).onAddRoleHolder(RoleManager.ROLE_BROWSER, - packageName, 0, callback); - } else { - getOrCreateController(userId).onClearRoleHolders(RoleManager.ROLE_BROWSER, 0, - callback); - } - } - } - - private class DefaultDialerProvider implements - PermissionManagerServiceInternal.DefaultDialerProvider { - - @Nullable - @Override - public String getDefaultDialer(@UserIdInt int userId) { - return CollectionUtils.firstOrNull(getOrCreateUserState(userId).getRoleHolders( - RoleManager.ROLE_DIALER)); - } - } - - private class DefaultHomeProvider implements - PermissionManagerServiceInternal.DefaultHomeProvider { - - @Nullable - @Override - public String getDefaultHome(@UserIdInt int userId) { - return CollectionUtils.firstOrNull(getOrCreateUserState(userId).getRoleHolders( - RoleManager.ROLE_HOME)); - } - - @Override - public void setDefaultHomeAsync(@Nullable String packageName, @UserIdInt int userId, - @NonNull Consumer<Boolean> callback) { - RemoteCallback remoteCallback = new RemoteCallback(result -> { - boolean successful = result != null; - if (!successful) { - Slog.e(LOG_TAG, "Failed to set default home: " + packageName); - } - callback.accept(successful); - }); - if (packageName != null) { - getOrCreateController(userId).onAddRoleHolder(RoleManager.ROLE_HOME, - packageName, 0, remoteCallback); - } else { - getOrCreateController(userId).onClearRoleHolders(RoleManager.ROLE_HOME, 0, - remoteCallback); - } - } - } } diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java index 63ed416f2859..d9b67024018b 100644 --- a/services/core/java/com/android/server/rollback/Rollback.java +++ b/services/core/java/com/android/server/rollback/Rollback.java @@ -581,9 +581,18 @@ class Rollback { ParcelFileDescriptor.MODE_READ_ONLY)) { final long token = Binder.clearCallingIdentity(); try { - session.write(packageCodePath.getName(), 0, - packageCodePath.length(), - fd); + boolean fallbackToCopy = false; + try { + // Populate apk/apex files using hard links to avoid copy + session.stageViaHardLink(packageCodePath.getAbsolutePath()); + } catch (Exception ignore) { + fallbackToCopy = true; + } + if (fallbackToCopy) { + session.write(packageCodePath.getName(), 0, + packageCodePath.length(), + fd); + } } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index d3b1ac6e6096..cf20cf4c0c9f 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -502,6 +502,8 @@ public class StatsPullAtomService extends SystemService { synchronized (mProcessSystemIonHeapSizeLock) { return pullProcessSystemIonHeapSizeLocked(atomTag, data); } + case FrameworkStatsLog.SYSTEM_MEMORY: + return pullSystemMemory(atomTag, data); case FrameworkStatsLog.TEMPERATURE: synchronized (mTemperatureLock) { return pullTemperatureLocked(atomTag, data); @@ -796,6 +798,7 @@ public class StatsPullAtomService extends SystemService { registerSystemIonHeapSize(); registerIonHeapSize(); registerProcessSystemIonHeapSize(); + registerSystemMemory(); registerTemperature(); registerCoolingDevice(); registerBinderCallsStats(); @@ -1913,6 +1916,30 @@ public class StatsPullAtomService extends SystemService { return StatsManager.PULL_SUCCESS; } + private void registerSystemMemory() { + int tagId = FrameworkStatsLog.SYSTEM_MEMORY; + mStatsManager.setPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + DIRECT_EXECUTOR, + mStatsCallbackImpl + ); + } + + int pullSystemMemory(int atomTag, List<StatsEvent> pulledData) { + SystemMemoryUtil.Metrics metrics = SystemMemoryUtil.getMetrics(); + pulledData.add( + FrameworkStatsLog.buildStatsEvent( + atomTag, + metrics.unreclaimableSlabKb, + metrics.vmallocUsedKb, + metrics.pageTablesKb, + metrics.kernelStackKb, + metrics.totalIonKb, + metrics.unaccountedKb)); + return StatsManager.PULL_SUCCESS; + } + private void registerTemperature() { int tagId = FrameworkStatsLog.TEMPERATURE; mStatsManager.setPullAtomCallback( diff --git a/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java b/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java new file mode 100644 index 000000000000..99fc7c11c5b3 --- /dev/null +++ b/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.stats.pull; + +import android.os.Debug; + +/** + * Snapshots system-wide memory stats and computes unaccounted memory. + * Thread-safe. + */ +final class SystemMemoryUtil { + private SystemMemoryUtil() {} + + static Metrics getMetrics() { + int totalIonKb = (int) Debug.getIonHeapsSizeKb(); + + long[] mInfos = new long[Debug.MEMINFO_COUNT]; + Debug.getMemInfo(mInfos); + + long kReclaimableKb = mInfos[Debug.MEMINFO_KRECLAIMABLE]; + // Note: MEMINFO_KRECLAIMABLE includes MEMINFO_SLAB_RECLAIMABLE and ION pools. + // Fall back to using MEMINFO_SLAB_RECLAIMABLE in case of older kernels that do + // not include KReclaimable meminfo field. + if (kReclaimableKb == 0) { + kReclaimableKb = mInfos[Debug.MEMINFO_SLAB_RECLAIMABLE]; + } + + long accountedKb = mInfos[Debug.MEMINFO_FREE] + + mInfos[Debug.MEMINFO_ZRAM_TOTAL] + + mInfos[Debug.MEMINFO_BUFFERS] + + mInfos[Debug.MEMINFO_ACTIVE] + + mInfos[Debug.MEMINFO_INACTIVE] + + mInfos[Debug.MEMINFO_UNEVICTABLE] + + mInfos[Debug.MEMINFO_SLAB_UNRECLAIMABLE] + + kReclaimableKb + + mInfos[Debug.MEMINFO_VM_ALLOC_USED] + + mInfos[Debug.MEMINFO_PAGE_TABLES] + + Math.max(totalIonKb, 0); + + if (!Debug.isVmapStack()) { + // See b/146088882 + accountedKb += mInfos[Debug.MEMINFO_KERNEL_STACK]; + } + + Metrics result = new Metrics(); + result.unreclaimableSlabKb = (int) mInfos[Debug.MEMINFO_SLAB_UNRECLAIMABLE]; + result.vmallocUsedKb = (int) mInfos[Debug.MEMINFO_VM_ALLOC_USED]; + result.pageTablesKb = (int) mInfos[Debug.MEMINFO_PAGE_TABLES]; + result.kernelStackKb = (int) mInfos[Debug.MEMINFO_KERNEL_STACK]; + result.totalIonKb = totalIonKb; + result.unaccountedKb = (int) (mInfos[Debug.MEMINFO_TOTAL] - accountedKb); + return result; + } + + static final class Metrics { + public int unreclaimableSlabKb; + public int vmallocUsedKb; + public int pageTablesKb; + public int kernelStackKb; + public int totalIonKb; + public int unaccountedKb; + } +} diff --git a/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java b/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java index 118899add968..1867ee207958 100644 --- a/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java +++ b/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java @@ -61,7 +61,7 @@ import java.util.StringTokenizer; */ public final class GeolocationTimeZoneSuggestion { - @NonNull private final List<String> mZoneIds; + @Nullable private final List<String> mZoneIds; @Nullable private ArrayList<String> mDebugInfo; public GeolocationTimeZoneSuggestion(@Nullable List<String> zoneIds) { diff --git a/services/core/java/com/android/server/tv/PersistentDataStore.java b/services/core/java/com/android/server/tv/PersistentDataStore.java index d3c9b3bbe7f5..72556a75a4b5 100644 --- a/services/core/java/com/android/server/tv/PersistentDataStore.java +++ b/services/core/java/com/android/server/tv/PersistentDataStore.java @@ -30,23 +30,17 @@ import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import android.util.Xml; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; import libcore.io.IoUtils; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -246,12 +240,7 @@ final class PersistentDataStore { if (parser.getName().equals(TAG_BLOCKED_RATINGS)) { loadBlockedRatingsFromXml(parser); } else if (parser.getName().equals(TAG_PARENTAL_CONTROLS)) { - String enabled = parser.getAttributeValue(null, ATTR_ENABLED); - if (TextUtils.isEmpty(enabled)) { - throw new XmlPullParserException( - "Missing " + ATTR_ENABLED + " attribute on " + TAG_PARENTAL_CONTROLS); - } - mParentalControlsEnabled = Boolean.parseBoolean(enabled); + mParentalControlsEnabled = parser.getAttributeBoolean(null, ATTR_ENABLED); } } } diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java index beb11ed4ea0c..7f49eead19fa 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java @@ -192,8 +192,9 @@ public class UseCasePriorityHints { } } - private int readAttributeToInt(String attributeName, TypedXmlPullParser parser) { - return Integer.valueOf(parser.getAttributeValue(null, attributeName)); + private int readAttributeToInt(String attributeName, TypedXmlPullParser parser) + throws XmlPullParserException { + return parser.getAttributeInt(null, attributeName); } private void addNewUseCasePriority(int useCase, int fgPriority, int bgPriority) { diff --git a/services/core/java/com/android/server/utils/quota/MultiRateLimiter.java b/services/core/java/com/android/server/utils/quota/MultiRateLimiter.java new file mode 100644 index 000000000000..fdbe4b425d39 --- /dev/null +++ b/services/core/java/com/android/server/utils/quota/MultiRateLimiter.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.utils.quota; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +/** + * Can be used to rate limit events per app based on multiple rates at the same time. For example, + * it can limit an event to happen only: + * + * <li>5 times in 20 seconds</li> + * and + * <li>6 times in 40 seconds</li> + * and + * <li>10 times in 1 hour</li> + * + * <p><br> + * All listed rates apply at the same time, and the UPTC will be out of quota if it doesn't satisfy + * all the given rates. The underlying mechanism used is + * {@link com.android.server.utils.quota.CountQuotaTracker}, so all its conditions apply, as well + * as an additional constraint: all the user-package-tag combinations (UPTC) are considered to be in + * the same {@link com.android.server.utils.quota.Category}. + * </p> + * + * @hide + */ +public class MultiRateLimiter { + + private static final CountQuotaTracker[] EMPTY_TRACKER_ARRAY = {}; + + private final Object mLock = new Object(); + @GuardedBy("mLock") + private final CountQuotaTracker[] mQuotaTrackers; + + private MultiRateLimiter(List<CountQuotaTracker> quotaTrackers) { + mQuotaTrackers = quotaTrackers.toArray(EMPTY_TRACKER_ARRAY); + } + + /** Record that an event happened and count it towards the given quota. */ + public void noteEvent(int userId, @NonNull String packageName, @Nullable String tag) { + synchronized (mLock) { + noteEventLocked(userId, packageName, tag); + } + } + + /** Check whether the given UPTC is allowed to trigger an event. */ + public boolean isWithinQuota(int userId, @NonNull String packageName, @Nullable String tag) { + synchronized (mLock) { + return isWithinQuotaLocked(userId, packageName, tag); + } + } + + @GuardedBy("mLock") + private void noteEventLocked(int userId, @NonNull String packageName, @Nullable String tag) { + for (CountQuotaTracker quotaTracker : mQuotaTrackers) { + quotaTracker.noteEvent(userId, packageName, tag); + } + } + + @GuardedBy("mLock") + private boolean isWithinQuotaLocked(int userId, @NonNull String packageName, + @Nullable String tag) { + for (CountQuotaTracker quotaTracker : mQuotaTrackers) { + if (!quotaTracker.isWithinQuota(userId, packageName, tag)) { + return false; + } + } + return true; + } + + /** Can create a new {@link MultiRateLimiter}. */ + public static class Builder { + + private final List<CountQuotaTracker> mQuotaTrackers; + private final Context mContext; + private final Categorizer mCategorizer; + private final Category mCategory; + @Nullable private final QuotaTracker.Injector mInjector; + + /** + * Creates a new builder and allows to inject an object that can be used + * to manipulate elapsed time in tests. + */ + @VisibleForTesting + Builder(Context context, QuotaTracker.Injector injector) { + this.mQuotaTrackers = new ArrayList<>(); + this.mContext = context; + this.mInjector = injector; + this.mCategorizer = Categorizer.SINGLE_CATEGORIZER; + this.mCategory = Category.SINGLE_CATEGORY; + } + + /** Creates a new builder for {@link MultiRateLimiter}. */ + public Builder(Context context) { + this(context, null); + } + + /** + * Adds another rate limit to be used in {@link MultiRateLimiter}. + * + * @param limit The maximum event count an app can have in the rolling time window. + * @param windowSize The rolling time window to use when checking quota usage. + */ + public Builder addRateLimit(int limit, Duration windowSize) { + CountQuotaTracker countQuotaTracker; + if (mInjector != null) { + countQuotaTracker = new CountQuotaTracker(mContext, mCategorizer, mInjector); + } else { + countQuotaTracker = new CountQuotaTracker(mContext, mCategorizer); + } + countQuotaTracker.setCountLimit(mCategory, limit, windowSize.toMillis()); + mQuotaTrackers.add(countQuotaTracker); + return this; + } + + /** Adds another rate limit to be used in {@link MultiRateLimiter}. */ + public Builder addRateLimit(@NonNull RateLimit rateLimit) { + return addRateLimit(rateLimit.mLimit, rateLimit.mWindowSize); + } + + /** Adds all given rate limits that will be used in {@link MultiRateLimiter}. */ + public Builder addRateLimits(@NonNull RateLimit[] rateLimits) { + for (RateLimit rateLimit : rateLimits) { + addRateLimit(rateLimit); + } + return this; + } + + /** + * Return a new {@link com.android.server.utils.quota.MultiRateLimiter} using set rate + * limit. + */ + public MultiRateLimiter build() { + return new MultiRateLimiter(mQuotaTrackers); + } + } + + /** Helper class that describes a rate limit. */ + public static class RateLimit { + public final int mLimit; + public final Duration mWindowSize; + + /** + * @param limit The maximum count of some occurrence in the rolling time window. + * @param windowSize The rolling time window to use when checking quota usage. + */ + private RateLimit(int limit, Duration windowSize) { + this.mLimit = limit; + this.mWindowSize = windowSize; + } + + /** + * @param limit The maximum count of some occurrence in the rolling time window. + * @param windowSize The rolling time window to use when checking quota usage. + */ + public static RateLimit create(int limit, Duration windowSize) { + return new RateLimit(limit, windowSize); + } + } +} diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index c3d5874de609..31984531d31f 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -2984,7 +2984,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } if (wallpaper.allowBackup) { - out.attribute(null, "backup", "true"); + out.attributeBoolean(null, "backup", true); } out.endTag(null, tag); @@ -3249,7 +3249,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub wallpaper.primaryColors = new WallpaperColors(primary, secondary, tertiary, colorHints); } wallpaper.name = parser.getAttributeValue(null, "name"); - wallpaper.allowBackup = "true".equals(parser.getAttributeValue(null, "backup")); + wallpaper.allowBackup = parser.getAttributeBoolean(null, "backup", false); } // Called by SystemBackupAgent after files are restored to disk. diff --git a/services/core/java/com/android/server/wm/AppWarnings.java b/services/core/java/com/android/server/wm/AppWarnings.java index 55200b566979..994f07959f3b 100644 --- a/services/core/java/com/android/server/wm/AppWarnings.java +++ b/services/core/java/com/android/server/wm/AppWarnings.java @@ -31,16 +31,12 @@ import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import android.util.Xml; -import com.android.internal.util.FastXmlSerializer; - import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; -import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -541,15 +537,7 @@ class AppWarnings { if ("package".equals(tagName)) { final String name = parser.getAttributeValue(null, "name"); if (name != null) { - final String flags = parser.getAttributeValue( - null, "flags"); - int flagsInt = 0; - if (flags != null) { - try { - flagsInt = Integer.parseInt(flags); - } catch (NumberFormatException e) { - } - } + int flagsInt = parser.getAttributeInt(null, "flags", 0); mPackageFlags.put(name, flagsInt); } } diff --git a/services/core/java/com/android/server/wm/ConfigurationContainerListener.java b/services/core/java/com/android/server/wm/ConfigurationContainerListener.java index 3d84e1752e6a..ce6b7f917991 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainerListener.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainerListener.java @@ -23,7 +23,7 @@ import android.content.res.Configuration; */ public interface ConfigurationContainerListener { - /** {@see ConfigurationContainer#onRequestedOverrideConfigurationChanged} */ + /** @see ConfigurationContainer#onRequestedOverrideConfigurationChanged */ default void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {} /** Called when new merged override configuration is reported. */ diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index cd3f32278165..15483cb90ce2 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -174,6 +174,13 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { return false; } + if (mDisplayContent.mFocusedApp != null) { + // We record the last focused TDA that respects orientation request, check if this + // change may affect it. + mDisplayContent.onLastFocusedTaskDisplayAreaChanged( + mDisplayContent.mFocusedApp.getDisplayArea()); + } + // The orientation request from this DA may now be respected. if (!ignoreOrientationRequest) { return mDisplayContent.updateOrientation(); diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java index 7573e92e82a8..d4b319a525da 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java @@ -35,6 +35,7 @@ import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature; import static com.android.server.wm.DisplayAreaPolicyBuilder.HierarchyBuilder; import android.content.res.Resources; +import android.os.Bundle; import android.text.TextUtils; import java.util.ArrayList; @@ -71,6 +72,10 @@ public abstract class DisplayAreaPolicy { */ public abstract void addWindow(WindowToken token); + /** Gets the {@link DisplayArea} which a {@link WindowToken} is about to be attached to. */ + public abstract DisplayArea.Tokens getDisplayAreaForWindowToken(int type, Bundle options, + boolean ownerCanManageAppTokens, boolean roundedCornerOverlay); + /** * Gets the set of {@link DisplayArea} that are created for the given feature to apply to. */ diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java index 8421faa249aa..c8fadf62eb2f 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java @@ -137,12 +137,12 @@ class DisplayAreaPolicyBuilder { private ArrayList<HierarchyBuilder> mDisplayAreaGroupHierarchyBuilders = new ArrayList<>(); /** - * When a window is created, the policy will use this function to select the - * {@link RootDisplayArea} to place that window in. The selected root can be either the one of - * the {@link #mRootHierarchyBuilder} or the one of any of the + * When a window is created, the policy will use this function, which takes window type and + * options, to select the {@link RootDisplayArea} to place that window in. The selected root + * can be either the one of the {@link #mRootHierarchyBuilder} or the one of any of the * {@link #mDisplayAreaGroupHierarchyBuilders}. **/ - @Nullable private BiFunction<WindowToken, Bundle, RootDisplayArea> mSelectRootForWindowFunc; + @Nullable private BiFunction<Integer, Bundle, RootDisplayArea> mSelectRootForWindowFunc; /** Defines the root hierarchy for the whole logical display. */ DisplayAreaPolicyBuilder setRootHierarchy(HierarchyBuilder rootHierarchyBuilder) { @@ -162,7 +162,7 @@ class DisplayAreaPolicyBuilder { /** The policy will use this function to find the root to place windows in. */ DisplayAreaPolicyBuilder setSelectRootForWindowFunc( - BiFunction<WindowToken, Bundle, RootDisplayArea> selectRootForWindowFunc) { + BiFunction<Integer, Bundle, RootDisplayArea> selectRootForWindowFunc) { mSelectRootForWindowFunc = selectRootForWindowFunc; return this; } @@ -655,19 +655,19 @@ class DisplayAreaPolicyBuilder { static class Result extends DisplayAreaPolicy { final List<RootDisplayArea> mDisplayAreaGroupRoots; - final BiFunction<WindowToken, Bundle, RootDisplayArea> mSelectRootForWindowFunc; + final BiFunction<Integer, Bundle, RootDisplayArea> mSelectRootForWindowFunc; private final TaskDisplayArea mDefaultTaskDisplayArea; Result(WindowManagerService wmService, RootDisplayArea root, List<RootDisplayArea> displayAreaGroupRoots, - @Nullable BiFunction<WindowToken, Bundle, RootDisplayArea> + @Nullable BiFunction<Integer, Bundle, RootDisplayArea> selectRootForWindowFunc) { super(wmService, root); mDisplayAreaGroupRoots = Collections.unmodifiableList(displayAreaGroupRoots); mSelectRootForWindowFunc = selectRootForWindowFunc == null // Always return the highest level root of the logical display when the func is // not specified. - ? (window, options) -> mRoot + ? (type, options) -> mRoot : selectRootForWindowFunc; // Cache the default TaskDisplayArea for quick access. @@ -689,7 +689,8 @@ class DisplayAreaPolicyBuilder { @VisibleForTesting DisplayArea.Tokens findAreaForToken(WindowToken token) { - return mSelectRootForWindowFunc.apply(token, token.mOptions).findAreaForToken(token); + return mSelectRootForWindowFunc.apply(token.windowType, token.mOptions) + .findAreaForToken(token); } @VisibleForTesting @@ -727,6 +728,13 @@ class DisplayAreaPolicyBuilder { public TaskDisplayArea getDefaultTaskDisplayArea() { return mDefaultTaskDisplayArea; } + + @Override + public DisplayArea.Tokens getDisplayAreaForWindowToken(int type, Bundle options, + boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) { + return mSelectRootForWindowFunc.apply(type, options).findAreaForToken(type, + ownerCanManageAppTokens, roundedCornerOverlay); + } } static class PendingArea { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index bcd78b1bdad3..e88f8e390833 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -165,6 +165,7 @@ import android.graphics.Region.Op; import android.hardware.display.DisplayManagerInternal; import android.metrics.LogMaker; import android.os.Binder; +import android.os.Bundle; import android.os.Debug; import android.os.Handler; import android.os.IBinder; @@ -488,8 +489,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ ActivityRecord mFocusedApp = null; - /** The last focused {@link TaskDisplayArea} on this display. */ - private TaskDisplayArea mLastFocusedTaskDisplayArea = null; + /** + * We only respect the orientation request from apps below this {@link TaskDisplayArea}. + * It is the last focused {@link TaskDisplayArea} on this display that handles orientation + * request. + */ + @Nullable + private TaskDisplayArea mOrientationRequestingTaskDisplayArea = null; /** * The launching activity which is using fixed rotation transformation. @@ -3325,7 +3331,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // Called even if the focused app is not changed in case the app is moved to a different // TaskDisplayArea. - setLastFocusedTaskDisplayArea(newFocus.getDisplayArea()); + onLastFocusedTaskDisplayAreaChanged(newFocus.getDisplayArea()); } if (mFocusedApp == newFocus) { return false; @@ -3339,16 +3345,27 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } /** Called when the focused {@link TaskDisplayArea} on this display may have changed. */ - @VisibleForTesting - void setLastFocusedTaskDisplayArea(@Nullable TaskDisplayArea taskDisplayArea) { - if (taskDisplayArea != null) { - mLastFocusedTaskDisplayArea = taskDisplayArea; + void onLastFocusedTaskDisplayAreaChanged(@Nullable TaskDisplayArea taskDisplayArea) { + // Only record the TaskDisplayArea that handles orientation request. + if (taskDisplayArea != null && taskDisplayArea.handlesOrientationChangeFromDescendant()) { + mOrientationRequestingTaskDisplayArea = taskDisplayArea; + return; + } + + // If the previous TDA no longer handles orientation request, clear it. + if (mOrientationRequestingTaskDisplayArea != null + && !mOrientationRequestingTaskDisplayArea + .handlesOrientationChangeFromDescendant()) { + mOrientationRequestingTaskDisplayArea = null; } } - /** Gets the last focused {@link TaskDisplayArea} on this display. */ - TaskDisplayArea getLastFocusedTaskDisplayArea() { - return mLastFocusedTaskDisplayArea; + /** + * Gets the {@link TaskDisplayArea} that we respect orientation requests from apps below it. + */ + @Nullable + TaskDisplayArea getOrientationRequestingTaskDisplayArea() { + return mOrientationRequestingTaskDisplayArea; } /** Updates the layer assignment of windows on this display. */ @@ -4775,7 +4792,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return getWindowContainers().getSurfaceControl(); } - @VisibleForTesting DisplayArea.Tokens getImeContainer() { return mImeWindowsContainers; } @@ -5731,4 +5747,20 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp MagnificationSpec getMagnificationSpec() { return mMagnificationSpec; } + + DisplayArea getAreaForWindowToken(int windowType, Bundle options, + boolean ownerCanManageAppToken, boolean roundedCornerOverlay) { + // TODO(b/159767464): figure out how to find an appropriate TDA. + if (windowType >= FIRST_APPLICATION_WINDOW && windowType <= LAST_APPLICATION_WINDOW) { + return getDefaultTaskDisplayArea(); + } + // Return IME container here because it could be in one of sub RootDisplayAreas depending on + // the focused edit text. Also, the RootDisplayArea choosing strategy is implemented by + // the server side, but not mSelectRootForWindowFunc customized by OEM. + if (windowType == TYPE_INPUT_METHOD || windowType == TYPE_INPUT_METHOD_DIALOG) { + return getImeContainer(); + } + return mDisplayAreaPolicy.getDisplayAreaForWindowToken(windowType, options, + ownerCanManageAppToken, roundedCornerOverlay); + } } diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java index f4fdd7370e9c..57c947f572d9 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java @@ -450,12 +450,12 @@ class DisplayWindowSettingsProvider implements SettingsProvider { out.attributeInt(null, "windowingMode", settingsEntry.mWindowingMode); } if (settingsEntry.mUserRotationMode != null) { - out.attribute(null, "userRotationMode", - settingsEntry.mUserRotationMode.toString()); + out.attributeInt(null, "userRotationMode", + settingsEntry.mUserRotationMode); } if (settingsEntry.mUserRotation != null) { - out.attribute(null, "userRotation", - settingsEntry.mUserRotation.toString()); + out.attributeInt(null, "userRotation", + settingsEntry.mUserRotation); } if (settingsEntry.mForcedWidth != 0 && settingsEntry.mForcedHeight != 0) { out.attributeInt(null, "forcedWidth", settingsEntry.mForcedWidth); @@ -465,30 +465,30 @@ class DisplayWindowSettingsProvider implements SettingsProvider { out.attributeInt(null, "forcedDensity", settingsEntry.mForcedDensity); } if (settingsEntry.mForcedScalingMode != null) { - out.attribute(null, "forcedScalingMode", - settingsEntry.mForcedScalingMode.toString()); + out.attributeInt(null, "forcedScalingMode", + settingsEntry.mForcedScalingMode); } if (settingsEntry.mRemoveContentMode != REMOVE_CONTENT_MODE_UNDEFINED) { out.attributeInt(null, "removeContentMode", settingsEntry.mRemoveContentMode); } if (settingsEntry.mShouldShowWithInsecureKeyguard != null) { - out.attribute(null, "shouldShowWithInsecureKeyguard", - settingsEntry.mShouldShowWithInsecureKeyguard.toString()); + out.attributeBoolean(null, "shouldShowWithInsecureKeyguard", + settingsEntry.mShouldShowWithInsecureKeyguard); } if (settingsEntry.mShouldShowSystemDecors != null) { - out.attribute(null, "shouldShowSystemDecors", - settingsEntry.mShouldShowSystemDecors.toString()); + out.attributeBoolean(null, "shouldShowSystemDecors", + settingsEntry.mShouldShowSystemDecors); } if (settingsEntry.mImePolicy != null) { out.attributeInt(null, "imePolicy", settingsEntry.mImePolicy); } if (settingsEntry.mFixedToUserRotation != null) { - out.attribute(null, "fixedToUserRotation", - settingsEntry.mFixedToUserRotation.toString()); + out.attributeInt(null, "fixedToUserRotation", + settingsEntry.mFixedToUserRotation); } if (settingsEntry.mIgnoreOrientationRequest != null) { - out.attribute(null, "ignoreOrientationRequest", - settingsEntry.mIgnoreOrientationRequest.toString()); + out.attributeBoolean(null, "ignoreOrientationRequest", + settingsEntry.mIgnoreOrientationRequest); } out.endTag(null, "display"); } diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java index 52ada472f0aa..fd42b2467d19 100644 --- a/services/core/java/com/android/server/wm/PinnedStackController.java +++ b/services/core/java/com/android/server/wm/PinnedStackController.java @@ -29,7 +29,6 @@ import android.util.DisplayMetrics; import android.util.Log; import android.util.Slog; import android.view.DisplayInfo; -import android.view.IPinnedStackController; import android.view.IPinnedStackListener; import java.io.PrintWriter; @@ -63,8 +62,6 @@ class PinnedStackController { private final PinnedStackListenerDeathHandler mPinnedStackListenerDeathHandler = new PinnedStackListenerDeathHandler(); - private final PinnedStackControllerCallback mCallbacks = new PinnedStackControllerCallback(); - /** Whether the PiP is entering or leaving. */ private boolean mIsPipWindowingModeChanging; @@ -86,18 +83,6 @@ class PinnedStackController { private final DisplayMetrics mTmpMetrics = new DisplayMetrics(); /** - * The callback object passed to listeners for them to notify the controller of state changes. - */ - private class PinnedStackControllerCallback extends IPinnedStackController.Stub { - @Override - public int getDisplayRotation() { - synchronized (mService.mGlobalLock) { - return mDisplayInfo.rotation; - } - } - } - - /** * Handler for the case where the listener dies. */ private class PinnedStackListenerDeathHandler implements IBinder.DeathRecipient { @@ -141,7 +126,6 @@ class PinnedStackController { void registerPinnedStackListener(IPinnedStackListener listener) { try { listener.asBinder().linkToDeath(mPinnedStackListenerDeathHandler, 0); - listener.onListenerRegistered(mCallbacks); mPinnedStackListener = listener; notifyDisplayInfoChanged(mDisplayInfo); notifyImeVisibilityChanged(mIsImeShowing, mImeHeight); diff --git a/services/core/java/com/android/server/wm/RootDisplayArea.java b/services/core/java/com/android/server/wm/RootDisplayArea.java index c4fcea68a6fc..393055eab111 100644 --- a/services/core/java/com/android/server/wm/RootDisplayArea.java +++ b/services/core/java/com/android/server/wm/RootDisplayArea.java @@ -106,12 +106,23 @@ class RootDisplayArea extends DisplayArea<DisplayArea> { } /** Finds the {@link DisplayArea.Tokens} that this type of window should be attached to. */ + @Nullable DisplayArea.Tokens findAreaForToken(WindowToken token) { - int windowLayerFromType = token.getWindowLayerFromType(); + return findAreaForToken(token.windowType, token.mOwnerCanManageAppTokens, + token.mRoundedCornerOverlay); + } + + @Nullable + DisplayArea.Tokens findAreaForToken(int windowType, boolean ownerCanManageAppTokens, + boolean roundedCornerOverlay) { + // TODO(b/159767464): cover TYPE_INPUT_METHOD(_DIALOG) case here. mAreaForLayer doesn't + // contain IME container. + int windowLayerFromType = mWmService.mPolicy.getWindowLayerFromTypeLw(windowType, + ownerCanManageAppTokens); if (windowLayerFromType == APPLICATION_LAYER) { throw new IllegalArgumentException( "There shouldn't be WindowToken on APPLICATION_LAYER"); - } else if (token.mRoundedCornerOverlay) { + } else if (roundedCornerOverlay) { windowLayerFromType = mAreaForLayer.length - 1; } return mAreaForLayer[windowLayerFromType]; diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 4498a8c82583..9425602763c5 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -641,9 +641,7 @@ final class TaskDisplayArea extends DisplayArea<Task> { @Override int getOrientation(int candidate) { mLastOrientationSource = null; - // Only allow to specify orientation if this TDA is not set to ignore orientation request, - // and it has the focus. - if (mIgnoreOrientationRequest || !isLastFocused()) { + if (!canSpecifyOrientation()) { return SCREEN_ORIENTATION_UNSET; } @@ -1918,10 +1916,14 @@ final class TaskDisplayArea extends DisplayArea<Task> { return lastReparentedStack; } - /** Whether this task display area is the last focused one on this logical display. */ + /** Whether this task display area can request orientation. */ @VisibleForTesting - boolean isLastFocused() { - return mDisplayContent.getLastFocusedTaskDisplayArea() == this; + boolean canSpecifyOrientation() { + // Only allow to specify orientation if this TDA is not set to ignore orientation request, + // and it is the last focused one on this logical display that can request orientation + // request. + return !mIgnoreOrientationRequest + && mDisplayContent.getOrientationRequestingTaskDisplayArea() == this; } @Override diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 6242fe9a1444..adc7a227861a 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -98,6 +98,7 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Comparator; import java.util.LinkedList; +import java.util.List; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; @@ -314,6 +315,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< final SurfaceControl.Transaction mSyncTransaction; @SyncState int mSyncState = SYNC_STATE_NONE; + private final List<WindowContainerListener> mListeners = new ArrayList<>(); + WindowContainer(WindowManagerService wms) { mWmService = wms; mPendingTransaction = wms.mTransactionFactory.get(); @@ -637,6 +640,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< if (mParent != null) { mParent.removeChild(this); } + + for (int i = mListeners.size() - 1; i >= 0; --i) { + mListeners.get(i).onRemoved(); + } } /** @@ -819,6 +826,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< final WindowContainer child = mChildren.get(i); child.onDisplayChanged(dc); } + for (int i = mListeners.size() - 1; i >= 0; --i) { + mListeners.get(i).onDisplayChanged(dc); + } } DisplayContent getDisplayContent() { @@ -3106,4 +3116,19 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< mSyncState = SYNC_STATE_NONE; prepareSync(); } + + void registerWindowContainerListener(WindowContainerListener listener) { + if (mListeners.contains(listener)) { + return; + } + mListeners.add(listener); + // Also register to ConfigurationChangeListener to receive configuration changes. + registerConfigurationChangeListener(listener); + listener.onDisplayChanged(getDisplayContent()); + } + + void unregisterWindowContainerListener(WindowContainerListener listener) { + mListeners.remove(listener); + unregisterConfigurationChangeListener(listener); + } } diff --git a/services/core/java/com/android/server/wm/WindowContainerListener.java b/services/core/java/com/android/server/wm/WindowContainerListener.java new file mode 100644 index 000000000000..ac1fe173dd09 --- /dev/null +++ b/services/core/java/com/android/server/wm/WindowContainerListener.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +/** + * Interface for listening to changes in a {@link WindowContainer}. A usage of this listener is + * to receive the changes and propagate them to the client side. + */ +interface WindowContainerListener extends ConfigurationContainerListener { + + /** @see WindowContainer#onDisplayChanged(DisplayContent) */ + default void onDisplayChanged(DisplayContent dc) {} + + /** Called when {@link WindowContainer#removeImmediately()} is invoked. */ + default void onRemoved() {} +} diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java new file mode 100644 index 000000000000..051ece38b26f --- /dev/null +++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.view.Display.INVALID_DISPLAY; + +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; +import static com.android.internal.protolog.ProtoLogGroup.WM_ERROR; + +import android.annotation.NonNull; +import android.app.IWindowToken; +import android.content.res.Configuration; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.ArrayMap; +import android.view.View; +import android.view.ViewGroup; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.protolog.common.ProtoLog; + +import java.util.Map; +import java.util.Objects; + +/** + * A controller to register/unregister {@link WindowContainerListener} for + * {@link android.app.WindowContext}. + * + * <ul> + * <li>When a {@link android.app.WindowContext} is created, it registers the listener via + * {@link WindowManagerService#registerWindowContextListener(IBinder, int, int, Bundle)} + * automatically.</li> + * <li>When the {@link android.app.WindowContext} adds the first window to the screen via + * {@link android.view.WindowManager#addView(View, ViewGroup.LayoutParams)}, + * {@link WindowManagerService} then updates the {@link WindowContextListenerImpl} to listen + * to corresponding {@link WindowToken} via this controller.</li> + * <li>When the {@link android.app.WindowContext} is GCed, it unregisters the previously + * registered listener via + * {@link WindowManagerService#unregisterWindowContextListener(IBinder)}. + * {@link WindowManagerService} is also responsible for removing the + * {@link android.app.WindowContext} created {@link WindowToken}.</li> + * </ul> + * <p>Note that the listener may be removed earlier than the + * {@link #unregisterWindowContainerListener(IBinder)} if the listened {@link WindowContainer} was + * removed. An example is that the {@link DisplayArea} is removed when users unfold the + * foldable devices. Another example is that the associated external display is detached.</p> + */ +class WindowContextListenerController { + @VisibleForTesting + final Map<IBinder, WindowContextListenerImpl> mListeners = new ArrayMap<>(); + + /** + * Registers the listener to a {@code container} which is associated with + * a {@code clientToken}, which is a {@link android.app.WindowContext} representation. If the + * listener associated with {@code clientToken} hasn't been initialized yet, create one + * {@link WindowContextListenerImpl}. Otherwise, the listener associated with + * {@code clientToken} switches to listen to the {@code container}. + * + * @param clientToken the token to associate with the listener + * @param container the {@link WindowContainer} which the listener is going to listen to. + * @param ownerUid the caller UID + */ + void registerWindowContainerListener(@NonNull IBinder clientToken, + @NonNull WindowContainer container, int ownerUid) { + WindowContextListenerImpl listener = mListeners.get(clientToken); + if (listener == null) { + listener = new WindowContextListenerImpl(clientToken, container, ownerUid); + listener.register(); + } else { + listener.updateContainer(container); + } + } + + void unregisterWindowContainerListener(IBinder clientToken) { + final WindowContextListenerImpl listener = mListeners.get(clientToken); + // Listeners may be removed earlier. An example is the display where the listener is + // located is detached. In this case, all window containers on the display, as well as + // their listeners will be removed before their listeners are unregistered. + if (listener == null) { + return; + } + listener.unregister(); + } + + boolean assertCallerCanRemoveListener(IBinder clientToken, boolean callerCanManageAppTokens, + int callingUid) { + final WindowContextListenerImpl listener = mListeners.get(clientToken); + if (listener == null) { + ProtoLog.i(WM_DEBUG_ADD_REMOVE, "The listener does not exist."); + return false; + } + if (callerCanManageAppTokens) { + return true; + } + if (callingUid != listener.mOwnerUid) { + throw new UnsupportedOperationException("Uid mismatch. Caller uid is " + callingUid + + ", while the listener's owner is from " + listener.mOwnerUid); + } + return true; + } + + @VisibleForTesting + class WindowContextListenerImpl implements WindowContainerListener { + @NonNull private final IBinder mClientToken; + private final int mOwnerUid; + @NonNull private WindowContainer mContainer; + + private DeathRecipient mDeathRecipient; + + private int mLastReportedDisplay = INVALID_DISPLAY; + private Configuration mLastReportedConfig; + + private WindowContextListenerImpl(IBinder clientToken, WindowContainer container, + int ownerUid) { + mClientToken = clientToken; + mContainer = Objects.requireNonNull(container); + mOwnerUid = ownerUid; + + final DeathRecipient deathRecipient = new DeathRecipient(); + try { + deathRecipient.linkToDeath(); + mDeathRecipient = deathRecipient; + } catch (RemoteException e) { + ProtoLog.e(WM_ERROR, "Could not register window container listener token=%s, " + + "container=%s", mClientToken, mContainer); + } + } + + /** TEST ONLY: returns the {@link WindowContainer} of the listener */ + @VisibleForTesting + WindowContainer getWindowContainer() { + return mContainer; + } + + private void updateContainer(WindowContainer newContainer) { + if (mContainer.equals(newContainer)) { + return; + } + mContainer.unregisterWindowContainerListener(this); + mContainer = newContainer; + clear(); + register(); + } + + private void register() { + if (mDeathRecipient == null) { + throw new IllegalStateException("Invalid client token: " + mClientToken); + } + mListeners.putIfAbsent(mClientToken, this); + mContainer.registerWindowContainerListener(this); + reportConfigToWindowTokenClient(); + } + + private void unregister() { + mContainer.unregisterWindowContainerListener(this); + mListeners.remove(mClientToken); + } + + private void clear() { + mLastReportedConfig = null; + mLastReportedDisplay = INVALID_DISPLAY; + } + + @Override + public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) { + reportConfigToWindowTokenClient(); + } + + @Override + public void onDisplayChanged(DisplayContent dc) { + reportConfigToWindowTokenClient(); + } + + private void reportConfigToWindowTokenClient() { + if (mDeathRecipient == null) { + throw new IllegalStateException("Invalid client token: " + mClientToken); + } + + if (mLastReportedConfig == null) { + mLastReportedConfig = new Configuration(); + } + final Configuration config = mContainer.getConfiguration(); + final int displayId = mContainer.getDisplayContent().getDisplayId(); + if (config.equals(mLastReportedConfig) && displayId == mLastReportedDisplay) { + // No changes since last reported time. + return; + } + + mLastReportedConfig.setTo(config); + mLastReportedDisplay = displayId; + + IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(mClientToken); + try { + windowTokenClient.onConfigurationChanged(config, displayId); + } catch (RemoteException e) { + ProtoLog.w(WM_ERROR, "Could not report config changes to the window token client."); + } + } + + @Override + public void onRemoved() { + if (mDeathRecipient == null) { + throw new IllegalStateException("Invalid client token: " + mClientToken); + } + mDeathRecipient.unlinkToDeath(); + IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(mClientToken); + try { + windowTokenClient.onWindowTokenRemoved(); + } catch (RemoteException e) { + ProtoLog.w(WM_ERROR, "Could not report token removal to the window token client."); + } + unregister(); + } + + private class DeathRecipient implements IBinder.DeathRecipient { + @Override + public void binderDied() { + synchronized (mContainer.mWmService.mGlobalLock) { + mDeathRecipient = null; + unregister(); + } + } + + void linkToDeath() throws RemoteException { + mClientToken.linkToDeath(this, 0); + } + + void unlinkToDeath() { + mClientToken.unlinkToDeath(this, 0); + } + } + } +} diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 37834cc4b8b0..8fd342c25bc3 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -769,6 +769,8 @@ public class WindowManagerService extends IWindowManager.Stub final AnrController mAnrController; private final ImpressionAttestationController mImpressionAttestationController; + private final WindowContextListenerController mWindowContextListenerController = + new WindowContextListenerController(); @VisibleForTesting final class SettingsObserver extends ContentObserver { @@ -2627,6 +2629,10 @@ public class WindowManagerService extends IWindowManager.Stub } boolean checkCallingPermission(String permission, String func) { + return checkCallingPermission(permission, func, true /* printLog */); + } + + boolean checkCallingPermission(String permission, String func, boolean printLog) { // Quick check: if the calling permission is me, it's all okay. if (Binder.getCallingPid() == myPid()) { return true; @@ -2636,8 +2642,10 @@ public class WindowManagerService extends IWindowManager.Stub == PackageManager.PERMISSION_GRANTED) { return true; } - ProtoLog.w(WM_ERROR, "Permission Denial: %s from pid=%d, uid=%d requires %s", - func, Binder.getCallingPid(), Binder.getCallingUid(), permission); + if (printLog) { + ProtoLog.w(WM_ERROR, "Permission Denial: %s from pid=%d, uid=%d requires %s", + func, Binder.getCallingPid(), Binder.getCallingUid(), permission); + } return false; } @@ -2730,6 +2738,52 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public boolean registerWindowContextListener(IBinder clientToken, int type, int displayId, + Bundle options) { + final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS, + "registerWindowContextListener", false /* printLog */); + final int callingUid = Binder.getCallingUid(); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId); + if (dc == null) { + ProtoLog.w(WM_ERROR, "registerWindowContextListener: trying to add listener to" + + " a non-existing display:%d", displayId); + return false; + } + // TODO(b/155340867): Investigate if we still need roundedCornerOverlay after + // the feature b/155340867 is completed. + final DisplayArea da = dc.getAreaForWindowToken(type, options, + callerCanManageAppTokens, false /* roundedCornerOverlay */); + mWindowContextListenerController.registerWindowContainerListener(clientToken, da, + callingUid); + return true; + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void unregisterWindowContextListener(IBinder clientToken) { + final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS, + "unregisterWindowContextListener", false /* printLog */); + final int callingUid = Binder.getCallingUid(); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + if (mWindowContextListenerController.assertCallerCanRemoveListener(clientToken, + callerCanManageAppTokens, callingUid)) { + mWindowContextListenerController.unregisterWindowContainerListener(clientToken); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override public boolean isWindowToken(IBinder binder) { synchronized (mGlobalLock) { final WindowToken windowToken = mRoot.getWindowToken(binder); diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 5ced6a52050b..34875ed70889 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -419,6 +419,7 @@ class WindowToken extends WindowContainer<WindowState> { reportWindowTokenRemovedToClient(); } + // TODO(b/159767464): Remove after we migrate to listener approach. private void reportWindowTokenRemovedToClient() { if (!shouldReportToClient()) { return; diff --git a/services/core/xsd/device-state-config/device-state-config.xsd b/services/core/xsd/device-state-config/device-state-config.xsd index 0d8c08c93ff2..501450398fc3 100644 --- a/services/core/xsd/device-state-config/device-state-config.xsd +++ b/services/core/xsd/device-state-config/device-state-config.xsd @@ -57,8 +57,8 @@ <xs:complexType name="sensorCondition"> <xs:sequence> + <xs:element name="type" type="xs:string" /> <xs:element name="name" type="xs:string" /> - <xs:element name="type" type="xs:positiveInteger" /> <xs:element name="value" type="numericRange" maxOccurs="unbounded" /> </xs:sequence> </xs:complexType> diff --git a/services/core/xsd/device-state-config/schema/current.txt b/services/core/xsd/device-state-config/schema/current.txt index 667d1add5a98..b396af0fa0c6 100644 --- a/services/core/xsd/device-state-config/schema/current.txt +++ b/services/core/xsd/device-state-config/schema/current.txt @@ -44,10 +44,10 @@ package com.android.server.policy.devicestate.config { public class SensorCondition { ctor public SensorCondition(); method public String getName(); - method public java.math.BigInteger getType(); + method public String getType(); method public java.util.List<com.android.server.policy.devicestate.config.NumericRange> getValue(); method public void setName(String); - method public void setType(java.math.BigInteger); + method public void setType(String); } public class XmlParser { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java index 1a147b9f73e4..a281180d77d1 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java @@ -40,14 +40,14 @@ import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.pm.UserRestrictionsUtils; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; @@ -300,7 +300,7 @@ class ActiveAdmin { return UserHandle.of(UserHandle.getUserId(info.getActivityInfo().applicationInfo.uid)); } - void writeToXml(XmlSerializer out) + void writeToXml(TypedXmlSerializer out) throws IllegalArgumentException, IllegalStateException, IOException { out.startTag(null, TAG_POLICIES); info.writePoliciesToXml(out); @@ -413,11 +413,11 @@ class ActiveAdmin { } if (isNetworkLoggingEnabled) { out.startTag(null, TAG_IS_NETWORK_LOGGING_ENABLED); - out.attribute(null, ATTR_VALUE, Boolean.toString(isNetworkLoggingEnabled)); - out.attribute(null, ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS, - Integer.toString(numNetworkLoggingNotifications)); - out.attribute(null, ATTR_LAST_NETWORK_LOGGING_NOTIFICATION, - Long.toString(lastNetworkLoggingNotificationTimeMs)); + out.attributeBoolean(null, ATTR_VALUE, isNetworkLoggingEnabled); + out.attributeInt(null, ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS, + numNetworkLoggingNotifications); + out.attributeLong(null, ATTR_LAST_NETWORK_LOGGING_NOTIFICATION, + lastNetworkLoggingNotificationTimeMs); out.endTag(null, TAG_IS_NETWORK_LOGGING_ENABLED); } if (disabledKeyguardFeatures != DEF_KEYGUARD_FEATURES_DISABLED) { @@ -535,13 +535,13 @@ class ActiveAdmin { } } - void writeTextToXml(XmlSerializer out, String tag, String text) throws IOException { + void writeTextToXml(TypedXmlSerializer out, String tag, String text) throws IOException { out.startTag(null, tag); out.text(text); out.endTag(null, tag); } - void writePackageListToXml(XmlSerializer out, String outerTag, + void writePackageListToXml(TypedXmlSerializer out, String outerTag, List<String> packageList) throws IllegalArgumentException, IllegalStateException, IOException { if (packageList == null) { @@ -550,35 +550,35 @@ class ActiveAdmin { writeAttributeValuesToXml(out, outerTag, TAG_PACKAGE_LIST_ITEM, packageList); } - void writeAttributeValueToXml(XmlSerializer out, String tag, String value) + void writeAttributeValueToXml(TypedXmlSerializer out, String tag, String value) throws IOException { out.startTag(null, tag); out.attribute(null, ATTR_VALUE, value); out.endTag(null, tag); } - void writeAttributeValueToXml(XmlSerializer out, String tag, int value) + void writeAttributeValueToXml(TypedXmlSerializer out, String tag, int value) throws IOException { out.startTag(null, tag); - out.attribute(null, ATTR_VALUE, Integer.toString(value)); + out.attributeInt(null, ATTR_VALUE, value); out.endTag(null, tag); } - void writeAttributeValueToXml(XmlSerializer out, String tag, long value) + void writeAttributeValueToXml(TypedXmlSerializer out, String tag, long value) throws IOException { out.startTag(null, tag); - out.attribute(null, ATTR_VALUE, Long.toString(value)); + out.attributeLong(null, ATTR_VALUE, value); out.endTag(null, tag); } - void writeAttributeValueToXml(XmlSerializer out, String tag, boolean value) + void writeAttributeValueToXml(TypedXmlSerializer out, String tag, boolean value) throws IOException { out.startTag(null, tag); - out.attribute(null, ATTR_VALUE, Boolean.toString(value)); + out.attributeBoolean(null, ATTR_VALUE, value); out.endTag(null, tag); } - void writeAttributeValuesToXml(XmlSerializer out, String outerTag, String innerTag, + void writeAttributeValuesToXml(TypedXmlSerializer out, String outerTag, String innerTag, @NonNull Collection<String> values) throws IOException { out.startTag(null, outerTag); for (String value : values) { @@ -589,7 +589,7 @@ class ActiveAdmin { out.endTag(null, outerTag); } - void readFromXml(XmlPullParser parser, boolean shouldOverridePolicies) + void readFromXml(TypedXmlPullParser parser, boolean shouldOverridePolicies) throws XmlPullParserException, IOException { int outerDepth = parser.getDepth(); int type; @@ -606,47 +606,33 @@ class ActiveAdmin { info.readPoliciesFromXml(parser); } } else if (TAG_PASSWORD_QUALITY.equals(tag)) { - mPasswordPolicy.quality = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + mPasswordPolicy.quality = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_MIN_PASSWORD_LENGTH.equals(tag)) { - mPasswordPolicy.length = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + mPasswordPolicy.length = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_PASSWORD_HISTORY_LENGTH.equals(tag)) { - passwordHistoryLength = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + passwordHistoryLength = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_MIN_PASSWORD_UPPERCASE.equals(tag)) { - mPasswordPolicy.upperCase = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + mPasswordPolicy.upperCase = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_MIN_PASSWORD_LOWERCASE.equals(tag)) { - mPasswordPolicy.lowerCase = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + mPasswordPolicy.lowerCase = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_MIN_PASSWORD_LETTERS.equals(tag)) { - mPasswordPolicy.letters = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + mPasswordPolicy.letters = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_MIN_PASSWORD_NUMERIC.equals(tag)) { - mPasswordPolicy.numeric = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + mPasswordPolicy.numeric = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_MIN_PASSWORD_SYMBOLS.equals(tag)) { - mPasswordPolicy.symbols = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + mPasswordPolicy.symbols = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_MIN_PASSWORD_NONLETTER.equals(tag)) { - mPasswordPolicy.nonLetter = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + mPasswordPolicy.nonLetter = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_PASSWORD_QUALITY_APPLIES_TO_PARENT.equals(tag)) { - mPasswordPolicyAppliesToParent = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + mPasswordPolicyAppliesToParent = parser.getAttributeBoolean(null, ATTR_VALUE); } else if (TAG_MAX_TIME_TO_UNLOCK.equals(tag)) { - maximumTimeToUnlock = Long.parseLong( - parser.getAttributeValue(null, ATTR_VALUE)); + maximumTimeToUnlock = parser.getAttributeLong(null, ATTR_VALUE); } else if (TAG_STRONG_AUTH_UNLOCK_TIMEOUT.equals(tag)) { - strongAuthUnlockTimeout = Long.parseLong( - parser.getAttributeValue(null, ATTR_VALUE)); + strongAuthUnlockTimeout = parser.getAttributeLong(null, ATTR_VALUE); } else if (TAG_MAX_FAILED_PASSWORD_WIPE.equals(tag)) { - maximumFailedPasswordsForWipe = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + maximumFailedPasswordsForWipe = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_SPECIFIES_GLOBAL_PROXY.equals(tag)) { - specifiesGlobalProxy = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + specifiesGlobalProxy = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_GLOBAL_PROXY_SPEC.equals(tag)) { globalProxySpec = parser.getAttributeValue(null, ATTR_VALUE); @@ -654,48 +640,36 @@ class ActiveAdmin { globalProxyExclusionList = parser.getAttributeValue(null, ATTR_VALUE); } else if (TAG_PASSWORD_EXPIRATION_TIMEOUT.equals(tag)) { - passwordExpirationTimeout = Long.parseLong( - parser.getAttributeValue(null, ATTR_VALUE)); + passwordExpirationTimeout = parser.getAttributeLong(null, ATTR_VALUE); } else if (TAG_PASSWORD_EXPIRATION_DATE.equals(tag)) { - passwordExpirationDate = Long.parseLong( - parser.getAttributeValue(null, ATTR_VALUE)); + passwordExpirationDate = parser.getAttributeLong(null, ATTR_VALUE); } else if (TAG_ENCRYPTION_REQUESTED.equals(tag)) { - encryptionRequested = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + encryptionRequested = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_TEST_ONLY_ADMIN.equals(tag)) { - testOnlyAdmin = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + testOnlyAdmin = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_DISABLE_CAMERA.equals(tag)) { - disableCamera = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + disableCamera = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_DISABLE_CALLER_ID.equals(tag)) { - disableCallerId = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + disableCallerId = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_DISABLE_CONTACTS_SEARCH.equals(tag)) { - disableContactsSearch = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + disableContactsSearch = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_DISABLE_BLUETOOTH_CONTACT_SHARING.equals(tag)) { - disableBluetoothContactSharing = Boolean.parseBoolean(parser - .getAttributeValue(null, ATTR_VALUE)); + disableBluetoothContactSharing = + parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_DISABLE_SCREEN_CAPTURE.equals(tag)) { - disableScreenCapture = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + disableScreenCapture = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_REQUIRE_AUTO_TIME.equals(tag)) { - requireAutoTime = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + requireAutoTime = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_FORCE_EPHEMERAL_USERS.equals(tag)) { - forceEphemeralUsers = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + forceEphemeralUsers = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_IS_NETWORK_LOGGING_ENABLED.equals(tag)) { - isNetworkLoggingEnabled = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); - lastNetworkLoggingNotificationTimeMs = Long.parseLong( - parser.getAttributeValue(null, ATTR_LAST_NETWORK_LOGGING_NOTIFICATION)); - numNetworkLoggingNotifications = Integer.parseInt( - parser.getAttributeValue(null, ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS)); + isNetworkLoggingEnabled = parser.getAttributeBoolean(null, ATTR_VALUE, false); + lastNetworkLoggingNotificationTimeMs = parser.getAttributeLong(null, + ATTR_LAST_NETWORK_LOGGING_NOTIFICATION); + numNetworkLoggingNotifications = parser.getAttributeInt(null, + ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS); } else if (TAG_DISABLE_KEYGUARD_FEATURES.equals(tag)) { - disabledKeyguardFeatures = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + disabledKeyguardFeatures = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_DISABLE_ACCOUNT_MANAGEMENT.equals(tag)) { readAttributeValues( parser, TAG_ACCOUNT_TYPE, accountTypesWithManagementDisabled); @@ -721,7 +695,7 @@ class ActiveAdmin { parser, TAG_RESTRICTION, defaultEnabledRestrictionsAlreadySet); } else if (TAG_SHORT_SUPPORT_MESSAGE.equals(tag)) { type = parser.next(); - if (type == XmlPullParser.TEXT) { + if (type == TypedXmlPullParser.TEXT) { shortSupportMessage = parser.getText(); } else { Log.w(DevicePolicyManagerService.LOG_TAG, @@ -729,7 +703,7 @@ class ActiveAdmin { } } else if (TAG_LONG_SUPPORT_MESSAGE.equals(tag)) { type = parser.next(); - if (type == XmlPullParser.TEXT) { + if (type == TypedXmlPullParser.TEXT) { longSupportMessage = parser.getText(); } else { Log.w(DevicePolicyManagerService.LOG_TAG, @@ -740,22 +714,20 @@ class ActiveAdmin { parentAdmin = new ActiveAdmin(info, /* parent */ true); parentAdmin.readFromXml(parser, shouldOverridePolicies); } else if (TAG_ORGANIZATION_COLOR.equals(tag)) { - organizationColor = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + organizationColor = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_ORGANIZATION_NAME.equals(tag)) { type = parser.next(); - if (type == XmlPullParser.TEXT) { + if (type == TypedXmlPullParser.TEXT) { organizationName = parser.getText(); } else { Log.w(DevicePolicyManagerService.LOG_TAG, "Missing text when loading organization name"); } } else if (TAG_IS_LOGOUT_ENABLED.equals(tag)) { - isLogoutEnabled = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + isLogoutEnabled = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_START_USER_SESSION_MESSAGE.equals(tag)) { type = parser.next(); - if (type == XmlPullParser.TEXT) { + if (type == TypedXmlPullParser.TEXT) { startUserSessionMessage = parser.getText(); } else { Log.w(DevicePolicyManagerService.LOG_TAG, @@ -763,7 +735,7 @@ class ActiveAdmin { } } else if (TAG_END_USER_SESSION_MESSAGE.equals(tag)) { type = parser.next(); - if (type == XmlPullParser.TEXT) { + if (type == TypedXmlPullParser.TEXT) { endUserSessionMessage = parser.getText(); } else { Log.w(DevicePolicyManagerService.LOG_TAG, @@ -779,24 +751,21 @@ class ActiveAdmin { mFactoryResetProtectionPolicy = FactoryResetProtectionPolicy.readFromXml( parser); } else if (TAG_SUSPEND_PERSONAL_APPS.equals(tag)) { - mSuspendPersonalApps = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + mSuspendPersonalApps = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_PROFILE_MAXIMUM_TIME_OFF.equals(tag)) { mProfileMaximumTimeOffMillis = - Long.parseLong(parser.getAttributeValue(null, ATTR_VALUE)); + parser.getAttributeLong(null, ATTR_VALUE); } else if (TAG_PROFILE_OFF_DEADLINE.equals(tag)) { mProfileOffDeadline = - Long.parseLong(parser.getAttributeValue(null, ATTR_VALUE)); + parser.getAttributeLong(null, ATTR_VALUE); } else if (TAG_ALWAYS_ON_VPN_PACKAGE.equals(tag)) { mAlwaysOnVpnPackage = parser.getAttributeValue(null, ATTR_VALUE); } else if (TAG_ALWAYS_ON_VPN_LOCKDOWN.equals(tag)) { - mAlwaysOnVpnLockdown = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + mAlwaysOnVpnLockdown = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_COMMON_CRITERIA_MODE.equals(tag)) { - mCommonCriteriaMode = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + mCommonCriteriaMode = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_PASSWORD_COMPLEXITY.equals(tag)) { - mPasswordComplexity = Integer.parseInt(parser.getAttributeValue(null, ATTR_VALUE)); + mPasswordComplexity = parser.getAttributeInt(null, ATTR_VALUE); } else { Slog.w(DevicePolicyManagerService.LOG_TAG, "Unknown admin tag: " + tag); XmlUtils.skipCurrentTag(parser); @@ -804,14 +773,14 @@ class ActiveAdmin { } } - private List<String> readPackageList(XmlPullParser parser, + private List<String> readPackageList(TypedXmlPullParser parser, String tag) throws XmlPullParserException, IOException { List<String> result = new ArrayList<String>(); int outerDepth = parser.getDepth(); int outerType; - while ((outerType = parser.next()) != XmlPullParser.END_DOCUMENT - && (outerType != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { - if (outerType == XmlPullParser.END_TAG || outerType == XmlPullParser.TEXT) { + while ((outerType = parser.next()) != TypedXmlPullParser.END_DOCUMENT + && (outerType != TypedXmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (outerType == TypedXmlPullParser.END_TAG || outerType == TypedXmlPullParser.TEXT) { continue; } String outerTag = parser.getName(); @@ -832,7 +801,7 @@ class ActiveAdmin { } private void readAttributeValues( - XmlPullParser parser, String tag, Collection<String> result) + TypedXmlPullParser parser, String tag, Collection<String> result) throws XmlPullParserException, IOException { result.clear(); int outerDepthDAM = parser.getDepth(); @@ -854,7 +823,7 @@ class ActiveAdmin { @NonNull private ArrayMap<String, TrustAgentInfo> getAllTrustAgentInfos( - XmlPullParser parser, String tag) throws XmlPullParserException, IOException { + TypedXmlPullParser parser, String tag) throws XmlPullParserException, IOException { int outerDepthDAM = parser.getDepth(); int typeDAM; final ArrayMap<String, TrustAgentInfo> result = new ArrayMap<>(); @@ -876,7 +845,7 @@ class ActiveAdmin { return result; } - private TrustAgentInfo getTrustAgentInfo(XmlPullParser parser, String tag) + private TrustAgentInfo getTrustAgentInfo(TypedXmlPullParser parser, String tag) throws XmlPullParserException, IOException { int outerDepthDAM = parser.getDepth(); int typeDAM; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java index 8a585ec62d00..31ba1991ee72 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java @@ -170,24 +170,19 @@ class DevicePolicyData { } if (policyData.mUserSetupComplete) { if (VERBOSE_LOG) Slog.v(TAG, "setting " + ATTR_SETUP_COMPLETE + " to true"); - out.attribute(null, ATTR_SETUP_COMPLETE, - Boolean.toString(true)); + out.attributeBoolean(null, ATTR_SETUP_COMPLETE, true); } if (policyData.mPaired) { - out.attribute(null, ATTR_DEVICE_PAIRED, - Boolean.toString(true)); + out.attributeBoolean(null, ATTR_DEVICE_PAIRED, true); } if (policyData.mDeviceProvisioningConfigApplied) { - out.attribute(null, ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED, - Boolean.toString(true)); + out.attributeBoolean(null, ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED, true); } if (policyData.mUserProvisioningState != DevicePolicyManager.STATE_USER_UNMANAGED) { - out.attribute(null, ATTR_PROVISIONING_STATE, - Integer.toString(policyData.mUserProvisioningState)); + out.attributeInt(null, ATTR_PROVISIONING_STATE, policyData.mUserProvisioningState); } if (policyData.mPermissionPolicy != DevicePolicyManager.PERMISSION_POLICY_PROMPT) { - out.attribute(null, ATTR_PERMISSION_POLICY, - Integer.toString(policyData.mPermissionPolicy)); + out.attributeInt(null, ATTR_PERMISSION_POLICY, policyData.mPermissionPolicy); } // Serialize delegations. @@ -217,13 +212,13 @@ class DevicePolicyData { if (policyData.mPasswordOwner >= 0) { out.startTag(null, "password-owner"); - out.attribute(null, "value", Integer.toString(policyData.mPasswordOwner)); + out.attributeInt(null, "value", policyData.mPasswordOwner); out.endTag(null, "password-owner"); } if (policyData.mFailedPasswordAttempts != 0) { out.startTag(null, "failed-password-attempts"); - out.attribute(null, "value", Integer.toString(policyData.mFailedPasswordAttempts)); + out.attributeInt(null, "value", policyData.mFailedPasswordAttempts); out.endTag(null, "failed-password-attempts"); } @@ -232,8 +227,7 @@ class DevicePolicyData { // security reasons, we don't want to store the full set of active password metrics. if (isFdeDevice) { out.startTag(null, TAG_PASSWORD_VALIDITY); - out.attribute(null, ATTR_VALUE, - Boolean.toString(policyData.mPasswordValidAtLastCheckpoint)); + out.attributeBoolean(null, ATTR_VALUE, policyData.mPasswordValidAtLastCheckpoint); out.endTag(null, TAG_PASSWORD_VALIDITY); } @@ -252,19 +246,19 @@ class DevicePolicyData { if (policyData.mLockTaskFeatures != DevicePolicyManager.LOCK_TASK_FEATURE_NONE) { out.startTag(null, TAG_LOCK_TASK_FEATURES); - out.attribute(null, ATTR_VALUE, Integer.toString(policyData.mLockTaskFeatures)); + out.attributeInt(null, ATTR_VALUE, policyData.mLockTaskFeatures); out.endTag(null, TAG_LOCK_TASK_FEATURES); } if (policyData.mSecondaryLockscreenEnabled) { out.startTag(null, TAG_SECONDARY_LOCK_SCREEN); - out.attribute(null, ATTR_VALUE, Boolean.toString(true)); + out.attributeBoolean(null, ATTR_VALUE, true); out.endTag(null, TAG_SECONDARY_LOCK_SCREEN); } if (policyData.mStatusBarDisabled) { out.startTag(null, TAG_STATUS_BAR); - out.attribute(null, ATTR_DISABLED, Boolean.toString(policyData.mStatusBarDisabled)); + out.attributeBoolean(null, ATTR_DISABLED, policyData.mStatusBarDisabled); out.endTag(null, TAG_STATUS_BAR); } @@ -281,29 +275,25 @@ class DevicePolicyData { if (policyData.mLastSecurityLogRetrievalTime >= 0) { out.startTag(null, TAG_LAST_SECURITY_LOG_RETRIEVAL); - out.attribute(null, ATTR_VALUE, - Long.toString(policyData.mLastSecurityLogRetrievalTime)); + out.attributeLong(null, ATTR_VALUE, policyData.mLastSecurityLogRetrievalTime); out.endTag(null, TAG_LAST_SECURITY_LOG_RETRIEVAL); } if (policyData.mLastBugReportRequestTime >= 0) { out.startTag(null, TAG_LAST_BUG_REPORT_REQUEST); - out.attribute(null, ATTR_VALUE, - Long.toString(policyData.mLastBugReportRequestTime)); + out.attributeLong(null, ATTR_VALUE, policyData.mLastBugReportRequestTime); out.endTag(null, TAG_LAST_BUG_REPORT_REQUEST); } if (policyData.mLastNetworkLogsRetrievalTime >= 0) { out.startTag(null, TAG_LAST_NETWORK_LOG_RETRIEVAL); - out.attribute(null, ATTR_VALUE, - Long.toString(policyData.mLastNetworkLogsRetrievalTime)); + out.attributeLong(null, ATTR_VALUE, policyData.mLastNetworkLogsRetrievalTime); out.endTag(null, TAG_LAST_NETWORK_LOG_RETRIEVAL); } if (policyData.mAdminBroadcastPending) { out.startTag(null, TAG_ADMIN_BROADCAST_PENDING); - out.attribute(null, ATTR_VALUE, - Boolean.toString(policyData.mAdminBroadcastPending)); + out.attributeBoolean(null, ATTR_VALUE, policyData.mAdminBroadcastPending); out.endTag(null, TAG_ADMIN_BROADCAST_PENDING); } @@ -315,8 +305,7 @@ class DevicePolicyData { if (policyData.mPasswordTokenHandle != 0) { out.startTag(null, TAG_PASSWORD_TOKEN_HANDLE); - out.attribute(null, ATTR_VALUE, - Long.toString(policyData.mPasswordTokenHandle)); + out.attributeLong(null, ATTR_VALUE, policyData.mPasswordTokenHandle); out.endTag(null, TAG_PASSWORD_TOKEN_HANDLE); } @@ -340,7 +329,7 @@ class DevicePolicyData { if (policyData.mAppsSuspended) { out.startTag(null, TAG_APPS_SUSPENDED); - out.attribute(null, ATTR_VALUE, Boolean.toString(policyData.mAppsSuspended)); + out.attributeBoolean(null, ATTR_VALUE, policyData.mAppsSuspended); out.endTag(null, TAG_APPS_SUSPENDED); } @@ -413,13 +402,13 @@ class DevicePolicyData { if (Boolean.toString(true).equals(deviceProvisioningConfigApplied)) { policy.mDeviceProvisioningConfigApplied = true; } - String provisioningState = parser.getAttributeValue(null, ATTR_PROVISIONING_STATE); - if (!TextUtils.isEmpty(provisioningState)) { - policy.mUserProvisioningState = Integer.parseInt(provisioningState); + int provisioningState = parser.getAttributeInt(null, ATTR_PROVISIONING_STATE, -1); + if (provisioningState != -1) { + policy.mUserProvisioningState = provisioningState; } - String permissionPolicy = parser.getAttributeValue(null, ATTR_PERMISSION_POLICY); - if (!TextUtils.isEmpty(permissionPolicy)) { - policy.mPermissionPolicy = Integer.parseInt(permissionPolicy); + int permissionPolicy = parser.getAttributeInt(null, ATTR_PERMISSION_POLICY, -1); + if (permissionPolicy != -1) { + policy.mPermissionPolicy = permissionPolicy; } parser.next(); @@ -472,37 +461,34 @@ class DevicePolicyData { scopes.add(scope); } } else if ("failed-password-attempts".equals(tag)) { - policy.mFailedPasswordAttempts = Integer.parseInt( - parser.getAttributeValue(null, "value")); + policy.mFailedPasswordAttempts = parser.getAttributeInt(null, "value"); } else if ("password-owner".equals(tag)) { - policy.mPasswordOwner = Integer.parseInt( - parser.getAttributeValue(null, "value")); + policy.mPasswordOwner = parser.getAttributeInt(null, "value"); } else if (TAG_ACCEPTED_CA_CERTIFICATES.equals(tag)) { policy.mAcceptedCaCertificates.add(parser.getAttributeValue(null, ATTR_NAME)); } else if (TAG_LOCK_TASK_COMPONENTS.equals(tag)) { policy.mLockTaskPackages.add(parser.getAttributeValue(null, "name")); } else if (TAG_LOCK_TASK_FEATURES.equals(tag)) { - policy.mLockTaskFeatures = Integer.parseInt( - parser.getAttributeValue(null, ATTR_VALUE)); + policy.mLockTaskFeatures = parser.getAttributeInt(null, ATTR_VALUE); } else if (TAG_SECONDARY_LOCK_SCREEN.equals(tag)) { - policy.mSecondaryLockscreenEnabled = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + policy.mSecondaryLockscreenEnabled = + parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_STATUS_BAR.equals(tag)) { - policy.mStatusBarDisabled = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_DISABLED)); + policy.mStatusBarDisabled = + parser.getAttributeBoolean(null, ATTR_DISABLED, false); } else if (TAG_DO_NOT_ASK_CREDENTIALS_ON_BOOT.equals(tag)) { policy.mDoNotAskCredentialsOnBoot = true; } else if (TAG_AFFILIATION_ID.equals(tag)) { policy.mAffiliationIds.add(parser.getAttributeValue(null, ATTR_ID)); } else if (TAG_LAST_SECURITY_LOG_RETRIEVAL.equals(tag)) { - policy.mLastSecurityLogRetrievalTime = Long.parseLong( - parser.getAttributeValue(null, ATTR_VALUE)); + policy.mLastSecurityLogRetrievalTime = + parser.getAttributeLong(null, ATTR_VALUE); } else if (TAG_LAST_BUG_REPORT_REQUEST.equals(tag)) { - policy.mLastBugReportRequestTime = Long.parseLong( - parser.getAttributeValue(null, ATTR_VALUE)); + policy.mLastBugReportRequestTime = + parser.getAttributeLong(null, ATTR_VALUE); } else if (TAG_LAST_NETWORK_LOG_RETRIEVAL.equals(tag)) { - policy.mLastNetworkLogsRetrievalTime = Long.parseLong( - parser.getAttributeValue(null, ATTR_VALUE)); + policy.mLastNetworkLogsRetrievalTime = + parser.getAttributeLong(null, ATTR_VALUE); } else if (TAG_ADMIN_BROADCAST_PENDING.equals(tag)) { String pending = parser.getAttributeValue(null, ATTR_VALUE); policy.mAdminBroadcastPending = Boolean.toString(true).equals(pending); @@ -515,12 +501,11 @@ class DevicePolicyData { } else if (TAG_PASSWORD_VALIDITY.equals(tag)) { if (isFdeDevice) { // This flag is only used for FDE devices - policy.mPasswordValidAtLastCheckpoint = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_VALUE)); + policy.mPasswordValidAtLastCheckpoint = + parser.getAttributeBoolean(null, ATTR_VALUE, false); } } else if (TAG_PASSWORD_TOKEN_HANDLE.equals(tag)) { - policy.mPasswordTokenHandle = Long.parseLong( - parser.getAttributeValue(null, ATTR_VALUE)); + policy.mPasswordTokenHandle = parser.getAttributeLong(null, ATTR_VALUE); } else if (TAG_CURRENT_INPUT_METHOD_SET.equals(tag)) { policy.mCurrentInputMethodSet = true; } else if (TAG_OWNER_INSTALLED_CA_CERT.equals(tag)) { @@ -530,7 +515,7 @@ class DevicePolicyData { parser.getAttributeValue(null, ATTR_NAME)); } else if (TAG_APPS_SUSPENDED.equals(tag)) { policy.mAppsSuspended = - Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_VALUE)); + parser.getAttributeBoolean(null, ATTR_VALUE, false); } else { Slog.w(TAG, "Unknown tag: " + tag); XmlUtils.skipCurrentTag(parser); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 98bb52963725..6f22f1139ba9 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -983,17 +983,29 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void setDevicePolicySafetyChecker(DevicePolicySafetyChecker safetyChecker) { - Slog.i(LOG_TAG, "Setting DevicePolicySafetyChecker as " + safetyChecker.getClass()); + Slog.i(LOG_TAG, "Setting DevicePolicySafetyChecker as " + safetyChecker); mSafetyChecker = safetyChecker; } /** + * Used by {@link OneTimeSafetyChecker} only. + */ + DevicePolicySafetyChecker getDevicePolicySafetyChecker() { + return mSafetyChecker; + } + + /** * Checks if it's safe to execute the given {@code operation}. * * @throws UnsafeStateException if it's not safe to execute the operation. */ private void checkCanExecuteOrThrowUnsafe(@DevicePolicyOperation int operation) { if (!canExecute(operation)) { + if (mSafetyChecker == null) { + // Happens on CTS after it's set just once (by OneTimeSafetyChecker) + throw new UnsafeStateException(operation); + } + // Let mSafetyChecker customize it (for example, by explaining how to retry) throw mSafetyChecker.newUnsafeStateException(operation); } } @@ -1006,6 +1018,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } /** + * Used by {@code cmd device_policy} to set the result of the next safety operation check. + */ + void setNextOperationSafety(@DevicePolicyOperation int operation, boolean safe) { + Preconditions.checkCallAuthorization( + hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS)); + Slog.i(LOG_TAG, "setNextOperationSafety(" + DevicePolicyManager.operationToString(operation) + + ", " + safe + ")"); + mSafetyChecker = new OneTimeSafetyChecker(this, operation, safe); + } + + /** * Unit test will subclass it to inject mocks. */ @VisibleForTesting diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java index 0b0aee9afa6d..22866b45911d 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java @@ -24,6 +24,7 @@ import java.util.Objects; final class DevicePolicyManagerServiceShellCommand extends ShellCommand { private static final String CMD_IS_SAFE_OPERATION = "is-operation-safe"; + private static final String CMD_SET_SAFE_OPERATION = "set-operation-safe"; private final DevicePolicyManagerService mService; @@ -48,6 +49,8 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { switch (cmd) { case CMD_IS_SAFE_OPERATION: return runIsSafeOperation(pw); + case CMD_SET_SAFE_OPERATION: + return runSetSafeOperation(pw); default: return onInvalidCommand(pw, cmd); } @@ -70,6 +73,9 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { pw.printf(" Prints this help text.\n\n"); pw.printf(" %s <OPERATION_ID>\n", CMD_IS_SAFE_OPERATION); pw.printf(" Checks if the give operation is safe \n\n"); + pw.printf(" %s <OPERATION_ID> <true|false>\n", CMD_SET_SAFE_OPERATION); + pw.printf(" Emulates the result of the next call to check if the given operation is safe" + + " \n\n"); } private int runIsSafeOperation(PrintWriter pw) { @@ -79,4 +85,13 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { safe ? "SAFE" : "UNSAFE"); return 0; } + + private int runSetSafeOperation(PrintWriter pw) { + int operation = Integer.parseInt(getNextArgRequired()); + boolean safe = getNextArg().equals("true"); + mService.setNextOperationSafety(operation, safe); + pw.printf("Next call to check operation %s will return %s\n", + DevicePolicyManager.operationToString(operation), safe ? "SAFE" : "UNSAFE"); + return 0; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java new file mode 100644 index 000000000000..b0f8bfb713ab --- /dev/null +++ b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.devicepolicy; + +import static android.app.admin.DevicePolicyManager.operationToString; + +import android.app.admin.DevicePolicyManager.DevicePolicyOperation; +import android.app.admin.DevicePolicySafetyChecker; +import android.util.Slog; + +import java.util.Objects; + +//TODO(b/172376923): add unit tests + +/** + * {@code DevicePolicySafetyChecker} implementation that overrides the real checker for just + * one command. + * + * <p>Used only for debugging and CTS tests. + */ +final class OneTimeSafetyChecker implements DevicePolicySafetyChecker { + + private static final String TAG = OneTimeSafetyChecker.class.getSimpleName(); + + private final DevicePolicyManagerService mService; + private final DevicePolicySafetyChecker mRealSafetyChecker; + private final @DevicePolicyOperation int mOperation; + private final boolean mSafe; + + OneTimeSafetyChecker(DevicePolicyManagerService service, + @DevicePolicyOperation int operation, boolean safe) { + mService = Objects.requireNonNull(service); + mOperation = operation; + mSafe = safe; + mRealSafetyChecker = service.getDevicePolicySafetyChecker(); + Slog.i(TAG, "Saving real DevicePolicySafetyChecker as " + mRealSafetyChecker); + } + + @Override + public boolean isDevicePolicyOperationSafe(@DevicePolicyOperation int operation) { + String name = operationToString(operation); + boolean safe = true; + if (operation == mOperation) { + safe = mSafe; + } else { + Slog.wtf(TAG, "invalid call to isDevicePolicyOperationSafe(): asked for " + name + + ", should be " + operationToString(mOperation)); + } + Slog.i(TAG, "isDevicePolicyOperationSafe(" + name + "): returning " + safe + + " and restoring DevicePolicySafetyChecker to " + mRealSafetyChecker); + mService.setDevicePolicySafetyChecker(mRealSafetyChecker); + return safe; + } +} diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java index 8ef69822750f..79a82b8e7175 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java @@ -43,22 +43,18 @@ import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.FastXmlSerializer; import com.android.server.LocalServices; import com.android.server.pm.UserManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; import libcore.io.IoUtils; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.StandardCharsets; import java.time.LocalDate; import java.util.List; import java.util.Map; @@ -558,11 +554,10 @@ class Owners { } try { InputStream input = new AtomicFile(file).openRead(); - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(input, StandardCharsets.UTF_8.name()); + TypedXmlPullParser parser = Xml.resolvePullParser(input); int type; - while ((type=parser.next()) != XmlPullParser.END_DOCUMENT) { - if (type!=XmlPullParser.START_TAG) { + while ((type=parser.next()) != TypedXmlPullParser.END_DOCUMENT) { + if (type!=TypedXmlPullParser.START_TAG) { continue; } @@ -581,7 +576,7 @@ class Owners { String profileOwnerName = parser.getAttributeValue(null, ATTR_NAME); String profileOwnerComponentStr = parser.getAttributeValue(null, ATTR_COMPONENT_NAME); - int userId = Integer.parseInt(parser.getAttributeValue(null, ATTR_USERID)); + int userId = parser.getAttributeInt(null, ATTR_USERID); OwnerInfo profileOwnerInfo = null; if (profileOwnerComponentStr != null) { ComponentName admin = ComponentName.unflattenFromString( @@ -781,12 +776,12 @@ class Owners { int type; int depth = 0; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { + while ((type = parser.next()) != TypedXmlPullParser.END_DOCUMENT) { switch (type) { - case XmlPullParser.START_TAG: + case TypedXmlPullParser.START_TAG: depth++; break; - case XmlPullParser.END_TAG: + case TypedXmlPullParser.END_TAG: depth--; // fallthrough default: @@ -813,9 +808,9 @@ class Owners { } } - abstract void writeInner(XmlSerializer out) throws IOException; + abstract void writeInner(TypedXmlSerializer out) throws IOException; - abstract boolean readInner(XmlPullParser parser, int depth, String tag); + abstract boolean readInner(TypedXmlPullParser parser, int depth, String tag); } private class DeviceOwnerReadWriter extends FileReadWriter { @@ -831,11 +826,11 @@ class Owners { } @Override - void writeInner(XmlSerializer out) throws IOException { + void writeInner(TypedXmlSerializer out) throws IOException { if (mDeviceOwner != null) { mDeviceOwner.writeToXml(out, TAG_DEVICE_OWNER); out.startTag(null, TAG_DEVICE_OWNER_CONTEXT); - out.attribute(null, ATTR_USERID, String.valueOf(mDeviceOwnerUserId)); + out.attributeInt(null, ATTR_USERID, mDeviceOwnerUserId); out.endTag(null, TAG_DEVICE_OWNER_CONTEXT); } @@ -863,7 +858,7 @@ class Owners { } @Override - boolean readInner(XmlPullParser parser, int depth, String tag) { + boolean readInner(TypedXmlPullParser parser, int depth, String tag) { if (depth > 2) { return true; // Ignore } @@ -873,13 +868,8 @@ class Owners { mDeviceOwnerUserId = UserHandle.USER_SYSTEM; // Set default break; case TAG_DEVICE_OWNER_CONTEXT: { - final String userIdString = - parser.getAttributeValue(null, ATTR_USERID); - try { - mDeviceOwnerUserId = Integer.parseInt(userIdString); - } catch (NumberFormatException e) { - Slog.e(TAG, "Error parsing user-id " + userIdString); - } + mDeviceOwnerUserId = parser.getAttributeInt(null, ATTR_USERID, + mDeviceOwnerUserId); break; } case TAG_DEVICE_INITIALIZER: @@ -927,7 +917,7 @@ class Owners { } @Override - void writeInner(XmlSerializer out) throws IOException { + void writeInner(TypedXmlSerializer out) throws IOException { final OwnerInfo profileOwner = mProfileOwners.get(mUserId); if (profileOwner != null) { profileOwner.writeToXml(out, TAG_PROFILE_OWNER); @@ -935,7 +925,7 @@ class Owners { } @Override - boolean readInner(XmlPullParser parser, int depth, String tag) { + boolean readInner(TypedXmlPullParser parser, int depth, String tag) { if (depth > 2) { return true; // Ignore } @@ -985,7 +975,7 @@ class Owners { this.isOrganizationOwnedDevice = isOrganizationOwnedDevice; } - public void writeToXml(XmlSerializer out, String tag) throws IOException { + public void writeToXml(TypedXmlSerializer out, String tag) throws IOException { out.startTag(null, tag); out.attribute(null, ATTR_PACKAGE, packageName); if (name != null) { @@ -994,8 +984,7 @@ class Owners { if (admin != null) { out.attribute(null, ATTR_COMPONENT_NAME, admin.flattenToString()); } - out.attribute(null, ATTR_USER_RESTRICTIONS_MIGRATED, - String.valueOf(userRestrictionsMigrated)); + out.attributeBoolean(null, ATTR_USER_RESTRICTIONS_MIGRATED, userRestrictionsMigrated); if (remoteBugreportUri != null) { out.attribute(null, ATTR_REMOTE_BUGREPORT_URI, remoteBugreportUri); } @@ -1003,13 +992,13 @@ class Owners { out.attribute(null, ATTR_REMOTE_BUGREPORT_HASH, remoteBugreportHash); } if (isOrganizationOwnedDevice) { - out.attribute(null, ATTR_PROFILE_OWNER_OF_ORG_OWNED_DEVICE, - String.valueOf(isOrganizationOwnedDevice)); + out.attributeBoolean(null, ATTR_PROFILE_OWNER_OF_ORG_OWNED_DEVICE, + isOrganizationOwnedDevice); } out.endTag(null, tag); } - public static OwnerInfo readFromXml(XmlPullParser parser) { + public static OwnerInfo readFromXml(TypedXmlPullParser parser) { final String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); final String name = parser.getAttributeValue(null, ATTR_NAME); final String componentName = diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java index 58ece0748d8b..289ed364e2a3 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java @@ -29,18 +29,15 @@ import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.Objects; /** @@ -106,7 +103,7 @@ class TransferOwnershipMetadataManager { return false; } - private void insertSimpleTag(XmlSerializer serializer, String tagName, String value) + private void insertSimpleTag(TypedXmlSerializer serializer, String tagName, String value) throws IOException { serializer.startTag(null, tagName); serializer.text(value); @@ -132,7 +129,7 @@ class TransferOwnershipMetadataManager { return null; } - private Metadata parseMetadataFile(XmlPullParser parser) + private Metadata parseMetadataFile(TypedXmlPullParser parser) throws XmlPullParserException, IOException { int type; final int outerDepth = parser.getDepth(); diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index 8fc5c085999e..03083c1f00b2 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -15,7 +15,7 @@ android_test { name: "FrameworksMockingServicesTests", - srcs: ["src/**/*.java"], + srcs: ["src/**/*.java", "src/**/*.kt"], static_libs: [ "services.core", @@ -29,6 +29,10 @@ android_test { "mockito-target-extended-minus-junit4", "platform-test-annotations", "truth-prebuilt", + "hamcrest-library", + "servicestests-utils-mockito-extended", + "mockingservicestests-utils-mockito", + "servicestests-core-utils", "testables", // TODO: remove once Android migrates to JUnit 4.12, which provides assertThrows "testng", @@ -54,3 +58,17 @@ android_test { enabled: false, }, } + +java_library { + name: "mockingservicestests-utils-mockito", + srcs: [ + "utils-mockito/**/*.kt", + ], + static_libs: [ + "junit", + "mockito-target-extended-minus-junit4", + ], + libs: [ + "android.test.runner", + ], +}
\ No newline at end of file diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt new file mode 100644 index 000000000000..edae08a3bfe1 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt @@ -0,0 +1,662 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.pm + +import android.content.Context +import android.content.Intent +import android.content.pm.ActivityInfo +import android.content.pm.ApplicationInfo +import android.content.pm.FallbackCategoryProvider +import android.content.pm.FeatureInfo +import android.content.pm.PackageParser.SigningDetails +import android.content.pm.ResolveInfo +import android.content.pm.ServiceInfo +import android.content.pm.Signature +import android.content.pm.UserInfo +import android.content.pm.parsing.ParsingPackage +import android.content.pm.parsing.ParsingPackageUtils +import android.content.res.Resources +import android.hardware.display.DisplayManager +import android.os.Build +import android.os.Environment +import android.os.SystemProperties +import android.os.UserHandle +import android.os.UserManager +import android.os.incremental.IncrementalManager +import android.permission.IPermissionManager +import android.util.ArrayMap +import android.util.DisplayMetrics +import android.util.EventLog +import android.view.Display +import com.android.dx.mockito.inline.extended.ExtendedMockito +import com.android.dx.mockito.inline.extended.ExtendedMockito.any +import com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean +import com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt +import com.android.dx.mockito.inline.extended.ExtendedMockito.anyString +import com.android.dx.mockito.inline.extended.ExtendedMockito.argThat +import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn +import com.android.dx.mockito.inline.extended.ExtendedMockito.eq +import com.android.dx.mockito.inline.extended.ExtendedMockito.spy +import com.android.dx.mockito.inline.extended.StaticMockitoSession +import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder +import com.android.internal.R +import com.android.server.LocalServices +import com.android.server.LockGuard +import com.android.server.SystemConfig +import com.android.server.SystemServerInitThreadPool +import com.android.server.compat.PlatformCompat +import com.android.server.extendedtestutils.wheneverStatic +import com.android.server.pm.dex.DexManager +import com.android.server.pm.parsing.PackageParser2 +import com.android.server.pm.parsing.pkg.AndroidPackage +import com.android.server.pm.parsing.pkg.PackageImpl +import com.android.server.pm.parsing.pkg.ParsedPackage +import com.android.server.pm.permission.PermissionManagerServiceInternal +import com.android.server.testutils.mock +import com.android.server.testutils.nullable +import com.android.server.testutils.whenever +import org.junit.Assert +import org.junit.rules.TestRule +import org.junit.runner.Description +import org.junit.runners.model.Statement +import org.mockito.AdditionalMatchers.or +import org.mockito.invocation.InvocationOnMock +import org.mockito.quality.Strictness +import java.io.File +import java.io.IOException +import java.nio.file.Files +import java.security.PublicKey +import java.security.cert.CertificateException +import java.util.Arrays +import java.util.Random +import java.util.concurrent.FutureTask + +/** + * A utility for mocking behavior of the system and dependencies when testing PackageManagerService + * + * Create one of these and call [stageNominalSystemState] as a basis for additional behavior in most + * tests. + */ +class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { + private val random = Random() + val mocks = Mocks() + val packageCacheDirectory: File = + Files.createTempDirectory("packageCache").toFile() + val rootDirectory: File = + Files.createTempDirectory("root").toFile() + val dataAppDirectory: File = + File(Files.createTempDirectory("data").toFile(), "app") + val frameworkSignature: SigningDetails = SigningDetails(arrayOf(generateSpySignature()), 3) + val systemPartitions: List<PackageManagerService.ScanPartition> = + redirectScanPartitions(PackageManagerService.SYSTEM_PARTITIONS) + val session: StaticMockitoSession + + /** Tracks temporary files created by this class during the running of a test. */ + private val createdFiles = ArrayList<File>() + + /** Settings that are expected to be added as part of the test */ + private val mPendingPackageAdds: MutableList<Pair<String, PackageSetting>> = ArrayList() + + /** Settings simulated to be stored on disk */ + private val mPreExistingSettings = ArrayMap<String, PackageSetting>() + + /** The active map simulating the in memory storage of Settings */ + private val mSettingsMap = ArrayMap<String, PackageSetting>() + + init { + val apply = ExtendedMockito.mockitoSession() + .strictness(Strictness.LENIENT) + .mockStatic(SystemProperties::class.java) + .mockStatic(SystemConfig::class.java) + .mockStatic(SELinuxMMAC::class.java) + .mockStatic(FallbackCategoryProvider::class.java) + .mockStatic(PackageManagerServiceUtils::class.java) + .mockStatic(Environment::class.java) + .mockStatic(SystemServerInitThreadPool::class.java) + .mockStatic(ParsingPackageUtils::class.java) + .mockStatic(LockGuard::class.java) + .mockStatic(EventLog::class.java) + .mockStatic(LocalServices::class.java) + .apply(withSession) + session = apply.startMocking() + whenever(mocks.settings.insertPackageSettingLPw( + any(PackageSetting::class.java), any(AndroidPackage::class.java))) { + val name: String = (getArgument<Any>(0) as PackageSetting).name + val pendingAdd = + mPendingPackageAdds.firstOrNull { it.first == name } ?: return@whenever null + mPendingPackageAdds.remove(pendingAdd) + mSettingsMap[name] = pendingAdd.second + null + } + whenever(mocks.settings.addPackageLPw(nullable(), nullable(), nullable(), nullable(), + nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), + nullable(), nullable(), nullable())) { + val name: String = getArgument(0) + val pendingAdd = mPendingPackageAdds.firstOrNull { it.first == name } + ?: return@whenever null + mPendingPackageAdds.remove(pendingAdd) + mSettingsMap[name] = pendingAdd.second + pendingAdd.second + } + whenever(mocks.settings.packagesLocked).thenReturn(mSettingsMap) + whenever(mocks.settings.getPackageLPr(anyString())) { mSettingsMap[getArgument<Any>(0)] } + whenever(mocks.settings.readLPw(nullable())) { + mSettingsMap.putAll(mPreExistingSettings) + !mPreExistingSettings.isEmpty() + } + } + + /** Collection of mocks used for PackageManagerService tests. */ + + class Mocks { + val lock = Any() + val installLock = Any() + val injector: PackageManagerService.Injector = mock() + val systemWrapper: PackageManagerService.SystemWrapper = mock() + val context: Context = mock() + val userManagerService: UserManagerService = mock() + val componentResolver: ComponentResolver = mock() + val permissionManagerInternal: PermissionManagerServiceInternal = mock() + val incrementalManager: IncrementalManager = mock() + val platformCompat: PlatformCompat = mock() + val settings: Settings = mock() + val resources: Resources = mock() + val systemConfig: SystemConfig = mock() + val apexManager: ApexManager = mock() + val userManagerInternal: UserManagerInternal = mock() + val packageParser: PackageParser2 = mock() + val keySetManagerService: KeySetManagerService = mock() + val packageAbiHelper: PackageAbiHelper = mock() + val appsFilter: AppsFilter = mock() + val dexManager: DexManager = mock() + val installer: Installer = mock() + val displayMetrics: DisplayMetrics = mock() + val permissionManager: IPermissionManager = mock() + } + + companion object { + private const val DEVICE_PROVISIONING_PACKAGE_NAME = + "com.example.android.device.provisioning" + private val DEFAULT_AVAILABLE_FEATURES_MAP = ArrayMap<String, FeatureInfo>() + private val DEFAULT_ACTIVE_APEX_INFO_LIST = emptyList<ApexManager.ActiveApexInfo>() + private val DEFAULT_SHARED_LIBRARIES_LIST = + ArrayMap<String, SystemConfig.SharedLibraryEntry>() + private val DEFAULT_USERS = Arrays.asList( + UserInfo(UserHandle.USER_SYSTEM, "primary", "", + UserInfo.FLAG_PRIMARY or UserInfo.FLAG_SYSTEM or UserInfo.FLAG_FULL, + UserManager.USER_TYPE_FULL_SYSTEM)) + public val DEFAULT_VERSION_INFO = Settings.VersionInfo() + + init { + DEFAULT_VERSION_INFO.fingerprint = "abcdef" + DEFAULT_VERSION_INFO.sdkVersion = Build.VERSION_CODES.R + DEFAULT_VERSION_INFO.databaseVersion = Settings.CURRENT_DATABASE_VERSION + } + } + + /** + * Clean up any potentially dangling state. This should be run at the end of every test to + * account for changes to static memory, such as [LocalServices] + */ + fun cleanup() { + createdFiles.forEach(File::delete) + createdFiles.clear() + mSettingsMap.clear() + mPendingPackageAdds.clear() + mPreExistingSettings.clear() + session.finishMocking() + } + + /** + * Run this method to ensure that all expected actions were executed, such as pending + * [Settings] adds. + */ + fun validateFinalState() { + if (mPendingPackageAdds.isNotEmpty()) { + Assert.fail( + "Not all expected settings were added: ${mPendingPackageAdds.map { it.first }}") + } + } + + /** + * This method stages enough of system startup to execute the PackageManagerService constructor + * successfullly. + */ + @Throws(Exception::class) + fun stageNominalSystemState() { + whenever(mocks.injector.context).thenReturn(mocks.context) + whenever(mocks.injector.lock).thenReturn(mocks.lock) + whenever(mocks.injector.installLock).thenReturn(mocks.installLock) + whenever(mocks.injector.systemWrapper).thenReturn(mocks.systemWrapper) + whenever(mocks.injector.userManagerService).thenReturn(mocks.userManagerService) + whenever(mocks.injector.componentResolver).thenReturn(mocks.componentResolver) + whenever(mocks.injector.permissionManagerServiceInternal) { + mocks.permissionManagerInternal + } + whenever(mocks.injector.permissionManagerService).thenReturn(mocks.permissionManager) + whenever(mocks.injector.incrementalManager).thenReturn(mocks.incrementalManager) + whenever(mocks.injector.compatibility).thenReturn(mocks.platformCompat) + whenever(mocks.injector.settings).thenReturn(mocks.settings) + whenever(mocks.injector.dexManager).thenReturn(mocks.dexManager) + whenever(mocks.injector.systemConfig).thenReturn(mocks.systemConfig) + whenever(mocks.injector.apexManager).thenReturn(mocks.apexManager) + whenever(mocks.injector.scanningCachingPackageParser).thenReturn(mocks.packageParser) + whenever(mocks.injector.scanningPackageParser).thenReturn(mocks.packageParser) + whenever(mocks.injector.systemPartitions).thenReturn(systemPartitions) + whenever(mocks.injector.appsFilter).thenReturn(mocks.appsFilter) + whenever(mocks.injector.abiHelper).thenReturn(mocks.packageAbiHelper) + whenever(mocks.injector.userManagerInternal).thenReturn(mocks.userManagerInternal) + whenever(mocks.injector.installer).thenReturn(mocks.installer) + whenever(mocks.injector.displayMetrics).thenReturn(mocks.displayMetrics) + wheneverStatic { SystemConfig.getInstance() }.thenReturn(mocks.systemConfig) + whenever(mocks.systemConfig.availableFeatures).thenReturn(DEFAULT_AVAILABLE_FEATURES_MAP) + whenever(mocks.systemConfig.sharedLibraries).thenReturn(DEFAULT_SHARED_LIBRARIES_LIST) + wheneverStatic { SystemProperties.getBoolean("fw.free_cache_v2", true) }.thenReturn(true) + wheneverStatic { Environment.getPackageCacheDirectory() }.thenReturn(packageCacheDirectory) + wheneverStatic { SystemProperties.digestOf("ro.build.fingerprint") }.thenReturn("cacheName") + wheneverStatic { Environment.getRootDirectory() }.thenReturn(rootDirectory) + wheneverStatic { SystemServerInitThreadPool.submit(any(Runnable::class.java), anyString())} + .thenAnswer { FutureTask<Any?>(it.getArgument(0), null) } + + wheneverStatic { Environment.getDataDirectory() }.thenReturn(dataAppDirectory.parentFile) + wheneverStatic { Environment.getDataSystemDirectory() } + .thenReturn(File(dataAppDirectory.parentFile, "system")) + whenever(mocks.context.resources).thenReturn(mocks.resources) + whenever(mocks.resources.getString(R.string.config_deviceProvisioningPackage)) { + DEVICE_PROVISIONING_PACKAGE_NAME + } + whenever(mocks.apexManager.activeApexInfos).thenReturn(DEFAULT_ACTIVE_APEX_INFO_LIST) + whenever(mocks.settings.packagesLocked).thenReturn(mSettingsMap) + whenever(mocks.settings.internalVersion).thenReturn(DEFAULT_VERSION_INFO) + whenever(mocks.settings.keySetManagerService).thenReturn(mocks.keySetManagerService) + whenever(mocks.settings.keySetManagerService).thenReturn(mocks.keySetManagerService) + whenever(mocks.packageAbiHelper.derivePackageAbi( + any(AndroidPackage::class.java), anyBoolean(), nullable(), any(File::class.java))) { + android.util.Pair(PackageAbiHelper.Abis("", ""), + PackageAbiHelper.NativeLibraryPaths("", false, "", "")) + } + whenever(mocks.userManagerInternal.getUsers(true, false, false)).thenReturn(DEFAULT_USERS) + whenever(mocks.userManagerService.userIds).thenReturn(intArrayOf(0)) + whenever(mocks.userManagerService.exists(0)).thenReturn(true) + whenever(mocks.packageAbiHelper.deriveNativeLibraryPaths( + any(AndroidPackage::class.java), anyBoolean(), any(File::class.java))) { + PackageAbiHelper.NativeLibraryPaths("", false, "", "") + } + // everything visible by default + whenever(mocks.appsFilter.shouldFilterApplication( + anyInt(), nullable(), nullable(), anyInt())) { false } + + val displayManager: DisplayManager = mock() + whenever(mocks.context.getSystemService(DisplayManager::class.java)) + .thenReturn(displayManager) + val display: Display = mock() + whenever(displayManager.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(display) + + stageFrameworkScan() + stageInstallerScan() + stageServicesExtensionScan() + stageSystemSharedLibraryScan() + stagePermissionsControllerScan() + stageInstantAppResolverScan() + } + + /** + * This method will stage the parsing and scanning of a package as well as add it to the + * [PackageSetting]s read from disk. + */ + @Throws(Exception::class) + fun stageScanExistingPackage( + packageName: String, + versionCode: Long, + parent: File?, + withPackage: (PackageImpl) -> PackageImpl = { it }, + withSetting: + (PackageSettingBuilder) -> PackageSettingBuilder = { it }, + withExistingSetting: + (PackageSettingBuilder) -> PackageSettingBuilder = { it } + ) { + val existingSettingBuilderRef = arrayOfNulls<PackageSettingBuilder>(1) + stageScanNewPackage(packageName, versionCode, parent, withPackage, + withSetting = { settingBuilder -> + withSetting(settingBuilder) + existingSettingBuilderRef[0] = settingBuilder + settingBuilder + }) + existingSettingBuilderRef[0]?.setPackage(null) + val packageSetting = existingSettingBuilderRef[0]?.let { withExistingSetting(it) }!!.build() + addPreExistingSetting(packageName, packageSetting) + } + + /** + * This method will stage a [PackageSetting] read from disk, but does not stage any scanning + * or parsing of the package. + */ + fun addPreExistingSetting(packageName: String, packageSetting: PackageSetting) { + mPreExistingSettings[packageName] = packageSetting + } + + /** + * This method will stage the parsing and scanning of a package but will not add it to the set + * of [PackageSetting]s read from disk. + */ + @Throws(Exception::class) + fun stageScanNewPackage( + packageName: String, + versionCode: Long, + parent: File?, + withPackage: (PackageImpl) -> PackageImpl = { it }, + withSetting: (PackageSettingBuilder) -> PackageSettingBuilder = { it } + ) { + val pair = createBasicAndroidPackage(parent, packageName, versionCode) + val apkPath = pair.first + val pkg = withPackage(pair.second) + stageParse(apkPath, pkg) + val parentFile = apkPath.parentFile + val settingBuilder = withSetting(createBasicSettingBuilder(parentFile, pkg)) + stageSettingInsert(packageName, settingBuilder.build()) + } + + /** + * Creates a simple package that should reasonably parse for scan operations. This can be used + * as a basis for more complicated packages. + */ + fun createBasicAndroidPackage( + parent: File?, + packageName: String, + versionCode: Long, + signingDetails: SigningDetails = + createRandomSigningDetails() + ): Pair<File, PackageImpl> { + val apkPath = File(File(parent, packageName), "base.apk") + val pkg = PackageImpl.forTesting(packageName, apkPath.parentFile.path) as PackageImpl + pkg.signingDetails = signingDetails + wheneverStatic { ParsingPackageUtils.getSigningDetails(eq(pkg), anyBoolean()) } + .thenReturn(signingDetails) + pkg.versionCode = versionCode.toInt() + pkg.versionCodeMajor = (versionCode shr 32).toInt() + pkg.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT + return Pair(apkPath, pkg) + } + + /** + * This method will create a spy of a [SigningDetails] object to be used when simulating the + * collection of signatures. + */ + fun createRandomSigningDetails(): SigningDetails { + val signingDetails = spy(SigningDetails(arrayOf(generateSpySignature()), + SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3)) + doReturn(true).whenever(signingDetails).checkCapability( + anyString(), anyInt()) + doReturn(true).whenever(signingDetails).checkCapability( + any(SigningDetails::class.java), anyInt()) + return signingDetails + } + + /** + * This method will create a basic [PackageSettingBuilder] from an [AndroidPackage] with all of + * the necessary parameters to be returned by a simple scan. This can be used as a basis for + * more complicated settings. + */ + fun createBasicSettingBuilder(parentFile: File, pkg: AndroidPackage): PackageSettingBuilder { + return createBasicSettingBuilder(parentFile, pkg.packageName, pkg.longVersionCode, + pkg.signingDetails) + .setPackage(pkg) + } + + /** + * This method will create a basic [PackageSettingBuilder] with all of the necessary parameters + * to be returned by a simple scan. This can be used as a basis for more complicated settings. + */ + fun createBasicSettingBuilder( + parentFile: File, + packageName: String, + versionCode: Long, + signingDetails: SigningDetails + ): PackageSettingBuilder { + return PackageSettingBuilder() + .setCodePath(parentFile.path) + .setName(packageName) + .setPVersionCode(versionCode) + .setSigningDetails(signingDetails) + } + + fun createBasicApplicationInfo(pkg: ParsingPackage): ApplicationInfo { + val applicationInfo: ApplicationInfo = mock() + applicationInfo.packageName = pkg.packageName + return applicationInfo + } + + fun createBasicActivityInfo( + pkg: ParsingPackage, + applicationInfo: ApplicationInfo?, + className: String? + ): + ActivityInfo { + val activityInfo = ActivityInfo() + activityInfo.applicationInfo = applicationInfo + activityInfo.packageName = pkg.packageName + activityInfo.name = className + return activityInfo + } + + fun createBasicServiceInfo( + pkg: ParsingPackage, + applicationInfo: ApplicationInfo?, + className: String? + ): + ServiceInfo { + val serviceInfo = ServiceInfo() + serviceInfo.applicationInfo = applicationInfo + serviceInfo.packageName = pkg.packageName + serviceInfo.name = className + return serviceInfo + } + + /** Finds the appropriate partition, if available, based on a scan flag unique to it. */ + fun getPartitionFromFlag(scanFlagMask: Int): PackageManagerService.ScanPartition = + systemPartitions.first { (it.scanFlag and scanFlagMask) != 0 } + + @Throws(Exception::class) + private fun stageParse(path: File, parseResult: ParsingPackage): ParsedPackage { + val basePath = path.parentFile + basePath.mkdirs() + path.createNewFile() + createdFiles.add(path) + val parsedPackage = parseResult.hideAsParsed() as ParsedPackage + whenever(mocks.packageParser.parsePackage( + or(eq(path), eq(basePath)), anyInt(), anyBoolean())) { parsedPackage } + return parsedPackage + } + + private fun stageSettingInsert(name: String, setting: PackageSetting): PackageSetting { + mPendingPackageAdds.add(Pair(name, setting)) + return setting + } + + @Throws(Exception::class) + private fun stageFrameworkScan() { + val apk = File(File(rootDirectory, "framework"), "framework-res.apk") + val frameworkPkg = PackageImpl.forTesting("android", + apk.parentFile.path) as PackageImpl + wheneverStatic { ParsingPackageUtils.getSigningDetails(frameworkPkg, true) } + .thenReturn(frameworkSignature) + stageParse(apk, frameworkPkg) + stageSettingInsert("android", + PackageSettingBuilder().setCodePath(apk.path).setName( + "android").setPackage(frameworkPkg).build()) + } + + @Throws(Exception::class) + private fun stageInstantAppResolverScan() { + whenever(mocks.resources.getStringArray(R.array.config_ephemeralResolverPackage)) { + arrayOf("com.android.test.ephemeral.resolver") + } + stageScanNewPackage("com.android.test.ephemeral.resolver", + 1L, getPartitionFromFlag(PackageManagerService.SCAN_AS_PRODUCT).privAppFolder, + withPackage = { pkg: PackageImpl -> + val applicationInfo: ApplicationInfo = createBasicApplicationInfo(pkg) + whenever(applicationInfo.isPrivilegedApp).thenReturn(true) + mockQueryServices(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE, + createBasicServiceInfo(pkg, applicationInfo, "test.EphemeralService")) + mockQueryActivities(Intent.ACTION_INSTANT_APP_RESOLVER_SETTINGS, + createBasicActivityInfo(pkg, applicationInfo, "test.SettingsActivity")) + pkg + }, + withSetting = { setting: PackageSettingBuilder -> + setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM) + }) + } + + @Throws(Exception::class) + private fun stagePermissionsControllerScan() { + stageScanNewPackage("com.android.permissions.controller", + 1L, systemPartitions[0].privAppFolder, + withPackage = { pkg: PackageImpl -> + val applicationInfo: ApplicationInfo = createBasicApplicationInfo(pkg) + whenever(applicationInfo.isPrivilegedApp).thenReturn(true) + mockQueryActivities(Intent.ACTION_MANAGE_PERMISSIONS, + createBasicActivityInfo( + pkg, applicationInfo, "test.PermissionActivity")) + pkg + }, + withSetting = { setting: PackageSettingBuilder -> + setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM) + }) + } + + @Throws(Exception::class) + private fun stageSystemSharedLibraryScan() { + stageScanNewPackage("android.ext.shared", + 1L, systemPartitions[0].appFolder, + withPackage = { it.addLibraryName("android.ext.shared") as PackageImpl }, + withSetting = { setting: PackageSettingBuilder -> + setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM) + } + ) + } + + @Throws(Exception::class) + private fun stageServicesExtensionScan() { + whenever(mocks.context.getString(R.string.config_servicesExtensionPackage)) { + "com.android.test.services.extension" + } + stageScanNewPackage("com.android.test.services.extension", + 1L, getPartitionFromFlag(PackageManagerService.SCAN_AS_SYSTEM_EXT).privAppFolder, + withSetting = { setting: PackageSettingBuilder -> + setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM) + }) + } + + @Throws(Exception::class) + private fun stageInstallerScan() { + stageScanNewPackage( + "com.android.test.installer", + 1L, getPartitionFromFlag(PackageManagerService.SCAN_AS_PRODUCT).privAppFolder, + withPackage = { pkg: PackageImpl -> + val applicationInfo: ApplicationInfo = createBasicApplicationInfo(pkg) + whenever(applicationInfo.isPrivilegedApp).thenReturn(true) + val installerActivity: ActivityInfo = createBasicActivityInfo( + pkg, applicationInfo, "test.InstallerActivity") + mockQueryActivities(Intent.ACTION_INSTALL_PACKAGE, installerActivity) + mockQueryActivities(Intent.ACTION_UNINSTALL_PACKAGE, installerActivity) + mockQueryActivities(Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE, + installerActivity) + pkg + }, + withSetting = { setting: PackageSettingBuilder -> + setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM) + } + ) + } + + private fun mockQueryActivities(action: String, vararg activities: ActivityInfo) { + whenever(mocks.componentResolver.queryActivities( + argThat { intent: Intent? -> intent != null && (action == intent.action) }, + nullable(), anyInt(), anyInt())) { + ArrayList(activities.asList().map { info: ActivityInfo? -> + ResolveInfo().apply { activityInfo = info } + }) + } + } + + private fun mockQueryServices(action: String, vararg services: ServiceInfo) { + whenever(mocks.componentResolver.queryServices( + argThat { intent: Intent? -> intent != null && (action == intent.action) }, + nullable(), anyInt(), anyInt())) { + ArrayList(services.asList().map { info -> + ResolveInfo().apply { serviceInfo = info } + }) + } + } + + fun generateSpySignature(): Signature { + val bytes = ByteArray(32) + random.nextBytes(bytes) + val signature = spy(Signature(bytes)) + try { + val mockPublicKey: PublicKey = mock() + doReturn(mockPublicKey).whenever(signature).getPublicKey() + } catch (e: CertificateException) { + throw RuntimeException(e) + } + return signature + } + + /** Override get*Folder methods to point to temporary local directories */ + + @Throws(IOException::class) + private fun redirectScanPartitions(partitions: List<PackageManagerService.ScanPartition>): + List<PackageManagerService.ScanPartition> { + val spiedPartitions: MutableList<PackageManagerService.ScanPartition> = + ArrayList(partitions.size) + for (partition: PackageManagerService.ScanPartition in partitions) { + val spy = spy(partition) + val newRoot = Files.createTempDirectory(partition.folder.name).toFile() + whenever(spy.overlayFolder).thenReturn(File(newRoot, "overlay")) + whenever(spy.appFolder).thenReturn(File(newRoot, "app")) + whenever(spy.privAppFolder).thenReturn(File(newRoot, "priv-app")) + whenever(spy.folder).thenReturn(newRoot) + spiedPartitions.add(spy) + } + return spiedPartitions + } +} + +/** + * Sets up a basic [MockSystem] for use in a test method. This will create a MockSystem before the + * test method and any [org.junit.Before] annotated methods. It can then be used to access the + * MockSystem via the [system] method or the mocks directly via [mocks]. + */ +class MockSystemRule : TestRule { + var mockSystem: MockSystem? = null + override fun apply(base: Statement?, description: Description?) = object : Statement() { + @Throws(Throwable::class) + override fun evaluate() { + mockSystem = MockSystem() + try { + base!!.evaluate() + } finally { + mockSystem?.cleanup() + mockSystem = null + } + } + } + + /** Fetch the [MockSystem] instance prepared for this test */ + fun system(): MockSystem = mockSystem!! + /** Fetch the [MockSystem.Mocks] prepared for this test */ + fun mocks(): MockSystem.Mocks = mockSystem!!.mocks +} diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt new file mode 100644 index 000000000000..bd44c360b518 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.pm + +import android.content.pm.ApplicationInfo.FLAG_SYSTEM +import android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED +import android.content.pm.PackageManager +import android.content.pm.PackageParser +import android.os.Build +import android.os.Process +import android.util.Log +import com.android.server.pm.parsing.pkg.AndroidPackage +import com.android.server.testutils.whenever +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.hamcrest.Matchers.notNullValue +import org.hamcrest.collection.IsMapContaining.hasKey +import org.hamcrest.core.IsNot.not +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.argThat +import org.mockito.Mockito +import org.mockito.Mockito.verify +import java.io.File + +@RunWith(JUnit4::class) +class PackageManagerServiceBootTest { + + @Rule + @JvmField + val rule = MockSystemRule() + + @Before + @Throws(Exception::class) + fun setup() { + Log.i("system.out", "setup", Exception()) + rule.system().stageNominalSystemState() + } + + private fun createPackageManagerService(): PackageManagerService { + return PackageManagerService(rule.mocks().injector, + false /*coreOnly*/, + false /*factoryTest*/, + MockSystem.DEFAULT_VERSION_INFO.fingerprint, + false /*isEngBuild*/, + false /*isUserDebugBuild*/, + Build.VERSION_CODES.CUR_DEVELOPMENT, + Build.VERSION.INCREMENTAL) + } + + @Test + @Throws(Exception::class) + fun simpleConstruction() { + val pm = createPackageManagerService() + verify(rule.mocks().injector).bootstrap(pm) + verify(rule.mocks().settings).addSharedUserLPw("android.uid.system", + Process.SYSTEM_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED) + verify(rule.mocks().settings).addSharedUserLPw("android.uid.phone", + Process.PHONE_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED) + verify(rule.mocks().settings).addSharedUserLPw("android.uid.log", + Process.LOG_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED) + verify(rule.mocks().settings).addSharedUserLPw("android.uid.nfc", + Process.NFC_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED) + verify(rule.mocks().settings).addSharedUserLPw("android.uid.bluetooth", + Process.BLUETOOTH_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED) + verify(rule.mocks().settings).addSharedUserLPw("android.uid.shell", + Process.SHELL_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED) + verify(rule.mocks().settings).addSharedUserLPw("android.uid.se", + Process.SE_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED) + verify(rule.mocks().settings).addSharedUserLPw("android.uid.networkstack", + Process.NETWORK_STACK_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED) + rule.system().validateFinalState() + } + + @Test + @Throws(Exception::class) + fun existingDataPackage_remains() { + rule.system().stageScanExistingPackage("a.data.package", 1L, rule.system().dataAppDirectory) + val pm = createPackageManagerService() + rule.system().validateFinalState() + assertThat(pm.mPackages, hasKey("a.data.package")) + } + + @Test + @Throws(Exception::class) + fun unexpectedDataPackage_isRemoved() { + rule.system().stageScanNewPackage( + "a.data.package", 1L, rule.system().dataAppDirectory) + val pm = createPackageManagerService() + verify(rule.mocks().settings, Mockito.never()).insertPackageSettingLPw( + argThat { setting: PackageSetting -> setting.name == "a.data.package" }, + argThat { pkg: AndroidPackage -> pkg.packageName == "a.data.package" }) + assertThat(pm.mPackages, not(hasKey("a.data.package"))) + } + + @Test + @Throws(Exception::class) + fun expectedPackageMissing_doesNotReplace() { + // setup existing package + rule.system().stageScanExistingPackage("a.data.package", 1L, + rule.system().dataAppDirectory) + // simulate parsing failure for any path containing the package name. + whenever(rule.mocks().packageParser.parsePackage( + argThat { path: File -> path.path.contains("a.data.package") }, + anyInt(), + anyBoolean())) + .thenThrow(PackageParser.PackageParserException( + PackageManager.INSTALL_FAILED_INVALID_APK, "Oh no!")) + val pm = createPackageManagerService() + verify(rule.mocks().settings, Mockito.never()).insertPackageSettingLPw( + argThat { setting: PackageSetting -> setting.name == "a.data.package" }, + argThat { pkg: AndroidPackage -> pkg.packageName == "a.data.package" }) + assertThat(pm.mPackages, not(hasKey("a.data.package"))) + } + + @Test + @Throws(Exception::class) + fun expectingBetter_updateStillBetter() { + // Debug.waitForDebugger() + val systemAppPackageName = "com.android.test.updated.system.app" + val systemAppSigningDetails = rule.system().createRandomSigningDetails() + val systemVersionParent = rule.system() + .getPartitionFromFlag(PackageManagerService.SCAN_AS_PRODUCT).privAppFolder + + // system app v1 is disabled + whenever(rule.mocks().settings.isDisabledSystemPackageLPr(systemAppPackageName)) { + true + } + whenever(rule.mocks().settings.getDisabledSystemPkgLPr(systemAppPackageName)) { + rule.system().createBasicSettingBuilder( + File(systemVersionParent, systemAppPackageName), + systemAppPackageName, 1, systemAppSigningDetails).build() + } + + // system app v3 is on data/app + rule.system().stageScanExistingPackage( + systemAppPackageName, + 3, + rule.system().dataAppDirectory, + withPackage = { it.apply { signingDetails = systemAppSigningDetails } }, + withExistingSetting = { it.setPkgFlags(FLAG_SYSTEM) }) + + // system app v2 is scanned from system + rule.system().stageScanNewPackage(systemAppPackageName, 2, systemVersionParent, + withPackage = { it.apply { signingDetails = systemAppSigningDetails } }, + withSetting = { it.setPkgFlags(FLAG_SYSTEM) }) + + val pm = createPackageManagerService() + + assertThat("system package should exist after boot", + pm.mPackages[systemAppPackageName], notNullValue()) + assertThat("system package should remain at version on data/app", + pm.mPackages[systemAppPackageName]!!.longVersionCode, equalTo(3)) + } +}
\ No newline at end of file diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/quota/MultiRateLimiterTest.java b/services/tests/mockingservicestests/src/com/android/server/utils/quota/MultiRateLimiterTest.java new file mode 100644 index 000000000000..df533f3c122a --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/utils/quota/MultiRateLimiterTest.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.utils.quota; + +import static com.google.common.truth.Truth.assertThat; + +import android.testing.TestableContext; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; + +import org.junit.Rule; +import org.junit.Test; + +import java.time.Duration; + +@SmallTest +public class MultiRateLimiterTest { + + private static final int USER_ID = 1; + private static final String PACKAGE_NAME_1 = "com.android.package.one"; + private static final String PACKAGE_NAME_2 = "com.android.package.two"; + private static final String TAG = "tag"; + + @Rule + public final TestableContext mContext = + new TestableContext(InstrumentationRegistry.getContext(), null); + + private final InjectorForTest mInjector = new InjectorForTest(); + + private static class InjectorForTest extends QuotaTracker.Injector { + Duration mElapsedTime = Duration.ZERO; + + @Override + public long getElapsedRealtime() { + return mElapsedTime.toMillis(); + } + + @Override + public boolean isAlarmManagerReady() { + return true; + } + } + + @Test + public void testSingleRateLimit_belowLimit_isWithinQuota() { + MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector) + .addRateLimit(3, Duration.ofSeconds(20)) + .build(); + + // Three quick events are within quota. + mInjector.mElapsedTime = Duration.ZERO; + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(50); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(100); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + } + + @Test + public void testSingleRateLimit_aboveLimit_isNotWithinQuota() { + MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector) + .addRateLimit(3, Duration.ofSeconds(20)) + .build(); + + mInjector.mElapsedTime = Duration.ZERO; + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(50); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(100); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(150); + // We hit the limit, 4th event in under 20 seconds is not within quota. + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isFalse(); + } + + @Test + public void testSingleRateLimit_afterGoingAboveQuotaAndWaitingWindow_isBackWithinQuota() { + MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector) + .addRateLimit(3, Duration.ofSeconds(20)) + .build(); + + mInjector.mElapsedTime = Duration.ZERO; + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(50); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(100); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(150); + // We hit the limit, 4th event in under 20 seconds is not within quota. + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isFalse(); + + mInjector.mElapsedTime = Duration.ofSeconds(21); + // 20 seconds have passed, we're again within quota. + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + } + + @Test + public void createMultipleRateLimits_testTheyLimitsAsExpected() { + MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector) + .addRateLimit(3, Duration.ofSeconds(20)) // 1st limit + .addRateLimit(4, Duration.ofSeconds(40)) // 2nd limit + .addRateLimit(5, Duration.ofSeconds(60)) // 3rd limit + .build(); + + // Testing the 1st limit + mInjector.mElapsedTime = Duration.ZERO; + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(50); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(100); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(150); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isFalse(); + + mInjector.mElapsedTime = Duration.ofSeconds(21); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + // Testing the 2nd limit + mInjector.mElapsedTime = Duration.ofSeconds(35); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isFalse(); + + mInjector.mElapsedTime = Duration.ofSeconds(42); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + // Testing the 3rd limit. + mInjector.mElapsedTime = Duration.ofSeconds(43); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isFalse(); + + mInjector.mElapsedTime = Duration.ofSeconds(62); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + } + + @Test + public void createSingleRateLimit_testItLimitsOnlyGivenUptc() { + MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector) + .addRateLimit(3, Duration.ofSeconds(20)) + .build(); + + mInjector.mElapsedTime = Duration.ZERO; + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_2, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(50); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_2, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(100); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_2, TAG)).isTrue(); + multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG); + + mInjector.mElapsedTime = Duration.ofMillis(150); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isFalse(); + // Different userId - packageName - tag combination is still allowed. + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_2, TAG)).isTrue(); + + mInjector.mElapsedTime = Duration.ofSeconds(21); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue(); + assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_2, TAG)).isTrue(); + } +} diff --git a/services/tests/mockingservicestests/utils-mockito/com/android/server/extendedtestutils/ExtendedMockitoUtils.kt b/services/tests/mockingservicestests/utils-mockito/com/android/server/extendedtestutils/ExtendedMockitoUtils.kt new file mode 100644 index 000000000000..72ae77e48e25 --- /dev/null +++ b/services/tests/mockingservicestests/utils-mockito/com/android/server/extendedtestutils/ExtendedMockitoUtils.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.extendedtestutils + +import com.android.dx.mockito.inline.extended.ExtendedMockito +import com.android.dx.mockito.inline.extended.MockedMethod +import com.android.dx.mockito.inline.extended.StaticCapableStubber +import org.mockito.stubbing.Answer + +fun <T> StaticCapableStubber.wheneverStatic(methodCall: MockedMethod<T>) { + this.`when`(methodCall) +} + +fun <T> wheneverStatic(mockedMethod: MockedMethod<T>) = object : CustomStaticStubber<T> { + override fun thenAnswer(answer: Answer<T>) { + ExtendedMockito.doAnswer(answer).wheneverStatic(mockedMethod) + } + + override fun thenReturn(value: T) { + ExtendedMockito.doReturn(value).wheneverStatic(mockedMethod) + } +} + +interface CustomStaticStubber<T> { + fun thenAnswer(answer: Answer<T>) + fun thenReturn(value: T) +} diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 343b156e443a..89770fe39d60 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -111,6 +111,16 @@ android_test { } java_library { + name: "servicestests-core-utils", + srcs: [ + "src/com/android/server/pm/PackageSettingBuilder.java", + ], + static_libs: [ + "services.core", + ], +} + +java_library { name: "servicestests-utils", srcs: [ "utils/**/*.java", diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index 3f3d5e5106c5..839bc93ba00f 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -35,6 +35,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import android.content.res.Resources; import android.graphics.drawable.Icon; import android.os.IBinder; import android.os.UserHandle; @@ -94,6 +95,8 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase { @Mock private IAccessibilityServiceClient mMockServiceClient; @Mock private WindowMagnificationManager mMockWindowMagnificationMgr; @Mock private MagnificationController mMockMagnificationController; + @Mock private Resources mMockResources; + private AccessibilityUserState mUserState; private MessageCapturingHandler mHandler = new MessageCapturingHandler(null); @@ -121,6 +124,9 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase { mMockA11yDisplayListener, mMockMagnificationController); + mMockResources = mock(Resources.class); + when(mMockContext.getResources()).thenReturn(mMockResources); + final AccessibilityUserState userState = new AccessibilityUserState( mA11yms.getCurrentUserIdLocked(), mMockContext, mA11yms); mA11yms.mUserStates.put(mA11yms.getCurrentUserIdLocked(), userState); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java index fbfb0455bd46..81ca92cc3c8c 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java @@ -42,11 +42,16 @@ import static org.mockito.Mockito.when; import android.accessibilityservice.AccessibilityServiceInfo; import android.content.ComponentName; import android.content.Context; +import android.content.res.Resources; +import android.graphics.Color; import android.provider.Settings; import android.test.mock.MockContentResolver; import android.testing.DexmakerShareClassLoaderRule; import android.util.ArraySet; +import androidx.test.InstrumentationRegistry; + +import com.android.internal.R; import com.android.internal.util.test.FakeSettingsProvider; import org.junit.After; @@ -90,8 +95,13 @@ public class AccessibilityUserStateTest { private AccessibilityUserState mUserState; + private int mFocusStrokeWidthDefaultValue; + private int mFocusColorDefaultValue; + @Before public void setUp() { + final Resources resources = InstrumentationRegistry.getContext().getResources(); + MockitoAnnotations.initMocks(this); FakeSettingsProvider.clearSettingsProvider(); mMockResolver = new MockContentResolver(); @@ -99,6 +109,11 @@ public class AccessibilityUserStateTest { when(mMockContext.getContentResolver()).thenReturn(mMockResolver); when(mMockServiceInfo.getComponentName()).thenReturn(COMPONENT_NAME); when(mMockConnection.getServiceInfo()).thenReturn(mMockServiceInfo); + when(mMockContext.getResources()).thenReturn(resources); + + mFocusStrokeWidthDefaultValue = + resources.getDimensionPixelSize(R.dimen.accessibility_focus_highlight_stroke_width); + mFocusColorDefaultValue = resources.getColor(R.color.accessibility_focus_highlight_color); mUserState = new AccessibilityUserState(USER_ID, mMockContext, mMockListener); } @@ -129,6 +144,7 @@ public class AccessibilityUserStateTest { mUserState.setUserNonInteractiveUiTimeoutLocked(30); mUserState.setUserInteractiveUiTimeoutLocked(30); mUserState.setMagnificationModeLocked(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); + mUserState.setFocusAppearanceLocked(20, Color.BLUE); mUserState.onSwitchToAnotherUserLocked(); @@ -150,6 +166,8 @@ public class AccessibilityUserStateTest { assertEquals(0, mUserState.getUserInteractiveUiTimeoutLocked()); assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, mUserState.getMagnificationModeLocked()); + assertEquals(mFocusStrokeWidthDefaultValue, mUserState.getFocusStrokeWidthLocked()); + assertEquals(mFocusColorDefaultValue, mUserState.getFocusColorLocked()); } @Test @@ -350,6 +368,21 @@ public class AccessibilityUserStateTest { mUserState.getMagnificationModeLocked()); } + @Test + public void setFocusAppearanceData_returnExpectedFocusAppearanceData() { + final int focusStrokeWidthValue = 100; + final int focusColorValue = Color.BLUE; + + assertEquals(mFocusStrokeWidthDefaultValue, mUserState.getFocusStrokeWidthLocked()); + assertEquals(mFocusColorDefaultValue, mUserState.getFocusColorLocked()); + + mUserState.setFocusAppearanceLocked(focusStrokeWidthValue, focusColorValue); + + assertEquals(focusStrokeWidthValue, mUserState.getFocusStrokeWidthLocked()); + assertEquals(focusColorValue, mUserState.getFocusColorLocked()); + + } + private int getSecureIntForUser(String key, int userId) { return Settings.Secure.getIntForUser(mMockResolver, key, -1, userId); } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java index a05cbb48a3f7..6285f58d07b3 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java @@ -56,6 +56,30 @@ public class HdmiCecMessageValidatorTest { } @Test + public void isValid_unregisteredSource() { + // Message invokes a broadcast response + // <Get Menu Language> + assertMessageValidity("F4:91").isEqualTo(OK); + // <Request Active Source> + assertMessageValidity("FF:85").isEqualTo(OK); + + // Message by CEC Switch + // <Routing Change> + assertMessageValidity("FF:80:00:00:10:00").isEqualTo(OK); + + // <Routing Information> + assertMessageValidity("FF:81:10:00").isEqualTo(OK); + + // Standby + assertMessageValidity("F4:36").isEqualTo(OK); + assertMessageValidity("FF:36").isEqualTo(OK); + + // <Report Physical Address> / <Active Source> + assertMessageValidity("FF:84:10:00:04").isEqualTo(OK); + assertMessageValidity("FF:82:10:00").isEqualTo(OK); + } + + @Test public void isValid_giveDevicePowerStatus() { assertMessageValidity("04:8F").isEqualTo(OK); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java index f8e92ad71d8e..84551c51052c 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java @@ -22,6 +22,7 @@ import android.util.ArraySet; import android.util.SparseArray; import com.android.server.pm.parsing.pkg.AndroidPackage; +import com.android.server.pm.parsing.pkg.PackageImpl; import java.io.File; import java.util.Map; @@ -38,14 +39,14 @@ public class PackageSettingBuilder { private int mPkgFlags; private int mPrivateFlags; private int mSharedUserId; - private String[] mUsesStaticLibraries; - private long[] mUsesStaticLibrariesVersions; - private Map<String, ArraySet<String>> mMimeGroups; private String mVolumeUuid; + private int mAppId; private SparseArray<PackageUserState> mUserStates = new SparseArray<>(); private AndroidPackage mPkg; - private int mAppId; private InstallSource mInstallSource; + private String[] mUsesStaticLibraries; + private long[] mUsesStaticLibrariesVersions; + private Map<String, ArraySet<String>> mMimeGroups; private PackageParser.SigningDetails mSigningDetails; public PackageSettingBuilder setPackage(AndroidPackage pkg) { @@ -143,6 +144,14 @@ public class PackageSettingBuilder { return this; } + public PackageSettingBuilder setInstallState(int userId, boolean installed) { + if (mUserStates.indexOfKey(userId) < 0) { + mUserStates.put(userId, new PackageUserState()); + } + mUserStates.get(userId).installed = installed; + return this; + } + public PackageSettingBuilder setInstallSource(InstallSource installSource) { mInstallSource = installSource; return this; @@ -173,6 +182,5 @@ public class PackageSettingBuilder { packageSetting.setUserState(mUserStates.keyAt(i), mUserStates.valueAt(i)); } return packageSetting; - } } diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java index fcbb5ed1140c..d8c3979c9cf9 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java @@ -101,7 +101,8 @@ public class ScanTests { @Before public void setupDefaultAbiBehavior() throws Exception { when(mMockPackageAbiHelper.derivePackageAbi( - any(AndroidPackage.class), anyBoolean(), nullable(String.class))) + any(AndroidPackage.class), anyBoolean(), nullable(String.class), + any(File.class))) .thenReturn(new Pair<>( new PackageAbiHelper.Abis("derivedPrimary", "derivedSecondary"), new PackageAbiHelper.NativeLibraryPaths( diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java index 92942bb91528..e816cbebd73f 100644 --- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java +++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java @@ -23,6 +23,7 @@ import static com.android.server.policy.DeviceStateProviderImpl.DEFAULT_DEVICE_S import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -170,16 +171,16 @@ public final class DeviceStateProviderImplTest { @Test public void create_sensor() throws Exception { - Sensor sensor = newSensor("sensor", Sensor.TYPE_HINGE_ANGLE); - when(mSensorManager.getSensorList(eq(sensor.getType()))).thenReturn(List.of(sensor)); + Sensor sensor = newSensor("sensor", Sensor.STRING_TYPE_HINGE_ANGLE); + when(mSensorManager.getSensorList(anyInt())).thenReturn(List.of(sensor)); String configString = "<device-state-config>\n" + " <device-state>\n" + " <identifier>1</identifier>\n" + " <conditions>\n" + " <sensor>\n" + + " <type>" + sensor.getStringType() + "</type>\n" + " <name>" + sensor.getName() + "</name>\n" - + " <type>" + sensor.getType() + "</type>\n" + " <value>\n" + " <max>90</max>\n" + " </value>\n" @@ -190,8 +191,8 @@ public final class DeviceStateProviderImplTest { + " <identifier>2</identifier>\n" + " <conditions>\n" + " <sensor>\n" + + " <type>" + sensor.getStringType() + "</type>\n" + " <name>" + sensor.getName() + "</name>\n" - + " <type>" + sensor.getType() + "</type>\n" + " <value>\n" + " <min-inclusive>90</min-inclusive>\n" + " <max>180</max>\n" @@ -203,8 +204,8 @@ public final class DeviceStateProviderImplTest { + " <identifier>3</identifier>\n" + " <conditions>\n" + " <sensor>\n" + + " <type>" + sensor.getStringType() + "</type>\n" + " <name>" + sensor.getName() + "</name>\n" - + " <type>" + sensor.getType() + "</type>\n" + " <value>\n" + " <min-inclusive>180</min-inclusive>\n" + " </value>\n" @@ -262,13 +263,13 @@ public final class DeviceStateProviderImplTest { assertEquals(1, mIntegerCaptor.getValue().intValue()); } - private static Sensor newSensor(String name, int type) throws Exception { + private static Sensor newSensor(String name, String type) throws Exception { Constructor<Sensor> constructor = Sensor.class.getDeclaredConstructor(); constructor.setAccessible(true); Sensor sensor = constructor.newInstance(); FieldSetter.setField(sensor, Sensor.class.getDeclaredField("mName"), name); - FieldSetter.setField(sensor, Sensor.class.getDeclaredField("mType"), type); + FieldSetter.setField(sensor, Sensor.class.getDeclaredField("mStringType"), type); return sensor; } diff --git a/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt b/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt index 59cbd1ce1d7b..4c82818f71e4 100644 --- a/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt +++ b/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt @@ -17,6 +17,7 @@ package com.android.server.testutils import org.mockito.Answers +import org.mockito.ArgumentMatchers import org.mockito.Mockito import org.mockito.invocation.InvocationOnMock import org.mockito.stubbing.Answer @@ -53,7 +54,7 @@ inline fun <reified T> mock(block: T.() -> Unit = {}) = Mockito.mock(T::class.ja fun <T> spy(value: T, block: T.() -> Unit = {}) = Mockito.spy(value).apply(block) -fun <Type> Stubber.whenever(mock: Type) = Mockito.`when`(mock) +fun <Type> Stubber.whenever(mock: Type) = this.`when`(mock) fun <Type : Any?> whenever(mock: Type) = Mockito.`when`(mock) @Suppress("UNCHECKED_CAST") @@ -81,3 +82,5 @@ inline fun <reified T> spyThrowOnUnmocked(value: T?, block: T.() -> Unit = {}): inline fun <reified T> mockThrowOnUnmocked(block: T.() -> Unit = {}) = spyThrowOnUnmocked<T>(null, block) + +inline fun <reified T : Any> nullable() = ArgumentMatchers.nullable(T::class.java)
\ No newline at end of file diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java index 00f706b13c58..32cb2a3a2540 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java @@ -153,7 +153,8 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { AccessibilityEvent.TYPES_ALL_MASK); when(mAccessibilityService.addClient(any(), anyInt())).thenReturn(serviceReturnValue); AccessibilityManager accessibilityManager = - new AccessibilityManager(Handler.getMain(), mAccessibilityService, 0); + new AccessibilityManager(getContext(), Handler.getMain(), mAccessibilityService, + 0, true); verify(mAccessibilityService).addClient(any(IAccessibilityManagerClient.class), anyInt()); assertTrue(accessibilityManager.isEnabled()); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java index fdc089ee2f7e..9b37e76e4c65 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java @@ -102,7 +102,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { String key = mKeys[i]; Ranking ranking = new Ranking(); service.getCurrentRanking().getRanking(key, ranking); - assertEquals(getVisibilityOverride(i), ranking.getVisibilityOverride()); + assertEquals(getVisibilityOverride(i), ranking.getLockscreenVisibilityOverride()); assertEquals(getOverrideGroupKey(key), ranking.getOverrideGroupKey()); assertEquals(!isIntercepted(i), ranking.matchesInterruptionFilter()); assertEquals(getSuppressedVisualEffects(i), ranking.getSuppressedVisualEffects()); @@ -173,7 +173,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { tweak.getKey(), tweak.getRank(), !tweak.matchesInterruptionFilter(), // note the inversion here! - tweak.getVisibilityOverride(), + tweak.getLockscreenVisibilityOverride(), tweak.getSuppressedVisualEffects(), tweak.getImportance(), tweak.getImportanceExplanation(), @@ -424,7 +424,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { assertEquals(comment, a.getKey(), b.getKey()); assertEquals(comment, a.getRank(), b.getRank()); assertEquals(comment, a.matchesInterruptionFilter(), b.matchesInterruptionFilter()); - assertEquals(comment, a.getVisibilityOverride(), b.getVisibilityOverride()); + assertEquals(comment, a.getLockscreenVisibilityOverride(), b.getLockscreenVisibilityOverride()); assertEquals(comment, a.getSuppressedVisualEffects(), b.getSuppressedVisualEffects()); assertEquals(comment, a.getImportance(), b.getImportance()); assertEquals(comment, a.getImportanceExplanation(), b.getImportanceExplanation()); @@ -440,7 +440,8 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { assertEquals(comment, a.getSmartReplies(), b.getSmartReplies()); assertEquals(comment, a.canBubble(), b.canBubble()); assertEquals(comment, a.isConversation(), b.isConversation()); - assertEquals(comment, a.getConversationShortcutInfo().getId(), b.getConversationShortcutInfo().getId()); + assertEquals(comment, a.getConversationShortcutInfo().getId(), + b.getConversationShortcutInfo().getId()); assertActionsEqual(a.getSmartActions(), b.getSmartActions()); } 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 5cf529a239dc..762ec93712cb 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -183,6 +183,7 @@ import com.android.server.notification.NotificationManagerService.NotificationAs import com.android.server.notification.NotificationManagerService.NotificationListeners; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.uri.UriGrantsManagerInternal; +import com.android.server.utils.quota.MultiRateLimiter; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; @@ -295,6 +296,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationHistoryManager mHistoryManager; @Mock StatsManager mStatsManager; + @Mock + MultiRateLimiter mToastRateLimiter; BroadcastReceiver mPackageIntentReceiver; NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake(); private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake( @@ -486,7 +489,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mGroupHelper, mAm, mAtm, mAppUsageStats, mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal, mAppOpsManager, mUm, mHistoryManager, mStatsManager, - mock(TelephonyManager.class), mAmi); + mock(TelephonyManager.class), mAmi, mToastRateLimiter); mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); mService.setAudioManager(mAudioManager); @@ -566,7 +569,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { try { mService.onDestroy(); - } catch (IllegalStateException e) { + } catch (IllegalStateException | IllegalArgumentException e) { // can throw if a broadcast receiver was never registered } @@ -4888,6 +4891,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -4910,6 +4914,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -4928,6 +4933,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -4950,10 +4956,32 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testToastRateLimiterCanPreventsShowCallForCustomToast() throws Exception { + final String testPackage = "testPackageName"; + assertEquals(0, mService.mToastQueue.size()); + mService.isSystemUid = false; + setToastRateIsWithinQuota(false); // rate limit reached + + // package is not suspended + when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) + .thenReturn(false); + + setAppInForegroundForToasts(mUid, true); + + Binder token = new Binder(); + ITransientNotification callback = mock(ITransientNotification.class); + INotificationManager nmService = (INotificationManager) mService.mService; + + nmService.enqueueToast(testPackage, token, callback, 2000, 0); + verify(callback, times(0)).show(any()); + } + + @Test public void testAllowForegroundTextToasts() throws Exception { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -4972,6 +5000,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -4990,6 +5019,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -5013,11 +5043,31 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testToastRateLimiterCanPreventsShowCallForTextToast() throws Exception { + final String testPackage = "testPackageName"; + assertEquals(0, mService.mToastQueue.size()); + mService.isSystemUid = false; + setToastRateIsWithinQuota(false); // rate limit reached + + // package is not suspended + when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) + .thenReturn(false); + + Binder token = new Binder(); + INotificationManager nmService = (INotificationManager) mService.mService; + + nmService.enqueueTextToast(testPackage, token, "Text", 2000, 0, null); + verify(mStatusBar, times(0)) + .showToast(anyInt(), any(), any(), any(), any(), anyInt(), any()); + } + + @Test public void backgroundSystemCustomToast_callsSetProcessImportantAsForegroundForToast() throws Exception { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = true; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -5042,6 +5092,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -5062,6 +5113,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -5081,6 +5133,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -5097,6 +5150,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -5117,6 +5171,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -5139,6 +5194,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = true; + setToastRateIsWithinQuota(true); // package is suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -5161,6 +5217,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); mService.isSystemUid = false; + setToastRateIsWithinQuota(true); // package is not suspended when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) @@ -5188,6 +5245,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mAtm.hasResumedActivity(uid)).thenReturn(inForeground); } + private void setToastRateIsWithinQuota(boolean isWithinQuota) { + when(mToastRateLimiter.isWithinQuota( + anyInt(), + anyString(), + eq(NotificationManagerService.TOAST_QUOTA_TAG))) + .thenReturn(isWithinQuota); + } + @Test public void testOnPanelRevealedAndHidden() { int items = 5; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 98c4a2da6a4f..4d2a4784b5d9 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -97,6 +97,7 @@ import android.test.suitebuilder.annotation.SmallTest; import android.testing.TestableContentResolver; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.IntArray; import android.util.Pair; import android.util.StatsEvent; import android.util.TypedXmlPullParser; @@ -3320,7 +3321,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { channel2.setImportantConversation(true); mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false); - List<ConversationChannelWrapper> convos = mHelper.getConversations(false); + List<ConversationChannelWrapper> convos = + mHelper.getConversations(IntArray.wrap(new int[] {0}), false); assertEquals(3, convos.size()); assertTrue(conversationWrapperContainsChannel(convos, channel)); @@ -3329,6 +3331,44 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test + public void testGetConversations_multiUser() { + String convoId = "convo"; + NotificationChannel messages = + new NotificationChannel("messages", "Messages", IMPORTANCE_DEFAULT); + mHelper.createNotificationChannel(PKG_O, UID_O, messages, true, false); + + NotificationChannel messagesUser10 = + new NotificationChannel("messages", "Messages", IMPORTANCE_DEFAULT); + mHelper.createNotificationChannel( + PKG_O, UID_O + UserHandle.PER_USER_RANGE, messagesUser10, true, false); + + NotificationChannel messagesFromB = + new NotificationChannel("B person msgs", "messages from B", IMPORTANCE_DEFAULT); + messagesFromB.setConversationId(messages.getId(), "different convo"); + mHelper.createNotificationChannel(PKG_O, UID_O, messagesFromB, true, false); + + NotificationChannel messagesFromBUser10 = + new NotificationChannel("B person msgs", "messages from B", IMPORTANCE_DEFAULT); + messagesFromBUser10.setConversationId(messagesUser10.getId(), "different convo"); + mHelper.createNotificationChannel( + PKG_O, UID_O + UserHandle.PER_USER_RANGE, messagesFromBUser10, true, false); + + + List<ConversationChannelWrapper> convos = + mHelper.getConversations(IntArray.wrap(new int[] {0}), false); + + assertEquals(1, convos.size()); + assertTrue(conversationWrapperContainsChannel(convos, messagesFromB)); + + convos = + mHelper.getConversations(IntArray.wrap(new int[] {0, UserHandle.getUserId(UID_O + UserHandle.PER_USER_RANGE)}), false); + + assertEquals(2, convos.size()); + assertTrue(conversationWrapperContainsChannel(convos, messagesFromB)); + assertTrue(conversationWrapperContainsChannel(convos, messagesFromBUser10)); + } + + @Test public void testGetConversations_notDemoted() { String convoId = "convo"; NotificationChannel messages = @@ -3358,7 +3398,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { channel2.setImportantConversation(true); mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false); - List<ConversationChannelWrapper> convos = mHelper.getConversations(false); + List<ConversationChannelWrapper> convos = + mHelper.getConversations(IntArray.wrap(new int[] {0}), false); assertEquals(2, convos.size()); assertTrue(conversationWrapperContainsChannel(convos, channel)); @@ -3396,7 +3437,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { channel2.setConversationId(calls.getId(), convoId); mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false); - List<ConversationChannelWrapper> convos = mHelper.getConversations(true); + List<ConversationChannelWrapper> convos = + mHelper.getConversations(IntArray.wrap(new int[] {0}), true); assertEquals(2, convos.size()); assertTrue(conversationWrapperContainsChannel(convos, channel)); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java index a80f62ab09ee..4ce237e3aadc 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java @@ -67,6 +67,7 @@ import com.android.server.lights.LightsManager; import com.android.server.notification.NotificationManagerService.NotificationAssistants; import com.android.server.notification.NotificationManagerService.NotificationListeners; import com.android.server.uri.UriGrantsManagerInternal; +import com.android.server.utils.quota.MultiRateLimiter; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; @@ -156,7 +157,8 @@ public class RoleObserverTest extends UiServiceTestCase { mock(UriGrantsManagerInternal.class), mock(AppOpsManager.class), mUm, mock(NotificationHistoryManager.class), mock(StatsManager.class), mock(TelephonyManager.class), - mock(ActivityManagerInternal.class)); + mock(ActivityManagerInternal.class), + mock(MultiRateLimiter.class)); } catch (SecurityException e) { if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { throw e; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/VisibilityExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/VisibilityExtractorTest.java new file mode 100644 index 000000000000..3998129659c6 --- /dev/null +++ b/services/tests/uiservicestests/src/com/android/server/notification/VisibilityExtractorTest.java @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.notification; + +import static android.app.Notification.VISIBILITY_PRIVATE; +import static android.app.Notification.VISIBILITY_SECRET; +import static android.app.NotificationManager.IMPORTANCE_DEFAULT; +import static android.app.NotificationManager.IMPORTANCE_HIGH; +import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; +import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE; +import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS; +import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.Mockito.when; + +import android.app.ActivityManager; +import android.app.Notification; +import android.app.Notification.Builder; +import android.app.NotificationChannel; +import android.app.PendingIntent; +import android.app.admin.DevicePolicyManager; +import android.content.Intent; +import android.graphics.drawable.Icon; +import android.media.session.MediaSession; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; +import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.UiServiceTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class VisibilityExtractorTest extends UiServiceTestCase { + + @Mock RankingConfig mConfig; + @Mock + DevicePolicyManager mDpm; + + private String mPkg = "com.android.server.notification"; + private int mId = 1001; + private String mTag = null; + private int mUid = 1000; + private int mPid = 2000; + private int mUser = ActivityManager.getCurrentUser(); + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext.addMockSystemService(DevicePolicyManager.class, mDpm); + } + + private NotificationRecord getNotificationRecord(int visibility) { + NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT); + channel.setLockscreenVisibility(visibility); + when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel); + + final Builder builder = new Builder(getContext()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon); + + Notification n = builder.build(); + StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, mId, mTag, mUid, + mPid, n, UserHandle.of(mUser), null, System.currentTimeMillis()); + NotificationRecord r = new NotificationRecord(getContext(), sbn, channel); + return r; + } + + // + // Tests + // + + @Test + public void testGlobalAllDpmAllChannelAll() { + VisibilityExtractor extractor = new VisibilityExtractor(); + extractor.setConfig(mConfig); + extractor.initialize(mContext, null); + + when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(true); + when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(true); + + when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn(0); + + NotificationRecord r = getNotificationRecord(VISIBILITY_NO_OVERRIDE); + + extractor.process(r); + + assertEquals(VISIBILITY_NO_OVERRIDE, r.getPackageVisibilityOverride()); + } + + @Test + public void testGlobalNoneDpmAllChannelAll() { + VisibilityExtractor extractor = new VisibilityExtractor(); + extractor.setConfig(mConfig); + extractor.initialize(mContext, null); + + when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(false); + when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(true); + + when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn(0); + + NotificationRecord r = getNotificationRecord(VISIBILITY_NO_OVERRIDE); + + extractor.process(r); + + assertEquals(VISIBILITY_SECRET, r.getPackageVisibilityOverride()); + } + + @Test + public void testGlobalSomeDpmAllChannelAll() { + VisibilityExtractor extractor = new VisibilityExtractor(); + extractor.setConfig(mConfig); + extractor.initialize(mContext, null); + + when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(true); + when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(false); + + when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn(0); + + NotificationRecord r = getNotificationRecord(VISIBILITY_NO_OVERRIDE); + + extractor.process(r); + + assertEquals(VISIBILITY_PRIVATE, r.getPackageVisibilityOverride()); + } + + @Test + public void testGlobalAllDpmNoneChannelAll() { + VisibilityExtractor extractor = new VisibilityExtractor(); + extractor.setConfig(mConfig); + extractor.initialize(mContext, null); + + when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(true); + when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(true); + + when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn( + KEYGUARD_DISABLE_SECURE_NOTIFICATIONS); + + NotificationRecord r = getNotificationRecord(VISIBILITY_NO_OVERRIDE); + + extractor.process(r); + + assertEquals(VISIBILITY_SECRET, r.getPackageVisibilityOverride()); + } + + @Test + public void testGlobalAllDpmSomeChannelAll() { + VisibilityExtractor extractor = new VisibilityExtractor(); + extractor.setConfig(mConfig); + extractor.initialize(mContext, null); + + when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(true); + when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(true); + + when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn( + KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); + + NotificationRecord r = getNotificationRecord(VISIBILITY_NO_OVERRIDE); + + extractor.process(r); + + assertEquals(VISIBILITY_PRIVATE, r.getPackageVisibilityOverride()); + } + + @Test + public void testGlobalAllDpmAllChannelNone() { + VisibilityExtractor extractor = new VisibilityExtractor(); + extractor.setConfig(mConfig); + extractor.initialize(mContext, null); + + when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(true); + when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(true); + + when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn(0); + + NotificationRecord r = getNotificationRecord(VISIBILITY_SECRET); + + extractor.process(r); + + assertEquals(VISIBILITY_SECRET, r.getPackageVisibilityOverride()); + } + + @Test + public void testGlobalAllDpmAllChannelSome() { + VisibilityExtractor extractor = new VisibilityExtractor(); + extractor.setConfig(mConfig); + extractor.initialize(mContext, null); + + when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(true); + when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(true); + + when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn(0); + + NotificationRecord r = getNotificationRecord(VISIBILITY_PRIVATE); + + extractor.process(r); + + assertEquals(VISIBILITY_PRIVATE, r.getPackageVisibilityOverride()); + } + + @Test + public void testGlobalAllDpmSomeChannelNone() { + VisibilityExtractor extractor = new VisibilityExtractor(); + extractor.setConfig(mConfig); + extractor.initialize(mContext, null); + + when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(true); + when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(true); + + when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn( + KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); + + NotificationRecord r = getNotificationRecord(VISIBILITY_SECRET); + + extractor.process(r); + + assertEquals(VISIBILITY_SECRET, r.getPackageVisibilityOverride()); + } + +} diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java index bc91c709aeb1..f536cd0b0ed4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java @@ -61,7 +61,7 @@ public class DisplayAreaGroupTest extends WindowTestsBase { mTaskDisplayArea = new TaskDisplayArea( mDisplayContent, mWm, "TDA1", FEATURE_VENDOR_FIRST + 1); mDisplayAreaGroup.addChild(mTaskDisplayArea, POSITION_TOP); - mDisplayContent.setLastFocusedTaskDisplayArea(mTaskDisplayArea); + mDisplayContent.onLastFocusedTaskDisplayAreaChanged(mTaskDisplayArea); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java index 811ff4acb995..e47913fba7ab 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java @@ -47,6 +47,7 @@ import static org.testng.Assert.assertThrows; import static java.util.stream.Collectors.toList; import android.content.res.Resources; +import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.platform.test.annotations.Presubmit; @@ -585,8 +586,8 @@ public class DisplayAreaPolicyBuilderTest { .setTaskDisplayAreas(mTaskDisplayAreaList)) .addDisplayAreaGroupHierarchy(hierarchy1) .addDisplayAreaGroupHierarchy(hierarchy2) - .setSelectRootForWindowFunc((token, options) -> { - if (token.windowType == TYPE_STATUS_BAR) { + .setSelectRootForWindowFunc((type, options) -> { + if (type == TYPE_STATUS_BAR) { return mGroupRoot1; } return mGroupRoot2; @@ -740,10 +741,9 @@ public class DisplayAreaPolicyBuilderTest { } private WindowToken tokenOfType(int type) { - WindowToken m = mock(WindowToken.class); - when(m.getWindowLayerFromType()).thenReturn( - mPolicy.getWindowLayerFromTypeLw(type, false /* canAddInternalSystemWindow */)); - return m; + WindowToken token = new WindowToken(mWms, new Binder(), type, false /* persistOnEmpty */, + mDisplayContent, false /* ownerCanManageAppTokens */); + return token; } private static void assertMatchLayerOrder(List<DisplayArea<?>> actualOrder, diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java index 025c5a6bb180..6f5a874114ea 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java @@ -458,8 +458,7 @@ public class DisplayAreaTest extends WindowTestsBase { @Test public void testSetIgnoreOrientationRequest_notCallSuperOnDescendantOrientationChanged() { - final TaskDisplayArea tda = - mDisplayContent.getDefaultTaskDisplayArea(); + final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea(); final Task stack = new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build(); final ActivityRecord activity = stack.getTopNonFinishingActivity(); @@ -478,6 +477,27 @@ public class DisplayAreaTest extends WindowTestsBase { verify(mDisplayContent).onDescendantOrientationChanged(any()); } + @Test + public void testSetIgnoreOrientationRequest_updateOrientationRequestingTaskDisplayArea() { + final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea(); + final Task stack = + new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build(); + final ActivityRecord activity = stack.getTopNonFinishingActivity(); + + mDisplayContent.setFocusedApp(activity); + assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isEqualTo(tda); + + // TDA is no longer handling orientation request, clear the last focused TDA. + tda.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isNull(); + + // TDA now handles orientation request, update last focused TDA based on the focused app. + tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); + + assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isEqualTo(tda); + } + private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> { private TestDisplayArea(WindowManagerService wms, Rect bounds) { super(wms, ANY, "half display area"); diff --git a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java index 73e886f67cd3..eebc207d2d4f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java @@ -110,7 +110,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { @Test public void testNotIgnoreOrientationRequest_differentOrientationFromDisplay_reversesRequest() { mFirstRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); - mDisplay.setLastFocusedTaskDisplayArea(mFirstTda); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE); @@ -127,7 +127,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { public void testNotIgnoreOrientationRequest_onlyRespectsFocusedTaskDisplayArea() { mFirstRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); mSecondRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); - mDisplay.setLastFocusedTaskDisplayArea(mFirstTda); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); // Second TDA is not focused, so Display won't get the request prepareUnresizable(mSecondActivity, SCREEN_ORIENTATION_LANDSCAPE); @@ -144,7 +144,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { public void testIgnoreOrientationRequest_displayDoesNotReceiveOrientationChange() { mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - mDisplay.setLastFocusedTaskDisplayArea(mFirstTda); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE); @@ -156,7 +156,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { public void testLaunchPortraitApp_fillsDisplayAreaGroup() { mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - mDisplay.setLastFocusedTaskDisplayArea(mFirstTda); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_PORTRAIT); final Rect dagBounds = new Rect(mFirstRoot.getBounds()); @@ -174,7 +174,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { public void testLaunchPortraitApp_sizeCompatAfterRotation() { mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - mDisplay.setLastFocusedTaskDisplayArea(mFirstTda); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_PORTRAIT); final Rect dagBounds = new Rect(mFirstRoot.getBounds()); @@ -207,7 +207,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { public void testLaunchLandscapeApp_taskIsLetterboxInDisplayAreaGroup() { mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - mDisplay.setLastFocusedTaskDisplayArea(mFirstTda); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE); final Rect dagBounds = new Rect(mFirstRoot.getBounds()); @@ -227,7 +227,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { public void testLaunchLandscapeApp_taskLetterboxBecomesActivityLetterboxAfterRotation() { mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - mDisplay.setLastFocusedTaskDisplayArea(mFirstTda); + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE); final Rect dagBounds = new Rect(mFirstRoot.getBounds()); diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index 83e3d22345e1..e1bc90a2551c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -325,7 +325,7 @@ public class SystemServicesTestRule implements TestRule { final TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea(); // Set the default focused TDA. - display.setLastFocusedTaskDisplayArea(taskDisplayArea); + display.onLastFocusedTaskDisplayAreaChanged(taskDisplayArea); spyOn(taskDisplayArea); final Task homeStack = taskDisplayArea.getRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java index d80f81642474..1c93e0f7e185 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java @@ -262,20 +262,50 @@ public class TaskDisplayAreaTests extends WindowTestsBase { // Activity on TDA1 is focused mDisplayContent.setFocusedApp(firstActivity); - assertThat(firstTaskDisplayArea.isLastFocused()).isTrue(); - assertThat(secondTaskDisplayArea.isLastFocused()).isFalse(); + assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isTrue(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isFalse(); // No focused app, TDA1 is still recorded as last focused. mDisplayContent.setFocusedApp(null); - assertThat(firstTaskDisplayArea.isLastFocused()).isTrue(); - assertThat(secondTaskDisplayArea.isLastFocused()).isFalse(); + assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isTrue(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isFalse(); // Activity on TDA2 is focused mDisplayContent.setFocusedApp(secondActivity); - assertThat(firstTaskDisplayArea.isLastFocused()).isFalse(); - assertThat(secondTaskDisplayArea.isLastFocused()).isTrue(); + assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isFalse(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isTrue(); + } + + @Test + public void testIsLastFocused_onlyCountIfTaskDisplayAreaHandlesOrientationRequest() { + final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); + final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea( + mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea", + FEATURE_VENDOR_FIRST); + final Task firstStack = firstTaskDisplayArea.createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final Task secondStack = secondTaskDisplayArea.createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final ActivityRecord firstActivity = new ActivityBuilder(mAtm) + .setTask(firstStack).build(); + final ActivityRecord secondActivity = new ActivityBuilder(mAtm) + .setTask(secondStack).build(); + firstTaskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + secondTaskDisplayArea.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); + + // Activity on TDA1 is focused, but TDA1 doesn't respect orientation request + mDisplayContent.setFocusedApp(firstActivity); + + assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isFalse(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isFalse(); + + // Activity on TDA2 is focused, and TDA2 respects orientation request + mDisplayContent.setFocusedApp(secondActivity); + + assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isFalse(); + assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isTrue(); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java index b1065914a925..ae85ceb72958 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java @@ -161,7 +161,7 @@ class TestDisplayContent extends DisplayContent { mService.mRootWindowContainer.addChild(newDisplay, mPosition); // Set the default focused TDA. - newDisplay.setLastFocusedTaskDisplayArea(newDisplay.getDefaultTaskDisplayArea()); + newDisplay.onLastFocusedTaskDisplayAreaChanged(newDisplay.getDefaultTaskDisplayArea()); return newDisplay; } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index c4bcfdbcf89d..b0b8afd6c3a4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -948,6 +948,36 @@ public class WindowContainerTests extends WindowTestsBase { assertFalse(act.isAnimating(PARENTS)); } + @Test + public void testRegisterWindowContainerListener() { + final WindowContainer container = new WindowContainer(mWm); + container.mDisplayContent = mDisplayContent; + final TestWindowContainerListener listener = new TestWindowContainerListener(); + Configuration config = container.getConfiguration(); + Rect bounds = new Rect(0, 0, 10, 10); + config.windowConfiguration.setBounds(bounds); + config.densityDpi = 100; + container.onRequestedOverrideConfigurationChanged(config); + container.registerWindowContainerListener(listener); + + assertEquals(mDisplayContent, listener.mDisplayContent); + assertEquals(bounds, listener.mConfiguration.windowConfiguration.getBounds()); + assertEquals(100, listener.mConfiguration.densityDpi); + + container.onDisplayChanged(mDefaultDisplay); + assertEquals(listener.mDisplayContent, mDefaultDisplay); + + config = new Configuration(); + bounds = new Rect(0, 0, 20, 20); + config.windowConfiguration.setBounds(bounds); + config.densityDpi = 200; + + container.onRequestedOverrideConfigurationChanged(config); + + assertEquals(bounds, listener.mConfiguration.windowConfiguration.getBounds()); + assertEquals(200, listener.mConfiguration.densityDpi); + } + /* Used so we can gain access to some protected members of the {@link WindowContainer} class */ private static class TestWindowContainer extends WindowContainer<TestWindowContainer> { private final int mLayer; @@ -1131,4 +1161,19 @@ public class WindowContainerTests extends WindowTestsBase { mSession.kill(); } } + + private static class TestWindowContainerListener implements WindowContainerListener { + private Configuration mConfiguration = new Configuration(); + private DisplayContent mDisplayContent; + + @Override + public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) { + mConfiguration.setTo(overrideConfiguration); + } + + @Override + public void onDisplayChanged(DisplayContent dc) { + mDisplayContent = dc; + } + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java new file mode 100644 index 000000000000..67067ee08e41 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.app.IWindowToken; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.os.Binder; +import android.os.IBinder; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Build/Install/Run: + * atest WmTests:WindowContextListenerControllerTests + */ +@SmallTest +@Presubmit +@RunWith(WindowTestRunner.class) +public class WindowContextListenerControllerTests extends WindowTestsBase { + private WindowContextListenerController mController; + + private static final int TEST_UID = 12345; + private static final int ANOTHER_UID = 1000; + + private final IBinder mClientToken = new Binder(); + private WindowContainer mContainer; + + @Before + public void setUp() { + mController = new WindowContextListenerController(); + mContainer = createTestWindowToken(TYPE_APPLICATION_OVERLAY, mDisplayContent); + } + + @Test + public void testRegisterWindowContextListener() { + mController.registerWindowContainerListener(mClientToken, mContainer, -1); + + assertEquals(1, mController.mListeners.size()); + + final IBinder clientToken = new Binder(); + mController.registerWindowContainerListener(clientToken, mContainer, -1); + + assertEquals(2, mController.mListeners.size()); + + final WindowContainer container = createTestWindowToken(TYPE_APPLICATION_OVERLAY, + mDefaultDisplay); + mController.registerWindowContainerListener(mClientToken, container, -1); + + // The number of listeners doesn't increase since the listener just gets updated. + assertEquals(2, mController.mListeners.size()); + + WindowContextListenerController.WindowContextListenerImpl listener = + mController.mListeners.get(mClientToken); + assertEquals(container, listener.getWindowContainer()); + } + + @Test + public void testRegisterWindowContextListenerClientConfigPropagation() { + final TestWindowTokenClient clientToken = new TestWindowTokenClient(); + + final Configuration config1 = mContainer.getConfiguration(); + final Rect bounds1 = new Rect(0, 0, 10, 10); + config1.windowConfiguration.setBounds(bounds1); + config1.densityDpi = 100; + mContainer.onRequestedOverrideConfigurationChanged(config1); + + mController.registerWindowContainerListener(clientToken, mContainer, -1); + + assertEquals(bounds1, clientToken.mConfiguration.windowConfiguration.getBounds()); + assertEquals(config1.densityDpi, clientToken.mConfiguration.densityDpi); + assertEquals(mDisplayContent.mDisplayId, clientToken.mDisplayId); + + // Update the WindowContainer. + final WindowContainer container = createTestWindowToken(TYPE_APPLICATION_OVERLAY, + mDefaultDisplay); + final Configuration config2 = container.getConfiguration(); + final Rect bounds2 = new Rect(0, 0, 20, 20); + config2.windowConfiguration.setBounds(bounds2); + config2.densityDpi = 200; + container.onRequestedOverrideConfigurationChanged(config2); + + mController.registerWindowContainerListener(clientToken, container, -1); + + assertEquals(bounds2, clientToken.mConfiguration.windowConfiguration.getBounds()); + assertEquals(config2.densityDpi, clientToken.mConfiguration.densityDpi); + assertEquals(DEFAULT_DISPLAY, clientToken.mDisplayId); + + // Update the configuration of WindowContainer. + container.onRequestedOverrideConfigurationChanged(config1); + + assertEquals(bounds1, clientToken.mConfiguration.windowConfiguration.getBounds()); + assertEquals(config1.densityDpi, clientToken.mConfiguration.densityDpi); + + // Update the display of WindowContainer. + container.onDisplayChanged(mDisplayContent); + + assertEquals(mDisplayContent.mDisplayId, clientToken.mDisplayId); + } + + @Test + public void testCanCallerRemoveListener_NullListener_ReturnFalse() { + assertFalse(mController.assertCallerCanRemoveListener(mClientToken, + true /* callerCanManagerAppTokens */, TEST_UID)); + } + + @Test + public void testCanCallerRemoveListener_CanManageAppTokens_ReturnTrue() { + mController.registerWindowContainerListener(mClientToken, mContainer, TEST_UID); + + assertTrue(mController.assertCallerCanRemoveListener(mClientToken, + true /* callerCanManagerAppTokens */, ANOTHER_UID)); + } + + @Test + public void testCanCallerRemoveListener_SameUid_ReturnTrue() { + mController.registerWindowContainerListener(mClientToken, mContainer, TEST_UID); + + assertTrue(mController.assertCallerCanRemoveListener(mClientToken, + false /* callerCanManagerAppTokens */, TEST_UID)); + } + + @Test(expected = UnsupportedOperationException.class) + public void testCanCallerRemoveListener_DifferentUid_ThrowException() { + mController.registerWindowContainerListener(mClientToken, mContainer, TEST_UID); + + mController.assertCallerCanRemoveListener(mClientToken, + false /* callerCanManagerAppTokens */, ANOTHER_UID); + } + + private class TestWindowTokenClient extends IWindowToken.Stub { + private Configuration mConfiguration; + private int mDisplayId; + private boolean mRemoved; + + @Override + public void onConfigurationChanged(Configuration configuration, int displayId) { + mConfiguration = configuration; + mDisplayId = displayId; + } + + @Override + public void onWindowTokenRemoved() { + mRemoved = true; + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java index e0785c13a336..462df300fdfd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java @@ -231,7 +231,7 @@ public class WindowTokenTests extends WindowTestsBase { @Test public void testWindowAttachedWithOptions() { - BiFunction<WindowToken, Bundle, RootDisplayArea> selectFunc = + BiFunction<Integer, Bundle, RootDisplayArea> selectFunc = ((DisplayAreaPolicyBuilder.Result) mDisplayContent.mDisplayAreaPolicy) .mSelectRootForWindowFunc; spyOn(selectFunc); @@ -241,7 +241,7 @@ public class WindowTokenTests extends WindowTestsBase { true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */, false /* fromClientToken */, null /* options */); - verify(selectFunc).apply(token1, null); + verify(selectFunc).apply(token1.windowType, null); final Bundle options = new Bundle(); final WindowToken token2 = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), @@ -249,6 +249,6 @@ public class WindowTokenTests extends WindowTestsBase { true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */, false /* fromClientToken */, options /* options */); - verify(selectFunc).apply(token2, options); + verify(selectFunc).apply(token2.windowType, options); } } diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java index 3104c7e7e0a1..1a0e5269d51a 100644 --- a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java +++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java @@ -34,6 +34,7 @@ import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; +import android.provider.DeviceConfig; import android.util.ArraySet; import android.util.Log; @@ -42,15 +43,20 @@ import com.android.server.IoThread; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.pm.BackgroundDexOptService; +import com.android.server.pm.PackageManagerService; import com.android.server.wm.ActivityMetricsLaunchObserver; import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto; import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature; import com.android.server.wm.ActivityMetricsLaunchObserverRegistry; import com.android.server.wm.ActivityTaskManagerInternal; +import java.time.Duration; import java.util.ArrayList; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BooleanSupplier; import java.util.HashMap; +import java.util.List; /** * System-server-local proxy into the {@code IIorap} native service. @@ -65,6 +71,7 @@ public class IorapForwardingService extends SystemService { /** $> adb shell 'setprop iorapd.forwarding_service.wtf_crash true' */ private static boolean WTF_CRASH = SystemProperties.getBoolean( "iorapd.forwarding_service.wtf_crash", false); + private static final Duration TIMEOUT = Duration.ofSeconds(600L); // "Unique" job ID from the service name. Also equal to 283673059. public static final int JOB_ID_IORAPD = encodeEnglishAlphabetStringIntoInt("iorapd"); @@ -80,6 +87,12 @@ public class IorapForwardingService extends SystemService { private volatile IorapdJobService mJobService; // Write-once (null -> non-null forever). private volatile static IorapForwardingService sSelfService; // Write once (null -> non-null). + + /** + * Atomics set to true if the JobScheduler requests an abort. + */ + private final AtomicBoolean mAbortIdleCompilation = new AtomicBoolean(false); + /** * Initializes the system service. * <p> @@ -154,9 +167,27 @@ public class IorapForwardingService extends SystemService { @VisibleForTesting protected boolean isIorapEnabled() { + // These two mendel flags should match those in iorapd native process + // system/iorapd/src/common/property.h + boolean isTracingEnabled = + getMendelFlag("iorap_perfetto_enable", "iorapd.perfetto.enable", false); + boolean isReadAheadEnabled = + getMendelFlag("iorap_readahead_enable", "iorapd.readahead.enable", false); // Same as the property in iorapd.rc -- disabling this will mean the 'iorapd' binder process // never comes up, so all binder connections will fail indefinitely. - return IS_ENABLED; + return IS_ENABLED && (isTracingEnabled || isReadAheadEnabled); + } + + private boolean getMendelFlag(String mendelFlag, String sysProperty, boolean defaultValue) { + // TODO(yawanng) use DeviceConfig to get mendel property. + // DeviceConfig doesn't work and the reason is not clear. + // Provider service is already up before IORapForwardService. + String mendelProperty = "persist.device_config." + + DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT + + "." + + mendelFlag; + return SystemProperties.getBoolean(mendelProperty, + SystemProperties.getBoolean(sysProperty, defaultValue)); } //</editor-fold> @@ -239,7 +270,9 @@ public class IorapForwardingService extends SystemService { // // TODO: it would be good to get nodified of 'adb shell stop iorapd' to avoid // printing this warning. - Log.w(TAG, "Failed to connect to iorapd, is it down? Delay for " + sleepTime); + if (DEBUG) { + Log.v(TAG, "Failed to connect to iorapd, is it down? Delay for " + sleepTime); + } // Use a handler instead of Thread#sleep to avoid backing up the binder thread // when this is called from the death recipient callback. @@ -275,7 +308,9 @@ public class IorapForwardingService extends SystemService { // Connect to the native binder service. mIorapRemote = provideIorapRemote(); if (mIorapRemote == null) { - Log.e(TAG, "connectToRemoteAndConfigure - null iorap remote. check for Log.wtf?"); + if (DEBUG) { + Log.e(TAG, "connectToRemoteAndConfigure - null iorap remote. check for Log.wtf?"); + } return false; } invokeRemote(mIorapRemote, @@ -542,32 +577,86 @@ public class IorapForwardingService extends SystemService { // Tell iorapd to start a background job. Log.d(TAG, "Starting background job: " + params.toString()); - // We wait until that job's sequence ID returns to us with 'Completed', - RequestId request; - synchronized (mLock) { - // TODO: would be cleaner if we got the request from the 'invokeRemote' function. - // Better yet, consider a Pair<RequestId, Future<TaskResult>> or similar. - request = RequestId.nextValueForSequence(); - mRunningJobs.put(request, params); - } + mAbortIdleCompilation.set(false); + // PackageManagerService starts before IORap service. + PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package"); + List<String> pkgs = pm.getAllPackages(); + runIdleCompilationAsync(params, pkgs); + return true; + } - if (!invokeRemote(mIorapRemote, (IIorap remote) -> - remote.onJobScheduledEvent(request, - JobScheduledEvent.createIdleMaintenance( - JobScheduledEvent.TYPE_START_JOB, - params)) - )) { - synchronized (mLock) { - mRunningJobs.remove(request); // Avoid memory leaks. + private void runIdleCompilationAsync(final JobParameters params, final List<String> pkgs) { + new Thread("IORap_IdleCompilation") { + @Override + public void run() { + for (int i = 0; i < pkgs.size(); i++) { + String pkg = pkgs.get(i); + if (mAbortIdleCompilation.get()) { + Log.i(TAG, "The idle compilation is aborted"); + return; + } + + // We wait until that job's sequence ID returns to us with 'Completed', + RequestId request; + synchronized (mLock) { + // TODO: would be cleaner if we got the request from the + // 'invokeRemote' function. Better yet, consider + // a Pair<RequestId, Future<TaskResult>> or similar. + request = RequestId.nextValueForSequence(); + mRunningJobs.put(request, params); + } + + Log.i(TAG, String.format("IORap compile package: %s, [%d/%d]", + pkg, i + 1, pkgs.size())); + boolean shouldUpdateVersions = (i == 0); + if (!invokeRemote(mIorapRemote, (IIorap remote) -> + remote.onJobScheduledEvent(request, + JobScheduledEvent.createIdleMaintenance( + JobScheduledEvent.TYPE_START_JOB, + params, + pkg, + shouldUpdateVersions)))) { + synchronized (mLock) { + mRunningJobs.remove(request); // Avoid memory leaks. + } + } + + // Wait until the job is complete and removed from the running jobs. + retryWithTimeout(TIMEOUT, () -> { + synchronized (mLock) { + return !mRunningJobs.containsKey(request); + } + }); + } + + // Finish the job after all packages are compiled. + if (mProxy != null) { + mProxy.jobFinished(params, /*reschedule*/false); + } } + }.start(); + } - // Something went wrong on the remote side. Treat the job as being - // 'already finished' (i.e. immediately release wake lock). - return false; - } + /** Retry until timeout. */ + private boolean retryWithTimeout(final Duration timeout, BooleanSupplier supplier) { + long totalSleepTimeMs = 0L; + long sleepIntervalMs = 10L; + while (true) { + if (supplier.getAsBoolean()) { + return true; + } + try { + TimeUnit.MILLISECONDS.sleep(sleepIntervalMs); + } catch (InterruptedException e) { + Log.e(TAG, e.getMessage()); + return false; + } - // True -> keep the wakelock acquired until #jobFinished is called. - return true; + totalSleepTimeMs += sleepIntervalMs; + if (totalSleepTimeMs > timeout.toMillis()) { + return false; + } + } } // Called by system to prematurely stop the job. @@ -575,32 +664,7 @@ public class IorapForwardingService extends SystemService { public boolean onStopJob(JobParameters params) { // As this is unexpected behavior, print a warning. Log.w(TAG, "onStopJob(params=" + params.toString() + ")"); - - // No longer track this job (avoids a memory leak). - boolean wasTracking = false; - synchronized (mLock) { - for (HashMap.Entry<RequestId, JobParameters> entry : mRunningJobs.entrySet()) { - if (entry.getValue().getJobId() == params.getJobId()) { - mRunningJobs.remove(entry.getKey()); - wasTracking = true; - } - } - } - - // Notify iorapd to stop (abort) the job. - if (wasTracking) { - invokeRemote(mIorapRemote, (IIorap remote) -> - remote.onJobScheduledEvent(RequestId.nextValueForSequence(), - JobScheduledEvent.createIdleMaintenance( - JobScheduledEvent.TYPE_STOP_JOB, - params)) - ); - } else { - // Even weirder. This could only be considered "correct" if iorapd reported success - // concurrently to the JobService requesting an onStopJob. - Log.e(TAG, "Untracked onStopJob request"); // see above Log.w for the params. - } - + mAbortIdleCompilation.set(true); // Yes, retry the job at a later time no matter what. return true; @@ -626,18 +690,6 @@ public class IorapForwardingService extends SystemService { } Log.d(TAG, "Finished background job: " + jobParameters.toString()); - - // Job is successful and periodic. Do not 'reschedule' according to the back-off - // criteria. - // - // This releases the wakelock that was acquired in #onStartJob. - - IorapdJobServiceProxy proxy = mProxy; - if (proxy != null) { - proxy.jobFinished(jobParameters, /*reschedule*/false); - } - // Cannot call 'jobFinished' on 'this' because it was not constructed - // from the JobService, so it would get an NPE when calling mEngine. } public void onIorapdDisconnected() { diff --git a/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java b/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java index 2055b206dd7a..b91dd71fd28c 100644 --- a/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java +++ b/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java @@ -55,6 +55,10 @@ public class JobScheduledEvent implements Parcelable { /** @see JobParameters#getJobId() */ public final int jobId; + public final String packageName; + + public final boolean shouldUpdateVersions; + /** Device is 'idle' and it's charging (plugged in). */ public static final int SORT_IDLE_MAINTENANCE = 0; private static final int SORT_MAX = 0; @@ -77,14 +81,22 @@ public class JobScheduledEvent implements Parcelable { * Only the job ID is retained from {@code jobParams}, all other param info is dropped. */ @NonNull - public static JobScheduledEvent createIdleMaintenance(@Type int type, JobParameters jobParams) { - return new JobScheduledEvent(type, jobParams.getJobId(), SORT_IDLE_MAINTENANCE); + public static JobScheduledEvent createIdleMaintenance( + @Type int type, JobParameters jobParams, String packageName, boolean shouldUpdateVersions) { + return new JobScheduledEvent( + type, jobParams.getJobId(), SORT_IDLE_MAINTENANCE, packageName, shouldUpdateVersions); } - private JobScheduledEvent(@Type int type, int jobId, @Sort int sort) { + private JobScheduledEvent(@Type int type, + int jobId, + @Sort int sort, + String packageName, + boolean shouldUpdateVersions) { this.type = type; this.jobId = jobId; this.sort = sort; + this.packageName = packageName; + this.shouldUpdateVersions = shouldUpdateVersions; checkConstructorArguments(); } @@ -108,12 +120,16 @@ public class JobScheduledEvent implements Parcelable { private boolean equals(JobScheduledEvent other) { return type == other.type && jobId == other.jobId && - sort == other.sort; + sort == other.sort && + packageName.equals(other.packageName) && + shouldUpdateVersions == other.shouldUpdateVersions; } @Override public String toString() { - return String.format("{type: %d, jobId: %d, sort: %d}", type, jobId, sort); + return String.format( + "{type: %d, jobId: %d, sort: %d, packageName: %s, shouldUpdateVersions %b}", + type, jobId, sort, packageName, shouldUpdateVersions); } //<editor-fold desc="Binder boilerplate"> @@ -122,6 +138,8 @@ public class JobScheduledEvent implements Parcelable { out.writeInt(type); out.writeInt(jobId); out.writeInt(sort); + out.writeString(packageName); + out.writeBoolean(shouldUpdateVersions); // We do not parcel the entire JobParameters here because there is no C++ equivalent // of that class [which the iorapd side of the binder interface requires]. @@ -131,6 +149,8 @@ public class JobScheduledEvent implements Parcelable { this.type = in.readInt(); this.jobId = in.readInt(); this.sort = in.readInt(); + this.packageName = in.readString(); + this.shouldUpdateVersions = in.readBoolean(); checkConstructorArguments(); } diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 960b0df40061..5b03863efc7d 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -268,10 +268,69 @@ public class TelecomManager { /** * Optional extra for {@link android.content.Intent#ACTION_CALL} containing a string call * subject which will be associated with an outgoing call. Should only be specified if the - * {@link PhoneAccount} supports the capability {@link PhoneAccount#CAPABILITY_CALL_SUBJECT}. + * {@link PhoneAccount} supports the capability {@link PhoneAccount#CAPABILITY_CALL_SUBJECT} + * or {@link PhoneAccount#CAPABILITY_CALL_COMPOSER}. */ public static final String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT"; + // Values for EXTRA_PRIORITY + /** + * Indicates the call composer call priority is normal. + * + * Reference: RCC.20 Section 2.4.4.2 + */ + public static final int PRIORITY_NORMAL = 0; + + /** + * Indicates the call composer call priority is urgent. + * + * Reference: RCC.20 Section 2.4.4.2 + */ + public static final int PRIORITY_URGENT = 1; + + /** + * Extra for the call composer call priority, either {@link #PRIORITY_NORMAL} or + * {@link #PRIORITY_URGENT}. + * + * Reference: RCC.20 Section 2.4.4.2 + */ + public static final String EXTRA_PRIORITY = "android.telecom.extra.PRIORITY"; + + /** + * Extra for the call composer call location, an {@link android.location.Location} parcelable + * class to represent the geolocation as a latitude and longitude pair. + * + * Reference: RCC.20 Section 2.4.3.2 + */ + public static final String EXTRA_LOCATION = "android.telecom.extra.LOCATION"; + + /** + * A boolean extra set on incoming calls to indicate that the call has a picture specified. + * Given that image download could take a (short) time, the EXTRA is set immediately upon + * adding the call to the Dialer app, this allows the Dialer app to reserve space for an image + * if one is expected. The EXTRA may be unset if the image download ends up failing for some + * reason. + */ + public static final String EXTRA_HAS_PICTURE = "android.telecom.extra.HAS_PICTURE"; + + /** + * A URI representing the picture that was downloaded when a call is received. + * This is a content URI within the call log provider which can be used to open a file + * descriptor. This could be set a short time after a call is added to the Dialer app if the + * download is delayed for some reason. The Dialer app will receive a callback via + * {@link Call.Callback#onDetailsChanged} when this value has changed. + * + * Reference: RCC.20 Section 2.4.3.2 + */ + public static final String EXTRA_INCOMING_PICTURE = "android.telecom.extra.INCOMING_PICTURE"; + + // TODO(hallliu), This UUID is obtained from TelephonyManager#uploadCallComposerPicture. + /** + * A ParcelUuid used as a token to represent a picture that was uploaded prior to the call + * being placed. + */ + public static final String EXTRA_OUTGOING_PICTURE = "android.telecom.extra.OUTGOING_PICTURE"; + /** * The extra used by a {@link ConnectionService} to provide the handle of the caller that * has initiated a new incoming call. diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java index b381ccecde47..189a4b898886 100644 --- a/telephony/java/android/telephony/CellInfo.java +++ b/telephony/java/android/telephony/CellInfo.java @@ -21,7 +21,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.hardware.radio.V1_4.CellInfo.Info; -import android.hardware.radio.V1_5.CellInfo.CellInfoRatSpecificInfo; import android.os.Parcel; import android.os.Parcelable; @@ -352,6 +351,13 @@ public abstract class CellInfo implements Parcelable { } /** @hide */ + protected CellInfo(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + this.mRegistered = ci.registered; + this.mTimeStamp = timeStamp; + this.mCellConnectionStatus = ci.connectionStatus; + } + + /** @hide */ public static CellInfo create(android.hardware.radio.V1_0.CellInfo ci) { if (ci == null) return null; switch(ci.cellInfoType) { @@ -395,17 +401,49 @@ public abstract class CellInfo implements Parcelable { public static CellInfo create(android.hardware.radio.V1_5.CellInfo ci, long timeStamp) { if (ci == null) return null; switch (ci.ratSpecificInfo.getDiscriminator()) { - case CellInfoRatSpecificInfo.hidl_discriminator.gsm: + case android.hardware.radio.V1_5.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.gsm: + return new CellInfoGsm(ci, timeStamp); + case android.hardware.radio.V1_5.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.cdma: + return new CellInfoCdma(ci, timeStamp); + case android.hardware.radio.V1_5.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.lte: + return new CellInfoLte(ci, timeStamp); + case android.hardware.radio.V1_5.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.wcdma: + return new CellInfoWcdma(ci, timeStamp); + case android.hardware.radio.V1_5.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.tdscdma: + return new CellInfoTdscdma(ci, timeStamp); + case android.hardware.radio.V1_5.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.nr: + return new CellInfoNr(ci, timeStamp); + default: return null; + } + } + + /** @hide */ + public static CellInfo create(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + if (ci == null) return null; + switch (ci.ratSpecificInfo.getDiscriminator()) { + case android.hardware.radio.V1_6.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.gsm: return new CellInfoGsm(ci, timeStamp); - case CellInfoRatSpecificInfo.hidl_discriminator.cdma: + case android.hardware.radio.V1_6.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.cdma: return new CellInfoCdma(ci, timeStamp); - case CellInfoRatSpecificInfo.hidl_discriminator.lte: + case android.hardware.radio.V1_6.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.lte: return new CellInfoLte(ci, timeStamp); - case CellInfoRatSpecificInfo.hidl_discriminator.wcdma: + case android.hardware.radio.V1_6.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.wcdma: return new CellInfoWcdma(ci, timeStamp); - case CellInfoRatSpecificInfo.hidl_discriminator.tdscdma: + case android.hardware.radio.V1_6.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.tdscdma: return new CellInfoTdscdma(ci, timeStamp); - case CellInfoRatSpecificInfo.hidl_discriminator.nr: + case android.hardware.radio.V1_6.CellInfo + .CellInfoRatSpecificInfo.hidl_discriminator.nr: return new CellInfoNr(ci, timeStamp); default: return null; } diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java index 3ce99facfdb1..dbb30d29eb87 100644 --- a/telephony/java/android/telephony/CellInfoCdma.java +++ b/telephony/java/android/telephony/CellInfoCdma.java @@ -87,6 +87,15 @@ public final class CellInfoCdma extends CellInfo implements Parcelable { new CellSignalStrengthCdma(cic.signalStrengthCdma, cic.signalStrengthEvdo); } + /** @hide */ + public CellInfoCdma(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + super(ci, timeStamp); + final android.hardware.radio.V1_2.CellInfoCdma cic = ci.ratSpecificInfo.cdma(); + mCellIdentityCdma = new CellIdentityCdma(cic.cellIdentityCdma); + mCellSignalStrengthCdma = + new CellSignalStrengthCdma(cic.signalStrengthCdma, cic.signalStrengthEvdo); + } + /** * @return a {@link CellIdentityCdma} instance. */ diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java index e296e613362a..e1d996e87fcf 100644 --- a/telephony/java/android/telephony/CellInfoGsm.java +++ b/telephony/java/android/telephony/CellInfoGsm.java @@ -82,6 +82,14 @@ public final class CellInfoGsm extends CellInfo implements Parcelable { mCellSignalStrengthGsm = new CellSignalStrengthGsm(cig.signalStrengthGsm); } + /** @hide */ + public CellInfoGsm(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + super(ci, timeStamp); + final android.hardware.radio.V1_5.CellInfoGsm cig = ci.ratSpecificInfo.gsm(); + mCellIdentityGsm = new CellIdentityGsm(cig.cellIdentityGsm); + mCellSignalStrengthGsm = new CellSignalStrengthGsm(cig.signalStrengthGsm); + } + /** * @return a {@link CellIdentityGsm} instance. */ diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java index 6f812341756b..39b320afdac9 100644 --- a/telephony/java/android/telephony/CellInfoLte.java +++ b/telephony/java/android/telephony/CellInfoLte.java @@ -91,6 +91,15 @@ public final class CellInfoLte extends CellInfo implements Parcelable { mCellConfig = new CellConfigLte(); } + /** @hide */ + public CellInfoLte(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + super(ci, timeStamp); + final android.hardware.radio.V1_6.CellInfoLte cil = ci.ratSpecificInfo.lte(); + mCellIdentityLte = new CellIdentityLte(cil.cellIdentityLte); + mCellSignalStrengthLte = new CellSignalStrengthLte(cil.signalStrengthLte); + mCellConfig = new CellConfigLte(); + } + /** * @return a {@link CellIdentityLte} instance. */ diff --git a/telephony/java/android/telephony/CellInfoNr.java b/telephony/java/android/telephony/CellInfoNr.java index e01e8f0d5b51..12e6a38bfdc0 100644 --- a/telephony/java/android/telephony/CellInfoNr.java +++ b/telephony/java/android/telephony/CellInfoNr.java @@ -68,6 +68,14 @@ public final class CellInfoNr extends CellInfo { mCellSignalStrength = new CellSignalStrengthNr(cil.signalStrengthNr); } + /** @hide */ + public CellInfoNr(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + super(ci, timeStamp); + final android.hardware.radio.V1_6.CellInfoNr cil = ci.ratSpecificInfo.nr(); + mCellIdentity = new CellIdentityNr(cil.cellIdentityNr); + mCellSignalStrength = new CellSignalStrengthNr(cil.signalStrengthNr); + } + /** * @return a {@link CellIdentityNr} instance. */ diff --git a/telephony/java/android/telephony/CellInfoTdscdma.java b/telephony/java/android/telephony/CellInfoTdscdma.java index 038c49ac37ee..994b317a47fd 100644 --- a/telephony/java/android/telephony/CellInfoTdscdma.java +++ b/telephony/java/android/telephony/CellInfoTdscdma.java @@ -85,6 +85,14 @@ public final class CellInfoTdscdma extends CellInfo implements Parcelable { mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(cit.signalStrengthTdscdma); } + /** @hide */ + public CellInfoTdscdma(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + super(ci, timeStamp); + final android.hardware.radio.V1_5.CellInfoTdscdma cit = ci.ratSpecificInfo.tdscdma(); + mCellIdentityTdscdma = new CellIdentityTdscdma(cit.cellIdentityTdscdma); + mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(cit.signalStrengthTdscdma); + } + /** * @return a {@link CellIdentityTdscdma} instance. */ diff --git a/telephony/java/android/telephony/CellInfoWcdma.java b/telephony/java/android/telephony/CellInfoWcdma.java index c74955f807b0..62ac0b8ae04e 100644 --- a/telephony/java/android/telephony/CellInfoWcdma.java +++ b/telephony/java/android/telephony/CellInfoWcdma.java @@ -80,6 +80,14 @@ public final class CellInfoWcdma extends CellInfo implements Parcelable { mCellSignalStrengthWcdma = new CellSignalStrengthWcdma(ciw.signalStrengthWcdma); } + /** @hide */ + public CellInfoWcdma(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) { + super(ci, timeStamp); + final android.hardware.radio.V1_5.CellInfoWcdma ciw = ci.ratSpecificInfo.wcdma(); + mCellIdentityWcdma = new CellIdentityWcdma(ciw.cellIdentityWcdma); + mCellSignalStrengthWcdma = new CellSignalStrengthWcdma(ciw.signalStrengthWcdma); + } + /** * @return a {@link CellIdentityWcdma} instance. */ diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java index 47a8f72a2337..db7d10ae8ce4 100644 --- a/telephony/java/android/telephony/CellSignalStrengthLte.java +++ b/telephony/java/android/telephony/CellSignalStrengthLte.java @@ -86,6 +86,15 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P private int mRsrq; @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P) private int mRssnr; + /** + * CSI channel quality indicator (CQI) table index. There are multiple CQI tables. + * The definition of CQI in each table is different. + * + * Reference: 3GPP TS 136.213 section 7.2.3. + * + * Range [1, 6]. + */ + private int mCqiTableIndex; @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P) private int mCqi; @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P) @@ -120,24 +129,42 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P * @param rsrp in dBm [-140,-43], UNKNOWN * @param rsrq in dB [-34, 3], UNKNOWN * @param rssnr in dB [-20, +30], UNKNOWN + * @param cqiTableIndex [1, 6], UNKNOWN * @param cqi [0, 15], UNKNOWN * @param timingAdvance [0, 1282], UNKNOWN * */ /** @hide */ - public CellSignalStrengthLte(int rssi, int rsrp, int rsrq, int rssnr, int cqi, - int timingAdvance) { - + public CellSignalStrengthLte(int rssi, int rsrp, int rsrq, int rssnr, int cqiTableIndex, + int cqi, int timingAdvance) { mRssi = inRangeOrUnavailable(rssi, -113, -51); mSignalStrength = mRssi; mRsrp = inRangeOrUnavailable(rsrp, -140, -43); mRsrq = inRangeOrUnavailable(rsrq, -34, 3); mRssnr = inRangeOrUnavailable(rssnr, -20, 30); + mCqiTableIndex = inRangeOrUnavailable(cqiTableIndex, 1, 6); mCqi = inRangeOrUnavailable(cqi, 0, 15); mTimingAdvance = inRangeOrUnavailable(timingAdvance, 0, 1282); updateLevel(null, null); } + /** + * Construct a cell signal strength + * + * @param rssi in dBm [-113,-51], UNKNOWN + * @param rsrp in dBm [-140,-43], UNKNOWN + * @param rsrq in dB [-34, 3], UNKNOWN + * @param rssnr in dB [-20, +30], UNKNOWN + * @param cqi [0, 15], UNKNOWN + * @param timingAdvance [0, 1282], UNKNOWN + * + */ + /** @hide */ + public CellSignalStrengthLte(int rssi, int rsrp, int rsrq, int rssnr, int cqi, + int timingAdvance) { + this(rssi, rsrp, rsrq, rssnr, CellInfo.UNAVAILABLE, cqi, timingAdvance); + } + /** @hide */ public CellSignalStrengthLte(android.hardware.radio.V1_0.LteSignalStrength lte) { // Convert from HAL values as part of construction. @@ -148,6 +175,16 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P } /** @hide */ + public CellSignalStrengthLte(android.hardware.radio.V1_6.LteSignalStrength lte) { + // Convert from HAL values as part of construction. + this(convertRssiAsuToDBm(lte.base.signalStrength), + lte.base.rsrp != CellInfo.UNAVAILABLE ? -lte.base.rsrp : lte.base.rsrp, + lte.base.rsrq != CellInfo.UNAVAILABLE ? -lte.base.rsrq : lte.base.rsrq, + convertRssnrUnitFromTenDbToDB(lte.base.rssnr), lte.cqiTableIndex, lte.base.cqi, + lte.base.timingAdvance); + } + + /** @hide */ public CellSignalStrengthLte(CellSignalStrengthLte s) { copyFrom(s); } @@ -159,6 +196,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P mRsrp = s.mRsrp; mRsrq = s.mRsrq; mRssnr = s.mRssnr; + mCqiTableIndex = s.mCqiTableIndex; mCqi = s.mCqi; mTimingAdvance = s.mTimingAdvance; mLevel = s.mLevel; @@ -179,6 +217,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P mRsrp = CellInfo.UNAVAILABLE; mRsrq = CellInfo.UNAVAILABLE; mRssnr = CellInfo.UNAVAILABLE; + mCqiTableIndex = CellInfo.UNAVAILABLE; mCqi = CellInfo.UNAVAILABLE; mTimingAdvance = CellInfo.UNAVAILABLE; mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; @@ -402,6 +441,17 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P } /** + * Get table index for channel quality indicator + * + * @return the CQI table index if available or + * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable. + */ + /** @hide */ + public int getCqiTableIndex() { + return mCqiTableIndex; + } + + /** * Get channel quality indicator * * @return the CQI if available or @@ -454,7 +504,8 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P @Override public int hashCode() { - return Objects.hash(mRssi, mRsrp, mRsrq, mRssnr, mCqi, mTimingAdvance, mLevel); + return Objects.hash(mRssi, mRsrp, mRsrq, mRssnr, mCqiTableIndex, mCqi, mTimingAdvance, + mLevel); } private static final CellSignalStrengthLte sInvalid = new CellSignalStrengthLte(); @@ -476,6 +527,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P && mRsrp == s.mRsrp && mRsrq == s.mRsrq && mRssnr == s.mRssnr + && mCqiTableIndex == s.mCqiTableIndex && mCqi == s.mCqi && mTimingAdvance == s.mTimingAdvance && mLevel == s.mLevel; @@ -491,6 +543,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P + " rsrp=" + mRsrp + " rsrq=" + mRsrq + " rssnr=" + mRssnr + + " cqiTableIndex=" + mCqiTableIndex + " cqi=" + mCqi + " ta=" + mTimingAdvance + " level=" + mLevel @@ -508,6 +561,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P dest.writeInt(mRsrp); dest.writeInt(mRsrq); dest.writeInt(mRssnr); + dest.writeInt(mCqiTableIndex); dest.writeInt(mCqi); dest.writeInt(mTimingAdvance); dest.writeInt(mLevel); @@ -523,6 +577,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P mRsrp = in.readInt(); mRsrq = in.readInt(); mRssnr = in.readInt(); + mCqiTableIndex = in.readInt(); mCqi = in.readInt(); mTimingAdvance = in.readInt(); mLevel = in.readInt(); diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java index 766019ec382a..1518190bb7f7 100644 --- a/telephony/java/android/telephony/CellSignalStrengthNr.java +++ b/telephony/java/android/telephony/CellSignalStrengthNr.java @@ -18,6 +18,7 @@ package android.telephony; import android.annotation.IntDef; import android.annotation.IntRange; +import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; @@ -27,7 +28,10 @@ import com.android.telephony.Rlog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; /** * 5G NR signal strength related information. @@ -109,6 +113,28 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa private int mCsiRsrp; private int mCsiRsrq; private int mCsiSinr; + /** + * CSI channel quality indicator (CQI) table index. There are multiple CQI tables. + * The definition of CQI in each table is different. + * + * Reference: 3GPP TS 138.214 section 5.2.2.1. + * + * Range [1, 3]. + */ + private int mCsiCqiTableIndex; + /** + * CSI channel quality indicators (CQI) for all subbands. + * + * If the CQI report is for the entire wideband, a single CQI index is provided. + * If the CQI report is for all subbands, one CQI index is provided for each subband, + * in ascending order of subband index. + * If CQI is not available, the CQI report is empty. + * + * Reference: 3GPP TS 138.214 section 5.2.2.1. + * + * Range [0, 15] for each CQI. + */ + private List<Integer> mCsiCqiReport;; private int mSsRsrp; private int mSsRsrq; private int mSsSinr; @@ -138,16 +164,22 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa * @param csiRsrp CSI reference signal received power. * @param csiRsrq CSI reference signal received quality. * @param csiSinr CSI signal-to-noise and interference ratio. + * @param csiCqiTableIndex CSI CSI channel quality indicator (CQI) table index. + * @param csiCqiReport CSI channel quality indicators (CQI) for all subbands. * @param ssRsrp SS reference signal received power. * @param ssRsrq SS reference signal received quality. * @param ssSinr SS signal-to-noise and interference ratio. * @hide */ - public CellSignalStrengthNr( - int csiRsrp, int csiRsrq, int csiSinr, int ssRsrp, int ssRsrq, int ssSinr) { + public CellSignalStrengthNr(int csiRsrp, int csiRsrq, int csiSinr, int csiCqiTableIndex, + List<Integer> csiCqiReport, int ssRsrp, int ssRsrq, int ssSinr) { mCsiRsrp = inRangeOrUnavailable(csiRsrp, -140, -44); mCsiRsrq = inRangeOrUnavailable(csiRsrq, -20, -3); mCsiSinr = inRangeOrUnavailable(csiSinr, -23, 23); + mCsiCqiTableIndex = inRangeOrUnavailable(csiCqiTableIndex, 1, 3); + mCsiCqiReport = csiCqiReport.stream() + .map(cqi -> new Integer(inRangeOrUnavailable(cqi.intValue(), 1, 3))) + .collect(Collectors.toList()); mSsRsrp = inRangeOrUnavailable(ssRsrp, -140, -44); mSsRsrq = inRangeOrUnavailable(ssRsrq, -43, 20); mSsSinr = inRangeOrUnavailable(ssSinr, -23, 40); @@ -155,6 +187,21 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa } /** + * @param csiRsrp CSI reference signal received power. + * @param csiRsrq CSI reference signal received quality. + * @param csiSinr CSI signal-to-noise and interference ratio. + * @param ssRsrp SS reference signal received power. + * @param ssRsrq SS reference signal received quality. + * @param ssSinr SS signal-to-noise and interference ratio. + * @hide + */ + public CellSignalStrengthNr( + int csiRsrp, int csiRsrq, int csiSinr, int ssRsrp, int ssRsrq, int ssSinr) { + this(csiRsrp, csiRsrq, csiSinr, CellInfo.UNAVAILABLE, Collections.emptyList(), + ssRsrp, ssRsrq, ssSinr); + } + + /** * @hide * @param ss signal strength from modem. */ @@ -164,6 +211,15 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa } /** + * @hide + * @param ss signal strength from modem. + */ + public CellSignalStrengthNr(android.hardware.radio.V1_6.NrSignalStrength ss) { + this(flip(ss.base.csiRsrp), flip(ss.base.csiRsrq), ss.base.csiSinr, ss.csiCqiTableIndex, + ss.csiCqiReport, flip(ss.base.ssRsrp), flip(ss.base.ssRsrq), ss.base.ssSinr); + } + + /** * Flip sign cell strength value when taking in the value from hal * @param val cell strength value * @return flipped value @@ -232,6 +288,36 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa return mCsiSinr; } + /** + * Return CSI channel quality indicator (CQI) table index. There are multiple CQI tables. + * The definition of CQI in each table is different. + * + * Reference: 3GPP TS 138.214 section 5.2.2.1. + * + * Range [1, 3]. + */ + /** @hide */ + public int getCsiCqiTableIndex() { + return mCsiCqiTableIndex; + } + /** + * Return a list of CSI channel quality indicators (CQI) for all subbands. + * + * If the CQI report is for the entire wideband, a single CQI index is provided. + * If the CQI report is for all subbands, one CQI index is provided for each subband, + * in ascending order of subband index. + * If CQI is not available, the CQI report is empty. + * + * Reference: 3GPP TS 138.214 section 5.2.2.1. + * + * Range [0, 15] for each CQI. + */ + /** @hide */ + @NonNull + public List<Integer> getCsiCqiReport() { + return mCsiCqiReport; + } + @Override public int describeContents() { return 0; @@ -243,6 +329,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa dest.writeInt(mCsiRsrp); dest.writeInt(mCsiRsrq); dest.writeInt(mCsiSinr); + dest.writeInt(mCsiCqiTableIndex); + dest.writeList(mCsiCqiReport); dest.writeInt(mSsRsrp); dest.writeInt(mSsRsrq); dest.writeInt(mSsSinr); @@ -253,6 +341,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa mCsiRsrp = in.readInt(); mCsiRsrq = in.readInt(); mCsiSinr = in.readInt(); + mCsiCqiTableIndex = in.readInt(); + mCsiCqiReport = in.readArrayList(Integer.class.getClassLoader()); mSsRsrp = in.readInt(); mSsRsrq = in.readInt(); mSsSinr = in.readInt(); @@ -265,6 +355,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa mCsiRsrp = CellInfo.UNAVAILABLE; mCsiRsrq = CellInfo.UNAVAILABLE; mCsiSinr = CellInfo.UNAVAILABLE; + mCsiCqiTableIndex = CellInfo.UNAVAILABLE; + mCsiCqiReport = Collections.emptyList(); mSsRsrp = CellInfo.UNAVAILABLE; mSsRsrq = CellInfo.UNAVAILABLE; mSsSinr = CellInfo.UNAVAILABLE; @@ -408,6 +500,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa mCsiRsrp = s.mCsiRsrp; mCsiRsrq = s.mCsiRsrq; mCsiSinr = s.mCsiSinr; + mCsiCqiTableIndex = s.mCsiCqiTableIndex; + mCsiCqiReport = s.mCsiCqiReport; mSsRsrp = s.mSsRsrp; mSsRsrq = s.mSsRsrq; mSsSinr = s.mSsSinr; @@ -423,7 +517,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa @Override public int hashCode() { - return Objects.hash(mCsiRsrp, mCsiRsrq, mCsiSinr, mSsRsrp, mSsRsrq, mSsSinr, mLevel); + return Objects.hash(mCsiRsrp, mCsiRsrq, mCsiSinr, mCsiCqiTableIndex, + mCsiCqiReport, mSsRsrp, mSsRsrq, mSsSinr, mLevel); } private static final CellSignalStrengthNr sInvalid = new CellSignalStrengthNr(); @@ -439,6 +534,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa if (obj instanceof CellSignalStrengthNr) { CellSignalStrengthNr o = (CellSignalStrengthNr) obj; return mCsiRsrp == o.mCsiRsrp && mCsiRsrq == o.mCsiRsrq && mCsiSinr == o.mCsiSinr + && mCsiCqiTableIndex == o.mCsiCqiTableIndex + && mCsiCqiReport.equals(o.mCsiCqiReport) && mSsRsrp == o.mSsRsrp && mSsRsrq == o.mSsRsrq && mSsSinr == o.mSsSinr && mLevel == o.mLevel; } @@ -451,7 +548,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa .append(TAG + ":{") .append(" csiRsrp = " + mCsiRsrp) .append(" csiRsrq = " + mCsiRsrq) - .append(" csiSinr = " + mCsiSinr) + .append(" csiCqiTableIndex = " + mCsiCqiTableIndex) + .append(" csiCqiReport = " + mCsiCqiReport) .append(" ssRsrp = " + mSsRsrp) .append(" ssRsrq = " + mSsRsrq) .append(" ssSinr = " + mSsSinr) diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java index 7bd0bc0b69ce..b317c5557108 100644 --- a/telephony/java/android/telephony/SignalStrength.java +++ b/telephony/java/android/telephony/SignalStrength.java @@ -187,6 +187,21 @@ public class SignalStrength implements Parcelable { new CellSignalStrengthNr(signalStrength.nr)); } + /** + * Constructor for Radio HAL V1.6. + * + * @param signalStrength signal strength reported from modem. + * @hide + */ + public SignalStrength(android.hardware.radio.V1_6.SignalStrength signalStrength) { + this(new CellSignalStrengthCdma(signalStrength.cdma, signalStrength.evdo), + new CellSignalStrengthGsm(signalStrength.gsm), + new CellSignalStrengthWcdma(signalStrength.wcdma), + new CellSignalStrengthTdscdma(signalStrength.tdscdma), + new CellSignalStrengthLte(signalStrength.lte), + new CellSignalStrengthNr(signalStrength.nr)); + } + private CellSignalStrength getPrimary() { // This behavior is intended to replicate the legacy behavior of getLevel() by prioritizing // newer faster RATs for default/for display purposes. diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 886ec33af2b8..9e4228473c3e 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -12290,23 +12290,15 @@ public class TelephonyManager { @NonNull public Map<Integer, List<EmergencyNumber>> getEmergencyNumberList( @EmergencyServiceCategories int categories) { - Map<Integer, List<EmergencyNumber>> emergencyNumberList = new HashMap<>(); + Map<Integer, List<EmergencyNumber>> emergencyNumberListForCategories = new HashMap<>(); try { ITelephony telephony = getITelephony(); if (telephony != null) { - emergencyNumberList = telephony.getEmergencyNumberList( - mContext.getOpPackageName(), mContext.getAttributionTag()); - if (emergencyNumberList != null) { - for (Integer subscriptionId : emergencyNumberList.keySet()) { - List<EmergencyNumber> numberList = emergencyNumberList.get(subscriptionId); - for (EmergencyNumber number : numberList) { - if (!number.isInEmergencyServiceCategories(categories)) { - numberList.remove(number); - } - } - } - } - return emergencyNumberList; + Map<Integer, List<EmergencyNumber>> emergencyNumberList = + telephony.getEmergencyNumberList(mContext.getOpPackageName(), + mContext.getAttributionTag()); + emergencyNumberListForCategories = + filterEmergencyNumbersByCategories(emergencyNumberList, categories); } else { throw new IllegalStateException("telephony service is null."); } @@ -12314,7 +12306,34 @@ public class TelephonyManager { Log.e(TAG, "getEmergencyNumberList with Categories RemoteException", ex); ex.rethrowAsRuntimeException(); } - return emergencyNumberList; + return emergencyNumberListForCategories; + } + + /** + * Filter emergency numbers with categories. + * + * @hide + */ + @VisibleForTesting + public Map<Integer, List<EmergencyNumber>> filterEmergencyNumbersByCategories( + Map<Integer, List<EmergencyNumber>> emergencyNumberList, + @EmergencyServiceCategories int categories) { + Map<Integer, List<EmergencyNumber>> emergencyNumberListForCategories = new HashMap<>(); + if (emergencyNumberList != null) { + for (Integer subscriptionId : emergencyNumberList.keySet()) { + List<EmergencyNumber> allNumbersForSub = emergencyNumberList.get( + subscriptionId); + List<EmergencyNumber> numbersForCategoriesPerSub = new ArrayList<>(); + for (EmergencyNumber number : allNumbersForSub) { + if (number.isInEmergencyServiceCategories(categories)) { + numbersForCategoriesPerSub.add(number); + } + } + emergencyNumberListForCategories.put( + subscriptionId, numbersForCategoriesPerSub); + } + } + return emergencyNumberListForCategories; } /** diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java index e0290a5dc2af..e757d9f70ccc 100644 --- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java @@ -126,7 +126,7 @@ public class ImsConfigImplBase { */ @Override public synchronized String getConfigString(int item) throws RemoteException { - if (mProvisionedIntValue.containsKey(item)) { + if (mProvisionedStringValue.containsKey(item)) { return mProvisionedStringValue.get(item); } else { String retVal = getImsConfigImpl().getConfigString(item); diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt index 0db064ab79c5..9d4a71874f67 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt @@ -67,7 +67,7 @@ class ChangeAppRotationTest( val instrumentation = InstrumentationRegistry.getInstrumentation() val testApp = StandardAppHelper(instrumentation, "com.android.server.wm.flicker.testapp", "SimpleApp") - return FlickerTestRunnerFactory(instrumentation) + return FlickerTestRunnerFactory(instrumentation, repetitions = 10) .buildRotationTest { configuration -> withTestName { buildTestTag( diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index 9738e58543e1..104758de49f1 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -22,6 +22,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; @@ -43,10 +44,15 @@ import android.os.SystemProperties; import android.os.test.TestLooper; import android.provider.DeviceConfig; import android.util.AtomicFile; +import android.util.LongArrayQueue; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; +import android.util.Xml; import androidx.test.InstrumentationRegistry; import com.android.dx.mockito.inline.extended.ExtendedMockito; +import com.android.internal.util.XmlUtils; import com.android.server.PackageWatchdog.HealthCheckState; import com.android.server.PackageWatchdog.MonitoredPackage; import com.android.server.PackageWatchdog.PackageHealthObserver; @@ -64,6 +70,7 @@ import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import java.io.File; +import java.io.FileOutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -739,7 +746,8 @@ public class PackageWatchdogTest { false /* hasPassedHealthCheck */); MonitoredPackage m2 = wd.newMonitoredPackage(APP_B, LONG_DURATION, false); MonitoredPackage m3 = wd.newMonitoredPackage(APP_C, LONG_DURATION, false); - MonitoredPackage m4 = wd.newMonitoredPackage(APP_D, LONG_DURATION, SHORT_DURATION, true); + MonitoredPackage m4 = wd.newMonitoredPackage(APP_D, LONG_DURATION, SHORT_DURATION, true, + new LongArrayQueue()); // Verify transition: inactive -> active -> passed // Verify initially inactive @@ -1210,6 +1218,73 @@ public class PackageWatchdogTest { assertThat(observer.mMitigationCounts).isEqualTo(List.of(1, 2, 3, 3, 2, 3)); } + @Test + public void testNormalizingMitigationCalls() { + PackageWatchdog watchdog = createWatchdog(); + + LongArrayQueue mitigationCalls = new LongArrayQueue(); + mitigationCalls.addLast(1000); + mitigationCalls.addLast(2000); + mitigationCalls.addLast(3000); + + MonitoredPackage pkg = watchdog.newMonitoredPackage( + "test", 123, 456, true, mitigationCalls); + + // Make current system uptime 10000ms. + moveTimeForwardAndDispatch(9999); + + LongArrayQueue expectedCalls = pkg.normalizeMitigationCalls(); + + assertThat(expectedCalls.size()).isEqualTo(mitigationCalls.size()); + + for (int i = 0; i < mitigationCalls.size(); i++) { + assertThat(expectedCalls.get(i)).isEqualTo(mitigationCalls.get(i) - 10000); + } + } + + /** + * Ensure that a {@link MonitoredPackage} may be correctly written and read in order to persist + * across reboots. + */ + @Test + public void testWritingAndReadingMonitoredPackage() throws Exception { + PackageWatchdog watchdog = createWatchdog(); + + LongArrayQueue mitigationCalls = new LongArrayQueue(); + mitigationCalls.addLast(1000); + mitigationCalls.addLast(2000); + mitigationCalls.addLast(3000); + MonitoredPackage writePkg = watchdog.newMonitoredPackage( + "test.package", 1000, 2000, true, mitigationCalls); + + // Move time forward so that the current uptime is 4000ms. Therefore, the written mitigation + // calls will each be reduced by 4000. + moveTimeForwardAndDispatch(3999); + LongArrayQueue expectedCalls = new LongArrayQueue(); + expectedCalls.addLast(-3000); + expectedCalls.addLast(-2000); + expectedCalls.addLast(-1000); + MonitoredPackage expectedPkg = watchdog.newMonitoredPackage( + "test.package", 1000, 2000, true, expectedCalls); + + // Write the package + File tmpFile = File.createTempFile("package-watchdog-test", ".xml"); + AtomicFile testFile = new AtomicFile(tmpFile); + FileOutputStream stream = testFile.startWrite(); + TypedXmlSerializer outputSerializer = Xml.resolveSerializer(stream); + outputSerializer.startDocument(null, true); + writePkg.writeLocked(outputSerializer); + outputSerializer.endDocument(); + testFile.finishWrite(stream); + + // Read the package + TypedXmlPullParser parser = Xml.resolvePullParser(testFile.openRead()); + XmlUtils.beginDocument(parser, "package"); + MonitoredPackage readPkg = watchdog.parseMonitoredPackage(parser); + + assertTrue(readPkg.isEqualTo(expectedPkg)); + } + private void adoptShellPermissions(String... permissions) { InstrumentationRegistry .getInstrumentation() diff --git a/tests/StagedInstallTest/TEST_MAPPING b/tests/StagedInstallTest/TEST_MAPPING index 5a7a5a766b88..fa2a60b21b50 100644 --- a/tests/StagedInstallTest/TEST_MAPPING +++ b/tests/StagedInstallTest/TEST_MAPPING @@ -1,5 +1,5 @@ { - "presubmit": [ + "presubmit-large": [ { "name": "StagedInstallInternalTest" } diff --git a/wifi/TEST_MAPPING b/wifi/TEST_MAPPING index 8c515109a309..7ddc30872cb9 100644 --- a/wifi/TEST_MAPPING +++ b/wifi/TEST_MAPPING @@ -1,5 +1,5 @@ { - "presubmit": [ + "presubmit-large": [ { "name": "CtsWifiTestCases", "options": [ @@ -11,9 +11,6 @@ ], "mainline-presubmit": [ { - "name": "FrameworksWifiApiTests[com.google.android.wifi.apex]" - }, - { "name": "CtsWifiTestCases[com.google.android.wifi.apex]", "options": [ { diff --git a/wifi/api/current.txt b/wifi/api/current.txt index ce2b8ca4f2cd..e11b33efcc33 100644 --- a/wifi/api/current.txt +++ b/wifi/api/current.txt @@ -332,6 +332,7 @@ package android.net.wifi { method public boolean is5GHzBandSupported(); method public boolean is6GHzBandSupported(); method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isAutoWakeupEnabled(); + method public boolean isBridgedApConcurrencySupported(); method @Deprecated public boolean isDeviceToApRttSupported(); method public boolean isEasyConnectSupported(); method public boolean isEnhancedOpenSupported(); @@ -342,6 +343,7 @@ package android.net.wifi { method @Deprecated public boolean isScanAlwaysAvailable(); method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isScanThrottleEnabled(); method public boolean isStaApConcurrencySupported(); + method public boolean isStaBridgedApConcurrencySupported(); method public boolean isTdlsSupported(); method public boolean isWapiSupported(); method public boolean isWifiEnabled(); diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java index fddc8899a0c8..226d1a368223 100644 --- a/wifi/java/android/net/wifi/SoftApConfiguration.java +++ b/wifi/java/android/net/wifi/SoftApConfiguration.java @@ -935,6 +935,9 @@ public final class SoftApConfiguration implements Parcelable { * on the requested bands (if possible). * <p> * + * Use {@link WifiManager#isBridgedApConcurrencySupported()} to determine + * whether or not concurrent APs are supported. + * * @param bands Array of the {@link #BandType}. * @return Builder for chaining. * @throws IllegalArgumentException when more than 2 bands are set or an invalid band type @@ -1007,6 +1010,9 @@ public final class SoftApConfiguration implements Parcelable { * The {@link SoftApCapability#getSupportedChannelList(int)} can be used to obtain * valid channels in each band. * + * Use {@link WifiManager#isBridgedApConcurrencySupported()} to determine + * whether or not concurrent APs are supported. + * * <p> * If not set, the default for the channel is the special value 0 which has the framework * auto-select a valid channel from the band configured with {@link #setBands(int[])}. diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index e5aeead2dec0..2b931a380f43 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -2489,6 +2489,12 @@ public class WifiManager { /** @hide */ public static final long WIFI_FEATURE_SAE_PK = 0x10000000000L; // SAE-PK + /** @hide */ + public static final long WIFI_FEATURE_STA_BRIDGED_AP = 0x20000000000L; // STA + Bridged AP + + /** @hide */ + public static final long WIFI_FEATURE_BRIDGED_AP = 0x40000000000L; // Bridged AP + private long getSupportedFeatures() { try { return mService.getSupportedFeatures(); @@ -2689,6 +2695,40 @@ public class WifiManager { } /** + * Query whether the device supports Station (STA) + Bridged access point (AP) + * concurrency or not. + * + * The bridged AP support means that the device supports AP + AP concurrency with the 2 APs + * bridged together. + * + * See {@link SoftApConfiguration.Builder#setBands(int[])} + * or {@link SoftApConfiguration.Builder#setChannels(SparseIntArray)} to configure bridged AP + * when the bridged AP supported. + * + * @return true if this device supports STA + bridged AP concurrency, false otherwise. + */ + public boolean isStaBridgedApConcurrencySupported() { + return isFeatureSupported(WIFI_FEATURE_STA_BRIDGED_AP); + } + + /** + * Query whether the device supports Bridged Access point (AP) concurrency or not. + * + * The bridged AP support means that the device supports AP + AP concurrency with the 2 APs + * bridged together. + * + * See {@link SoftApConfiguration.Builder#setBands(int[])} + * or {@link SoftApConfiguration.Builder#setChannels(SparseIntArray)} to configure bridged AP + * when the bridged AP supported. + * + * @return true if this device supports bridged AP concurrency, false otherwise. + */ + public boolean isBridgedApConcurrencySupported() { + return isFeatureSupported(WIFI_FEATURE_BRIDGED_AP); + } + + + /** * Interface for Wi-Fi activity energy info listener. Should be implemented by applications and * set when calling {@link WifiManager#getWifiActivityEnergyInfoAsync}. * diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java index e606d533a532..b7450c538ff8 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java +++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java @@ -917,6 +917,9 @@ public final class WifiNetworkSuggestion implements Parcelable { mPasspointConfiguration.setCarrierId(mCarrierId); mPasspointConfiguration.setSubscriptionId(mSubscriptionId); mPasspointConfiguration.setMeteredOverride(wifiConfiguration.meteredOverride); + mPasspointConfiguration.setOemPrivate(mIsNetworkOemPrivate); + mPasspointConfiguration.setOemPaid(mIsNetworkOemPaid); + mPasspointConfiguration.setCarrierMerged(mIsCarrierMerged); wifiConfiguration.macRandomizationSetting = mIsEnhancedMacRandomizationEnabled ? WifiConfiguration.RANDOMIZATION_ENHANCED : WifiConfiguration.RANDOMIZATION_PERSISTENT; diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java index 357c5bcfa265..006fbaa028bd 100644 --- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java +++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java @@ -474,6 +474,27 @@ public final class PasspointConfiguration implements Parcelable { */ private boolean mIsEnhancedMacRandomizationEnabled = false; + + /** + * Indicate whether the network is oem paid or not. Networks are considered oem paid + * if the corresponding connection is only available to system apps. + * @hide + */ + private boolean mIsOemPaid; + + /** + * Indicate whether the network is oem private or not. Networks are considered oem private + * if the corresponding connection is only available to system apps. + * @hide + */ + private boolean mIsOemPrivate; + + /** + * Indicate whether or not the network is a carrier merged network. + * @hide + */ + private boolean mIsCarrierMerged; + /** * Indicates if the end user has expressed an explicit opinion about the * meteredness of this network, such as through the Settings app. @@ -589,6 +610,54 @@ public final class PasspointConfiguration implements Parcelable { } /** + * Set whether the network is oem paid or not. + * @hide + */ + public void setOemPaid(boolean isOemPaid) { + mIsOemPaid = isOemPaid; + } + + /** + * Get whether the network is oem paid or not. + * @hide + */ + public boolean isOemPaid() { + return mIsOemPaid; + } + + /** + * Set whether the network is oem private or not. + * @hide + */ + public void setOemPrivate(boolean isOemPrivate) { + mIsOemPrivate = isOemPrivate; + } + + /** + * Get whether the network is oem private or not. + * @hide + */ + public boolean isOemPrivate() { + return mIsOemPrivate; + } + + /** + * Set whether the network is carrier merged or not. + * @hide + */ + public void setCarrierMerged(boolean isCarrierMerged) { + mIsCarrierMerged = isCarrierMerged; + } + + /** + * Get whether the network is carrier merged or not. + * @hide + */ + public boolean isCarrierMerged() { + return mIsCarrierMerged; + } + + /** * Constructor for creating PasspointConfiguration with default values. */ public PasspointConfiguration() {} @@ -635,6 +704,9 @@ public final class PasspointConfiguration implements Parcelable { mIsMacRandomizationEnabled = source.mIsMacRandomizationEnabled; mIsEnhancedMacRandomizationEnabled = source.mIsEnhancedMacRandomizationEnabled; mMeteredOverride = source.mMeteredOverride; + mIsCarrierMerged = source.mIsCarrierMerged; + mIsOemPaid = source.mIsOemPaid; + mIsOemPrivate = source.mIsOemPrivate; } @Override @@ -669,6 +741,9 @@ public final class PasspointConfiguration implements Parcelable { dest.writeBoolean(mIsEnhancedMacRandomizationEnabled); dest.writeInt(mMeteredOverride); dest.writeInt(mSubscriptionId); + dest.writeBoolean(mIsCarrierMerged); + dest.writeBoolean(mIsOemPaid); + dest.writeBoolean(mIsOemPrivate); } @Override @@ -700,6 +775,9 @@ public final class PasspointConfiguration implements Parcelable { && mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes && mCarrierId == that.mCarrierId && mSubscriptionId == that.mSubscriptionId + && mIsOemPrivate == that.mIsOemPrivate + && mIsOemPaid == that.mIsOemPaid + && mIsCarrierMerged == that.mIsCarrierMerged && mIsAutojoinEnabled == that.mIsAutojoinEnabled && mIsMacRandomizationEnabled == that.mIsMacRandomizationEnabled && mIsEnhancedMacRandomizationEnabled == that.mIsEnhancedMacRandomizationEnabled @@ -715,7 +793,8 @@ public final class PasspointConfiguration implements Parcelable { mSubscriptionExpirationTimeMillis, mUsageLimitUsageTimePeriodInMinutes, mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes, mServiceFriendlyNames, mCarrierId, mIsAutojoinEnabled, mIsMacRandomizationEnabled, - mIsEnhancedMacRandomizationEnabled, mMeteredOverride, mSubscriptionId); + mIsEnhancedMacRandomizationEnabled, mMeteredOverride, mSubscriptionId, + mIsCarrierMerged, mIsOemPaid, mIsOemPrivate); } @Override @@ -774,6 +853,9 @@ public final class PasspointConfiguration implements Parcelable { builder.append("mIsMacRandomizationEnabled:" + mIsMacRandomizationEnabled); builder.append("mIsEnhancedMacRandomizationEnabled:" + mIsEnhancedMacRandomizationEnabled); builder.append("mMeteredOverride:" + mMeteredOverride); + builder.append("mIsCarrierMerged:" + mIsCarrierMerged); + builder.append("mIsOemPaid:" + mIsOemPaid); + builder.append("mIsOemPrivate" + mIsOemPrivate); return builder.toString(); } @@ -884,6 +966,10 @@ public final class PasspointConfiguration implements Parcelable { config.mIsEnhancedMacRandomizationEnabled = in.readBoolean(); config.mMeteredOverride = in.readInt(); config.mSubscriptionId = in.readInt(); + config.mIsCarrierMerged = in.readBoolean(); + config.mIsOemPaid = in.readBoolean(); + config.mIsOemPrivate = in.readBoolean(); + return config; } diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java index 643a78c38f91..5e829188f93f 100644 --- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java +++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java @@ -809,7 +809,7 @@ public class WifiNetworkSuggestionTest { /** * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception * when both {@link WifiNetworkSuggestion.Builder#setWpa3Passphrase(String)} and - * {@link WifiNetworkSuggestion.Builderi + * {@link WifiNetworkSuggestion.Builder * #setWpa3EnterpriseStandardModeConfig(WifiEnterpriseConfig)} * are invoked. */ @@ -1310,6 +1310,7 @@ public class WifiNetworkSuggestionTest { .build(); assertTrue(suggestion.isOemPaid()); assertFalse(suggestion.isUserAllowedToManuallyConnect); + assertTrue(suggestion.getPasspointConfig().isOemPaid()); } /** @@ -1345,6 +1346,7 @@ public class WifiNetworkSuggestionTest { .build(); assertTrue(suggestion.isOemPrivate()); assertFalse(suggestion.isUserAllowedToManuallyConnect); + assertTrue(suggestion.getPasspointConfig().isOemPrivate()); } /** @@ -1439,6 +1441,25 @@ public class WifiNetworkSuggestionTest { } /** + * Validate {@link WifiNetworkSuggestion.Builder#setCarrierMerged(boolean)} (boolean)} set the + * correct value to the passpoint network. + */ + @Test + public void testSetCarrierMergedNetworkOnPasspointNetwork() { + assumeTrue(SdkLevel.isAtLeastS()); + + PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig(); + WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() + .setPasspointConfig(passpointConfiguration) + .setSubscriptionId(1) + .setCarrierMerged(true) + .setIsMetered(true) + .build(); + assertTrue(suggestion.isCarrierMerged()); + assertTrue(suggestion.getPasspointConfig().isCarrierMerged()); + } + + /** * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception * when set both {@link WifiNetworkSuggestion.Builder#setCarrierMerged(boolean)} (boolean)} * to true on a network is not metered. |