diff options
212 files changed, 5542 insertions, 2359 deletions
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java index b5d903a3206e..7e5f187b88c9 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java @@ -19,6 +19,8 @@ package android.app.appsearch; import android.annotation.IntDef; import android.annotation.NonNull; +import com.android.internal.annotations.VisibleForTesting; + import com.google.android.icing.proto.PropertyConfigProto; import com.google.android.icing.proto.SchemaProto; import com.google.android.icing.proto.SchemaTypeConfigProto; @@ -70,7 +72,8 @@ public final class AppSearchSchema { * @hide */ @NonNull - SchemaProto getProto() { + @VisibleForTesting + public SchemaProto getProto() { return mProto; } @@ -288,7 +291,7 @@ public final class AppSearchSchema { @NonNull public PropertyConfig build() { if (mProtoBuilder.getDataType() == PropertyConfigProto.DataType.Code.UNKNOWN) { - throw new IllegalSchemaException("Missing dataType field"); + throw new IllegalSchemaException("Missing field: dataType"); } if (mProtoBuilder.getSchemaType().isEmpty() && mProtoBuilder.getDataType() @@ -299,7 +302,7 @@ public final class AppSearchSchema { } if (mProtoBuilder.getCardinality() == PropertyConfigProto.Cardinality.Code.UNKNOWN) { - throw new IllegalSchemaException("Missing cardinality field"); + throw new IllegalSchemaException("Missing field: cardinality"); } return new PropertyConfig(mProtoBuilder.build()); } diff --git a/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING b/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING index 08811f804fd1..ca5b8841ea49 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING +++ b/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING @@ -10,6 +10,14 @@ "include-filter": "com.android.server.appsearch" } ] + }, + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "android.app.appsearch" + } + ] } ] } diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java index 89c57f7f9900..1faa814ed83f 100644 --- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java +++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java @@ -827,13 +827,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { pulledData.add(e); } - private void pullSystemUpTime(int tagId, long elapsedNanos, long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeLong(SystemClock.uptimeMillis()); - pulledData.add(e); - } - private void pullProcessMemoryState( int tagId, long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) { @@ -2027,11 +2020,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { break; } - case StatsLog.SYSTEM_UPTIME: { - pullSystemUpTime(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - case StatsLog.SYSTEM_ELAPSED_REALTIME: { pullSystemElapsedRealtime(tagId, elapsedNanos, wallClockNanos, ret); break; diff --git a/api/current.txt b/api/current.txt index b5b9984d8409..49676b45a00e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -386,6 +386,7 @@ package android { field public static final int canRequestFingerprintGestures = 16844109; // 0x101054d field public static final int canRequestTouchExplorationMode = 16843735; // 0x10103d7 field public static final int canRetrieveWindowContent = 16843653; // 0x1010385 + field public static final int canTakeScreenshot = 16844304; // 0x1010610 field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230 field public static final int cantSaveState = 16844142; // 0x101056e field @Deprecated public static final int capitalize = 16843113; // 0x1010169 @@ -2864,6 +2865,7 @@ package android.accessibilityservice { method protected void onServiceConnected(); method public final boolean performGlobalAction(int); method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo); + method public boolean takeScreenshot(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.graphics.Bitmap>); field public static final int GESTURE_SWIPE_DOWN = 2; // 0x2 field public static final int GESTURE_SWIPE_DOWN_AND_LEFT = 15; // 0xf field public static final int GESTURE_SWIPE_DOWN_AND_RIGHT = 16; // 0x10 @@ -2960,6 +2962,7 @@ package android.accessibilityservice { field public static final int CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES = 64; // 0x40 field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2 field public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 1; // 0x1 + field public static final int CAPABILITY_CAN_TAKE_SCREENSHOT = 128; // 0x80 field @NonNull public static final android.os.Parcelable.Creator<android.accessibilityservice.AccessibilityServiceInfo> CREATOR; field public static final int DEFAULT = 1; // 0x1 field public static final int FEEDBACK_ALL_MASK = -1; // 0xffffffff @@ -11418,6 +11421,7 @@ package android.content.pm { public final class InstallSourceInfo implements android.os.Parcelable { method public int describeContents(); method @Nullable public String getInitiatingPackageName(); + method @Nullable public android.content.pm.SigningInfo getInitiatingPackageSigningInfo(); method @Nullable public String getInstallingPackageName(); method @Nullable public String getOriginatingPackageName(); method public void writeToParcel(@NonNull android.os.Parcel, int); @@ -11784,6 +11788,7 @@ package android.content.pm { method @NonNull public abstract CharSequence getApplicationLabel(@NonNull android.content.pm.ApplicationInfo); method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull android.content.pm.ApplicationInfo); method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; + method @NonNull public CharSequence getBackgroundPermissionButtonLabel(); method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int); method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName); method @NonNull public abstract android.graphics.drawable.Drawable getDefaultActivityIcon(); @@ -27141,9 +27146,11 @@ package android.media { public abstract class VolumeProvider { ctor public VolumeProvider(int, int, int); + ctor public VolumeProvider(int, int, int, @Nullable String); method public final int getCurrentVolume(); method public final int getMaxVolume(); method public final int getVolumeControl(); + method @Nullable public final String getVolumeControlId(); method public void onAdjustVolume(int); method public void onSetVolumeTo(int); method public final void setCurrentVolume(int); @@ -28003,6 +28010,7 @@ package android.media.session { method public int getMaxVolume(); method public int getPlaybackType(); method public int getVolumeControl(); + method @Nullable public String getVolumeControlId(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.media.session.MediaController.PlaybackInfo> CREATOR; field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1 @@ -39520,7 +39528,8 @@ package android.provider { field public static final String DISPLAY_NAME = "_display_name"; field public static final String DOCUMENT_ID = "document_id"; field public static final String DURATION = "duration"; - field public static final String GENERATION = "generation"; + field public static final String GENERATION_ADDED = "generation_added"; + field public static final String GENERATION_MODIFIED = "generation_modified"; field public static final String GENRE = "genre"; field public static final String HEIGHT = "height"; field public static final String INSTANCE_ID = "instance_id"; @@ -58900,7 +58909,7 @@ package android.widget { method @android.view.ViewDebug.ExportedProperty public CharSequence getFormat24Hour(); method public String getTimeZone(); method public boolean is24HourModeEnabled(); - method public void refresh(); + method public void refreshTime(); method public void setFormat12Hour(CharSequence); method public void setFormat24Hour(CharSequence); method public void setTimeZone(String); @@ -59215,6 +59224,7 @@ package android.widget { public class Toast { ctor public Toast(android.content.Context); + method public void addCallback(@NonNull android.widget.Toast.Callback); method public void cancel(); method public int getDuration(); method public int getGravity(); @@ -59225,6 +59235,7 @@ package android.widget { method public int getYOffset(); method public static android.widget.Toast makeText(android.content.Context, CharSequence, int); method public static android.widget.Toast makeText(android.content.Context, @StringRes int, int) throws android.content.res.Resources.NotFoundException; + method public void removeCallback(@NonNull android.widget.Toast.Callback); method public void setDuration(int); method public void setGravity(int, int, int); method public void setMargin(float, float); @@ -59236,6 +59247,12 @@ package android.widget { field public static final int LENGTH_SHORT = 0; // 0x0 } + public abstract static class Toast.Callback { + ctor public Toast.Callback(); + method public void onToastHidden(); + method public void onToastShown(); + } + public class ToggleButton extends android.widget.CompoundButton { ctor public ToggleButton(android.content.Context, android.util.AttributeSet, int, int); ctor public ToggleButton(android.content.Context, android.util.AttributeSet, int); diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt index 0a6706550074..c8253a0b9e88 100644 --- a/api/module-lib-current.txt +++ b/api/module-lib-current.txt @@ -79,3 +79,48 @@ package android.os { } +package android.timezone { + + public final class CountryTimeZones { + method @Nullable public android.icu.util.TimeZone getDefaultTimeZone(); + method @Nullable public String getDefaultTimeZoneId(); + method @NonNull public java.util.List<android.timezone.CountryTimeZones.TimeZoneMapping> getEffectiveTimeZoneMappingsAt(long); + method public boolean hasUtcZone(long); + method public boolean isDefaultTimeZoneBoosted(); + method public boolean isForCountryCode(@NonNull String); + method @Nullable public android.timezone.CountryTimeZones.OffsetResult lookupByOffsetWithBias(int, @Nullable Boolean, @Nullable Integer, long, @Nullable android.icu.util.TimeZone); + } + + public static final class CountryTimeZones.OffsetResult { + ctor public CountryTimeZones.OffsetResult(@NonNull android.icu.util.TimeZone, boolean); + method @NonNull public android.icu.util.TimeZone getTimeZone(); + method public boolean isOnlyMatch(); + } + + public static final class CountryTimeZones.TimeZoneMapping { + method @Nullable public android.icu.util.TimeZone getTimeZone(); + method @NonNull public String getTimeZoneId(); + } + + public class TelephonyLookup { + method @NonNull public static android.timezone.TelephonyLookup getInstance(); + method @Nullable public android.timezone.TelephonyNetworkFinder getTelephonyNetworkFinder(); + } + + public class TelephonyNetwork { + method @NonNull public String getCountryIsoCode(); + method @NonNull public String getMcc(); + method @NonNull public String getMnc(); + } + + public class TelephonyNetworkFinder { + method @Nullable public android.timezone.TelephonyNetwork findNetworkByMccMnc(@NonNull String, @NonNull String); + } + + public final class TimeZoneFinder { + method @NonNull public static android.timezone.TimeZoneFinder getInstance(); + method @Nullable public android.timezone.CountryTimeZones lookupCountryTimeZones(@NonNull String); + } + +} + diff --git a/api/system-current.txt b/api/system-current.txt index 07b6b4b76a68..48f8bc16b661 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -791,6 +791,7 @@ package android.app { package android.app.admin { public class DevicePolicyManager { + method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwnerNameOnAnyUser(); @@ -1037,6 +1038,7 @@ package android.app.backup { field public static final int AGENT_ERROR = -1003; // 0xfffffc15 field public static final int AGENT_UNKNOWN = -1004; // 0xfffffc14 field public static final String EXTRA_TRANSPORT_REGISTRATION = "android.app.backup.extra.TRANSPORT_REGISTRATION"; + field public static final int FLAG_DATA_NOT_CHANGED = 8; // 0x8 field public static final int FLAG_INCREMENTAL = 2; // 0x2 field public static final int FLAG_NON_INCREMENTAL = 4; // 0x4 field public static final int FLAG_USER_INITIATED = 1; // 0x1 @@ -1703,7 +1705,7 @@ package android.content { method public abstract void sendBroadcast(android.content.Intent, @Nullable String, @Nullable android.os.Bundle); method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.os.Bundle); method public abstract void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle); - method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle); + method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle); field public static final String APP_INTEGRITY_SERVICE = "app_integrity"; field public static final String APP_PREDICTION_SERVICE = "app_prediction"; field public static final String BACKUP_SERVICE = "backup"; @@ -4802,6 +4804,7 @@ package android.net { method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported(); method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public int registerNetworkProvider(@NonNull android.net.NetworkProvider); method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback); + method @Deprecated public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, int, int, @NonNull android.os.Handler); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean); method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi(); method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle); @@ -4819,6 +4822,7 @@ package android.net { field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb field public static final int TYPE_NONE = -1; // 0xffffffff + field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd } public abstract static class ConnectivityManager.OnStartTetheringCallback { @@ -4967,6 +4971,27 @@ package android.net { field public final int netId; } + public abstract class NetworkAgent { + method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData); + method public void onAutomaticReconnectDisabled(); + method public void onBandwidthUpdateRequested(); + method public void onNetworkUnwanted(); + method public void onRemoveKeepalivePacketFilter(int); + method public void onSaveAcceptUnvalidated(boolean); + method public void onSignalStrengthThresholdsUpdated(@NonNull int[]); + method public void onStartSocketKeepalive(int, int, @NonNull android.net.KeepalivePacketData); + method public void onStopSocketKeepalive(int); + method public void onValidationStatus(int, @Nullable String); + method public void sendLinkProperties(@NonNull android.net.LinkProperties); + method public void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities); + method public void sendNetworkScore(int); + method public void sendSocketKeepaliveEvent(int, int); + field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2 + field public static final int VALIDATION_STATUS_VALID = 1; // 0x1 + field @NonNull public final android.net.Network network; + field public final int providerId; + } + public final class NetworkAgentConfig implements android.os.Parcelable { method public int describeContents(); method @Nullable public String getSubscriberId(); @@ -5022,6 +5047,10 @@ package android.net { method public abstract void onRequestScores(android.net.NetworkKey[]); } + public class NetworkRequest implements android.os.Parcelable { + method public boolean satisfiedBy(@Nullable android.net.NetworkCapabilities); + } + public static class NetworkRequest.Builder { method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int); } @@ -5128,6 +5157,10 @@ package android.net { field public final android.net.RssiCurve rssiCurve; } + public abstract class SocketKeepalive implements java.lang.AutoCloseable { + field public static final int SUCCESS = 0; // 0x0 + } + public final class StaticIpConfiguration implements android.os.Parcelable { ctor public StaticIpConfiguration(); ctor public StaticIpConfiguration(@Nullable android.net.StaticIpConfiguration); @@ -11602,6 +11635,7 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public String getProvisioningStringValue(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getRcsProvisioningStatusForCapability(int); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, boolean); @@ -11866,6 +11900,7 @@ package android.telephony.ims.stub { method public String getConfigString(int); method public final void notifyProvisionedValueChanged(int, int); method public final void notifyProvisionedValueChanged(int, String); + method public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean); method public int setConfig(int, int); method public int setConfig(int, String); field public static final int CONFIG_RESULT_FAILED = 1; // 0x1 @@ -12243,6 +12278,12 @@ package android.webkit { method public void onJsResultComplete(android.webkit.JsResult); } + public interface PacProcessor { + method @NonNull public static android.webkit.PacProcessor getInstance(); + method @Nullable public String makeProxyRequest(@NonNull String); + method public boolean setProxyScript(@NonNull String); + } + public class SslErrorHandler extends android.os.Handler { ctor public SslErrorHandler(); } @@ -12377,6 +12418,7 @@ package android.webkit { method public android.webkit.WebViewProvider createWebView(android.webkit.WebView, android.webkit.WebView.PrivateAccess); method public android.webkit.CookieManager getCookieManager(); method public android.webkit.GeolocationPermissions getGeolocationPermissions(); + method @NonNull public default android.webkit.PacProcessor getPacProcessor(); method public android.webkit.ServiceWorkerController getServiceWorkerController(); method public android.webkit.WebViewFactoryProvider.Statics getStatics(); method @Deprecated public android.webkit.TokenBindingService getTokenBindingService(); diff --git a/api/test-current.txt b/api/test-current.txt index 1db4c9b82343..10028f20aad3 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -471,7 +471,10 @@ package android.app { } public class WallpaperManager { + method @Nullable public android.graphics.Bitmap getBitmap(); method @RequiresPermission("android.permission.SET_WALLPAPER_COMPONENT") public boolean setWallpaperComponent(android.content.ComponentName); + method public boolean shouldEnableWideColorGamut(); + method @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public boolean wallpaperSupportsWcg(int); } public class WindowConfiguration implements java.lang.Comparable<android.app.WindowConfiguration> android.os.Parcelable { @@ -3827,6 +3830,7 @@ package android.telephony.ims { method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public boolean getProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int); method @Nullable @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public String getProvisioningStringValue(int); method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public boolean getRcsProvisioningStatusForCapability(int); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean); method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, boolean); @@ -4050,6 +4054,7 @@ package android.telephony.ims.stub { method public String getConfigString(int); method public final void notifyProvisionedValueChanged(int, int); method public final void notifyProvisionedValueChanged(int, String); + method public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean); method public int setConfig(int, int); method public int setConfig(int, String); field public static final int CONFIG_RESULT_FAILED = 1; // 0x1 @@ -4379,6 +4384,7 @@ package android.view { } public final class Display { + method @NonNull public android.graphics.ColorSpace[] getSupportedWideColorGamut(); method public boolean hasAccess(int); } @@ -4496,6 +4502,8 @@ package android.view.accessibility { } public class AccessibilityNodeInfo implements android.os.Parcelable { + method public void addChild(@NonNull android.os.IBinder); + method public void setLeashedParent(@Nullable android.os.IBinder, int); method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger); method public void writeToParcelNoRecycle(android.os.Parcel, int); } diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 1441d48771d6..cbece78ec355 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -338,6 +338,7 @@ message Atom { BootTimeEventElapsedTime boot_time_event_elapsed_time_reported = 240; BootTimeEventUtcTime boot_time_event_utc_time_reported = 241; BootTimeEventErrorCode boot_time_event_error_code_reported = 242; + UserspaceRebootReported userspace_reboot_reported = 243; } // Pulled events will start at field 10000. @@ -7904,4 +7905,42 @@ message RuntimeAppOpsAccess { optional SamplingStrategy sampling_strategy = 6; } - +/* + * Logs userspace reboot outcome and duration. + * + * Logged from: + * frameworks/base/core/java/com/android/server/BootReceiver.java + */ +message UserspaceRebootReported { + // Possible outcomes of userspace reboot. + enum Outcome { + // Default value in case platform failed to determine the outcome. + OUTCOME_UNKNOWN = 0; + // Userspace reboot succeeded (i.e. boot completed without a fall back to hard reboot). + SUCCESS = 1; + // Userspace reboot shutdown sequence was aborted. + FAILED_SHUTDOWN_SEQUENCE_ABORTED = 2; + // Remounting userdata into checkpointing mode failed. + FAILED_USERDATA_REMOUNT = 3; + // Device didn't finish booting before timeout and userspace reboot watchdog issued a hard + // reboot. + FAILED_USERSPACE_REBOOT_WATCHDOG_TRIGGERED = 4; + } + // Outcome of userspace reboot. Always set. + optional Outcome outcome = 1; + // Duration of userspace reboot in case it has a successful outcome. + // Duration is measured as time between userspace reboot was initiated and until boot completed + // (e.g. sys.boot_completed=1). + optional int64 duration_millis = 2; + // State of primary user's (user0) credential encryption storage. + enum UserEncryptionState { + // Default value. + USER_ENCRYPTION_STATE_UNKNOWN = 0; + // Credential encrypted storage is unlocked. + UNLOCKED = 1; + // Credential encrypted storage is locked. + LOCKED = 2; + } + // State of primary user's encryption storage at the moment boot completed. Always set. + optional UserEncryptionState user_encryption_state = 3; +} diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 549ca67f7c53..4baa08c69ade 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -83,10 +83,6 @@ std::map<PullerKey, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { .pullTimeoutNs = NS_PER_SEC / 2, }}, - // system_uptime - {{.atomTag = android::util::SYSTEM_UPTIME}, - {.puller = new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}}, - // remaining_battery_capacity {{.atomTag = android::util::REMAINING_BATTERY_CAPACITY}, {.puller = new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}}, diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index 7b651dfed7fc..e0aecceac4e3 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -617,19 +617,6 @@ int64_t StringToId(const string& str) { void ValidateAttributionUidDimension(const DimensionsValue& value, int atomId, int uid) { EXPECT_EQ(value.field(), atomId); - // Attribution field. - EXPECT_EQ(value.value_tuple().dimensions_value(0).field(), 1); - // Uid only. - EXPECT_EQ(value.value_tuple().dimensions_value(0) - .value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(value.value_tuple().dimensions_value(0) - .value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(value.value_tuple().dimensions_value(0) - .value_tuple().dimensions_value(0).value_int(), uid); -} - -void ValidateUidDimension(const DimensionsValue& value, int atomId, int uid) { - EXPECT_EQ(value.field(), atomId); EXPECT_EQ(value.value_tuple().dimensions_value_size(), 1); // Attribution field. EXPECT_EQ(value.value_tuple().dimensions_value(0).field(), 1); diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index e46840c0f467..0bd8ce692884 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -17,6 +17,7 @@ package android.accessibilityservice; import android.accessibilityservice.GestureDescription.MotionEventGenerator; +import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -26,12 +27,15 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; import android.content.pm.ParceledListSlice; +import android.graphics.Bitmap; import android.graphics.Region; +import android.os.Binder; import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.RemoteCallback; import android.os.RemoteException; import android.util.ArrayMap; import android.util.Log; @@ -48,10 +52,13 @@ import android.view.accessibility.AccessibilityWindowInfo; import com.android.internal.os.HandlerCaller; import com.android.internal.os.SomeArgs; +import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; +import java.util.concurrent.Executor; +import java.util.function.Consumer; /** * Accessibility services should only be used to assist users with disabilities in using @@ -483,6 +490,9 @@ public abstract class AccessibilityService extends Service { private FingerprintGestureController mFingerprintGestureController; + /** @hide */ + public static final String KEY_ACCESSIBILITY_SCREENSHOT = "screenshot"; + /** * Callback for {@link android.view.accessibility.AccessibilityEvent}s. * @@ -1761,6 +1771,51 @@ public abstract class AccessibilityService extends Service { } /** + * Takes a screenshot of the specified display and returns it by {@link Bitmap.Config#HARDWARE} + * format. + * <p> + * <strong>Note:</strong> In order to take screenshot your service has + * to declare the capability to take screenshot by setting the + * {@link android.R.styleable#AccessibilityService_canTakeScreenshot} + * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. + * Besides, This API is only supported for default display now + * {@link Display#DEFAULT_DISPLAY}. + * </p> + * + * @param displayId The logic display id, must be {@link Display#DEFAULT_DISPLAY} for + * default display. + * @param executor Executor on which to run the callback. + * @param callback The callback invoked when the taking screenshot is done. + * + * @return {@code true} if the taking screenshot accepted, {@code false} if not. + */ + public boolean takeScreenshot(int displayId, @NonNull @CallbackExecutor Executor executor, + @NonNull Consumer<Bitmap> callback) { + Preconditions.checkNotNull(executor, "executor cannot be null"); + Preconditions.checkNotNull(callback, "callback cannot be null"); + final IAccessibilityServiceConnection connection = + AccessibilityInteractionClient.getInstance().getConnection( + mConnectionId); + if (connection == null) { + return false; + } + try { + connection.takeScreenshotWithCallback(displayId, new RemoteCallback((result) -> { + final Bitmap screenshot = result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT); + final long identity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> callback.accept(screenshot)); + } finally { + Binder.restoreCallingIdentity(identity); + } + })); + } catch (RemoteException re) { + throw new RuntimeException(re); + } + return true; + } + + /** * Implement to return the implementation of the internal accessibility * service interface. */ diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index 5e2c1faac156..12f2c3b17c96 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -86,6 +86,7 @@ import java.util.List; * @attr ref android.R.styleable#AccessibilityService_settingsActivity * @attr ref android.R.styleable#AccessibilityService_nonInteractiveUiTimeout * @attr ref android.R.styleable#AccessibilityService_interactiveUiTimeout + * @attr ref android.R.styleable#AccessibilityService_canTakeScreenshot * @see AccessibilityService * @see android.view.accessibility.AccessibilityEvent * @see android.view.accessibility.AccessibilityManager @@ -136,6 +137,12 @@ public class AccessibilityServiceInfo implements Parcelable { */ public static final int CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES = 0x00000040; + /** + * Capability: This accessibility service can take screenshot. + * @see android.R.styleable#AccessibilityService_canTakeScreenshot + */ + public static final int CAPABILITY_CAN_TAKE_SCREENSHOT = 0x00000080; + private static SparseArray<CapabilityInfo> sAvailableCapabilityInfos; /** @@ -625,6 +632,10 @@ public class AccessibilityServiceInfo implements Parcelable { .AccessibilityService_canRequestFingerprintGestures, false)) { mCapabilities |= CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES; } + if (asAttributes.getBoolean(com.android.internal.R.styleable + .AccessibilityService_canTakeScreenshot, false)) { + mCapabilities |= CAPABILITY_CAN_TAKE_SCREENSHOT; + } TypedValue peekedValue = asAttributes.peekValue( com.android.internal.R.styleable.AccessibilityService_description); if (peekedValue != null) { @@ -794,6 +805,7 @@ public class AccessibilityServiceInfo implements Parcelable { * @see #CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS * @see #CAPABILITY_CAN_CONTROL_MAGNIFICATION * @see #CAPABILITY_CAN_PERFORM_GESTURES + * @see #CAPABILITY_CAN_TAKE_SCREENSHOT */ public int getCapabilities() { return mCapabilities; @@ -810,6 +822,7 @@ public class AccessibilityServiceInfo implements Parcelable { * @see #CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS * @see #CAPABILITY_CAN_CONTROL_MAGNIFICATION * @see #CAPABILITY_CAN_PERFORM_GESTURES + * @see #CAPABILITY_CAN_TAKE_SCREENSHOT * * @hide */ @@ -1253,6 +1266,8 @@ public class AccessibilityServiceInfo implements Parcelable { return "CAPABILITY_CAN_PERFORM_GESTURES"; case CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES: return "CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES"; + case CAPABILITY_CAN_TAKE_SCREENSHOT: + return "CAPABILITY_CAN_TAKE_SCREENSHOT"; default: return "UNKNOWN"; } @@ -1314,6 +1329,10 @@ public class AccessibilityServiceInfo implements Parcelable { new CapabilityInfo(CAPABILITY_CAN_PERFORM_GESTURES, R.string.capability_title_canPerformGestures, R.string.capability_desc_canPerformGestures)); + sAvailableCapabilityInfos.put(CAPABILITY_CAN_TAKE_SCREENSHOT, + new CapabilityInfo(CAPABILITY_CAN_TAKE_SCREENSHOT, + R.string.capability_title_canTakeScreenshot, + R.string.capability_desc_canTakeScreenshot)); if ((context == null) || fingerprintAvailable(context)) { sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES, new CapabilityInfo(CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES, diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index 656f87fe435b..4ea5c62bf05b 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -18,8 +18,10 @@ package android.accessibilityservice; import android.accessibilityservice.AccessibilityServiceInfo; import android.content.pm.ParceledListSlice; +import android.graphics.Bitmap; import android.graphics.Region; import android.os.Bundle; +import android.os.RemoteCallback; import android.view.MagnificationSpec; import android.view.MotionEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -102,4 +104,10 @@ interface IAccessibilityServiceConnection { boolean isFingerprintGestureDetectionAvailable(); IBinder getOverlayWindowToken(int displayid); + + int getWindowIdForLeashToken(IBinder token); + + Bitmap takeScreenshot(int displayId); + + void takeScreenshotWithCallback(int displayId, in RemoteCallback callback); } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 1c6a5615dbd5..d39a8c4699e8 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -133,6 +133,10 @@ public class ApplicationPackageManager extends PackageManager { // Default flags to use with PackageManager when no flags are given. private final static int sDefaultFlags = PackageManager.GET_SHARED_LIBRARY_FILES; + // Name of the resource which provides background permission button string + public static final String APP_PERMISSION_BUTTON_ALLOW_ALWAYS = + "app_permission_button_allow_always"; + private final Object mLock = new Object(); @GuardedBy("mLock") @@ -807,6 +811,26 @@ public class ApplicationPackageManager extends PackageManager { } @Override + public CharSequence getBackgroundPermissionButtonLabel() { + try { + + String permissionController = getPermissionControllerPackageName(); + Context context = + mContext.createPackageContext(permissionController, 0); + + int textId = context.getResources().getIdentifier(APP_PERMISSION_BUTTON_ALLOW_ALWAYS, + "string", "com.android.permissioncontroller"); +// permissionController); STOPSHIP b/147434671 + if (textId != 0) { + return context.getText(textId); + } + } catch (NameNotFoundException e) { + Log.e(TAG, "Permission controller not found.", e); + } + return ""; + } + + @Override public int checkSignatures(String pkg1, String pkg2) { try { return mPM.checkSignatures(pkg1, pkg2); diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index bd6baec30be0..580382e0a4aa 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -355,6 +355,12 @@ interface IActivityManager { in Bundle options, int userId); @UnsupportedAppUsage int stopUser(int userid, boolean force, in IStopUserCallback callback); + /** + * Check {@link com.android.server.am.ActivityManagerService#stopUserWithDelayedLocking(int, boolean, IStopUserCallback)} + * for details. + */ + int stopUserWithDelayedLocking(int userid, boolean force, in IStopUserCallback callback); + @UnsupportedAppUsage void registerUserSwitchObserver(in IUserSwitchObserver observer, in String name); void unregisterUserSwitchObserver(in IUserSwitchObserver observer); diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl index 8c3180b400ef..80ba464851e0 100644 --- a/core/java/android/app/IUiAutomationConnection.aidl +++ b/core/java/android/app/IUiAutomationConnection.aidl @@ -39,7 +39,6 @@ interface IUiAutomationConnection { boolean injectInputEvent(in InputEvent event, boolean sync); void syncInputTransactions(); boolean setRotation(int rotation); - Bitmap takeScreenshot(in Rect crop, int rotation); boolean clearWindowContentFrameStats(int windowId); WindowContentFrameStats getWindowContentFrameStats(int windowId); void clearWindowAnimationFrameStats(); diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java index 18a3e6e89552..2579bd1abbfd 100644 --- a/core/java/android/app/UiAutomation.java +++ b/core/java/android/app/UiAutomation.java @@ -27,10 +27,7 @@ import android.annotation.Nullable; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.Bitmap; -import android.graphics.Point; -import android.graphics.Rect; import android.graphics.Region; -import android.hardware.display.DisplayManagerGlobal; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; @@ -831,39 +828,20 @@ public final class UiAutomation { } /** - * Takes a screenshot. + * Takes a screenshot of the default display and returns it by {@link Bitmap.Config#HARDWARE} + * format. * * @return The screenshot bitmap on success, null otherwise. */ public Bitmap takeScreenshot() { + final int connectionId; synchronized (mLock) { throwIfNotConnectedLocked(); + connectionId = mConnectionId; } - Display display = DisplayManagerGlobal.getInstance() - .getRealDisplay(Display.DEFAULT_DISPLAY); - Point displaySize = new Point(); - display.getRealSize(displaySize); - - int rotation = display.getRotation(); - - // Take the screenshot - Bitmap screenShot = null; - try { - // Calling out without a lock held. - screenShot = mUiAutomationConnection.takeScreenshot( - new Rect(0, 0, displaySize.x, displaySize.y), rotation); - if (screenShot == null) { - return null; - } - } catch (RemoteException re) { - Log.e(LOG_TAG, "Error while taking screnshot!", re); - return null; - } - - // Optimization - screenShot.setHasAlpha(false); - - return screenShot; + // Calling out without a lock held. + return AccessibilityInteractionClient.getInstance() + .takeScreenshot(connectionId, Display.DEFAULT_DISPLAY); } /** diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java index 82e988109db8..4fb974305e48 100644 --- a/core/java/android/app/UiAutomationConnection.java +++ b/core/java/android/app/UiAutomationConnection.java @@ -21,8 +21,6 @@ import android.accessibilityservice.IAccessibilityServiceClient; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Rect; import android.hardware.input.InputManager; import android.os.Binder; import android.os.IBinder; @@ -180,23 +178,6 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { } @Override - public Bitmap takeScreenshot(Rect crop, int rotation) { - synchronized (mLock) { - throwIfCalledByNotTrustedUidLocked(); - throwIfShutdownLocked(); - throwIfNotConnectedLocked(); - } - final long identity = Binder.clearCallingIdentity(); - try { - int width = crop.width(); - int height = crop.height(); - return SurfaceControl.screenshot(crop, width, height, rotation); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - @Override public boolean clearWindowContentFrameStats(int windowId) throws RemoteException { synchronized (mLock) { throwIfCalledByNotTrustedUidLocked(); @@ -449,7 +430,8 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY - | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS); + | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS + | AccessibilityServiceInfo.CAPABILITY_CAN_TAKE_SCREENSHOT); try { // Calling out with a lock held is fine since if the system // process is gone the client calling in will be killed. diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 2507991362a5..8eee73c882e2 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -568,8 +568,10 @@ public class WallpaperManager { * * @see Configuration#isScreenWideColorGamut() * @return True if wcg should be enabled for this device. + * @hide */ - private boolean shouldEnableWideColorGamut() { + @TestApi + public boolean shouldEnableWideColorGamut() { return mWcgEnabled; } @@ -877,6 +879,7 @@ public class WallpaperManager { * @see #FLAG_SYSTEM * @hide */ + @TestApi @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public boolean wallpaperSupportsWcg(int which) { if (!shouldEnableWideColorGamut()) { @@ -893,6 +896,8 @@ public class WallpaperManager { * * @hide */ + @TestApi + @Nullable @UnsupportedAppUsage public Bitmap getBitmap() { return getBitmap(false); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index caaa686ecf08..c584575f4839 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -7418,7 +7418,9 @@ public class DevicePolicyManager { * @param userHandle The user for whom to check the caller-id permission * @hide */ - public boolean getBluetoothContactSharingDisabled(UserHandle userHandle) { + @SystemApi + @RequiresPermission(permission.INTERACT_ACROSS_USERS) + public boolean getBluetoothContactSharingDisabled(@NonNull UserHandle userHandle) { if (mService != null) { try { return mService.getBluetoothContactSharingDisabledForUser(userHandle diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java index c8f2ff34a70c..567eb4a68a1d 100644 --- a/core/java/android/app/backup/BackupTransport.java +++ b/core/java/android/app/backup/BackupTransport.java @@ -85,6 +85,15 @@ public class BackupTransport { public static final int FLAG_NON_INCREMENTAL = 1 << 2; /** + * For key value backup, indicates that the backup contains no new data since the last backup + * attempt completed without any errors. The transport should use this to record that + * a successful backup attempt has been completed but no backup data has been changed. + * + * @see #performBackup(PackageInfo, ParcelFileDescriptor, int) + */ + public static final int FLAG_DATA_NOT_CHANGED = 1 << 3; + + /** * Used as a boolean extra in the binding intent of transports. We pass {@code true} to * notify transports that the current connection is used for registering the transport. */ @@ -302,7 +311,8 @@ public class BackupTransport { * BackupService.doBackup() method. This may be a pipe rather than a file on * persistent media, so it may not be seekable. * @param flags a combination of {@link BackupTransport#FLAG_USER_INITIATED}, {@link - * BackupTransport#FLAG_NON_INCREMENTAL}, {@link BackupTransport#FLAG_INCREMENTAL}, or 0. + * BackupTransport#FLAG_NON_INCREMENTAL}, {@link BackupTransport#FLAG_INCREMENTAL}, + * {@link BackupTransport#FLAG_DATA_NOT_CHANGED},or 0. * @return one of {@link BackupTransport#TRANSPORT_OK} (OK so far), * {@link BackupTransport#TRANSPORT_PACKAGE_REJECTED} (to suppress backup of this * specific package, but allow others to proceed), diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 9ef95743b0c9..3860508a7794 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1825,7 +1825,7 @@ public abstract class Context { * @throws ActivityNotFoundException * @hide */ - @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) @SystemApi public void startActivityAsUser(@RequiresPermission @NonNull Intent intent, @NonNull UserHandle user) { @@ -1873,7 +1873,7 @@ public abstract class Context { * @throws ActivityNotFoundException * @hide */ - @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) @UnsupportedAppUsage public void startActivityAsUser(@RequiresPermission Intent intent, @Nullable Bundle options, UserHandle userId) { @@ -1979,7 +1979,7 @@ public abstract class Context { * @see #startActivities(Intent[]) * @see PackageManager#resolveActivity */ - @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public int startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) { throw new RuntimeException("Not implemented. Must override in a subclass."); } diff --git a/core/java/android/content/pm/InstallSourceInfo.java b/core/java/android/content/pm/InstallSourceInfo.java index 4d235f1af2f7..c0fdcc900577 100644 --- a/core/java/android/content/pm/InstallSourceInfo.java +++ b/core/java/android/content/pm/InstallSourceInfo.java @@ -29,32 +29,39 @@ public final class InstallSourceInfo implements Parcelable { @Nullable private final String mInitiatingPackageName; + @Nullable private final SigningInfo mInitiatingPackageSigningInfo; + @Nullable private final String mOriginatingPackageName; @Nullable private final String mInstallingPackageName; /** @hide */ public InstallSourceInfo(@Nullable String initiatingPackageName, + @Nullable SigningInfo initiatingPackageSigningInfo, @Nullable String originatingPackageName, @Nullable String installingPackageName) { - this.mInitiatingPackageName = initiatingPackageName; - this.mOriginatingPackageName = originatingPackageName; - this.mInstallingPackageName = installingPackageName; + mInitiatingPackageName = initiatingPackageName; + mInitiatingPackageSigningInfo = initiatingPackageSigningInfo; + mOriginatingPackageName = originatingPackageName; + mInstallingPackageName = installingPackageName; } @Override public int describeContents() { - return 0; + return mInitiatingPackageSigningInfo == null + ? 0 : mInitiatingPackageSigningInfo.describeContents(); } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString(mInitiatingPackageName); + dest.writeParcelable(mInitiatingPackageSigningInfo, flags); dest.writeString(mOriginatingPackageName); dest.writeString(mInstallingPackageName); } private InstallSourceInfo(Parcel source) { mInitiatingPackageName = source.readString(); + mInitiatingPackageSigningInfo = source.readParcelable(SigningInfo.class.getClassLoader()); mOriginatingPackageName = source.readString(); mInstallingPackageName = source.readString(); } @@ -66,6 +73,14 @@ public final class InstallSourceInfo implements Parcelable { } /** + * Information about the signing certificates used to sign the initiating package, if available. + */ + @Nullable + public SigningInfo getInitiatingPackageSigningInfo() { + return mInitiatingPackageSigningInfo; + } + + /** * The name of the package on behalf of which the initiating package requested the installation, * or null if not available. * <p> diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 4bfc40e698b9..5ca50c22e8e2 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4389,6 +4389,18 @@ public abstract class PackageManager { public abstract boolean shouldShowRequestPermissionRationale(@NonNull String permName); /** + * Gets the string that is displayed on the button which corresponds to granting background + * location in settings. The intended use for this is to help apps instruct users how to + * grant a background permission by providing the string that users will see. + * + * @return the string shown on the button for granting background location + */ + @NonNull + public CharSequence getBackgroundPermissionButtonLabel() { + return ""; + } + + /** * Returns an {@link android.content.Intent} suitable for passing to * {@link android.app.Activity#startActivityForResult(android.content.Intent, int)} * which prompts the user to grant permissions to this application. @@ -6053,6 +6065,11 @@ public abstract class PackageManager { * If the calling application does not hold the INSTALL_PACKAGES permission then * the result will always return {@code null} from * {@link InstallSourceInfo#getOriginatingPackageName()}. + * <p> + * If the package that requested the install has been uninstalled, then information about it + * will only be returned from {@link InstallSourceInfo#getInitiatingPackageName()} and + * {@link InstallSourceInfo#getInitiatingPackageSigningInfo()} if the calling package is + * requesting its own install information and is not an instant app. * * @param packageName The name of the package to query * @throws NameNotFoundException if the given package name is not installed diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java index a5e0f04eb034..456ebf23afd5 100644 --- a/core/java/android/hardware/soundtrigger/SoundTrigger.java +++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java @@ -557,9 +557,9 @@ public class SoundTrigger { dest.writeInt(vendorUuid.toString().length()); dest.writeString(vendorUuid.toString()); } + dest.writeInt(version); dest.writeBlob(data); dest.writeTypedArray(keyphrases, flags); - dest.writeInt(version); } @Override diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java index 72914192d73c..03d29a3855e1 100644 --- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java +++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java @@ -370,6 +370,12 @@ public class SoundTriggerModule { } @Override + public synchronized void onModuleDied() { + Message m = mHandler.obtainMessage(EVENT_SERVICE_DIED); + mHandler.sendMessage(m); + } + + @Override public synchronized void binderDied() { Message m = mHandler.obtainMessage(EVENT_SERVICE_DIED); mHandler.sendMessage(m); diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index c523f6514c54..8ba3131a83f1 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -662,7 +662,7 @@ public class ConnectivityManager { * {@hide} */ @Deprecated - @UnsupportedAppUsage + @SystemApi public static final int TYPE_WIFI_P2P = 13; /** @@ -3622,14 +3622,26 @@ public class ConnectivityManager { /** * Helper function to request a network with a particular legacy type. * - * This is temporarily public @hide so it can be called by system code that uses the - * NetworkRequest API to request networks but relies on CONNECTIVITY_ACTION broadcasts for - * instead network notifications. + * @deprecated This is temporarily public for tethering to backwards compatibility that uses + * the NetworkRequest API to request networks with legacy type and relies on + * CONNECTIVITY_ACTION broadcasts instead of NetworkCallbacks. New caller should use + * {@link #requestNetwork(NetworkRequest, NetworkCallback, Handler)} instead. * * TODO: update said system code to rely on NetworkCallbacks and make this method private. + + * @param request {@link NetworkRequest} describing this request. + * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note + * the callback must not be shared - it uniquely specifies this request. + * @param timeoutMs The time in milliseconds to attempt looking for a suitable network + * before {@link NetworkCallback#onUnavailable()} is called. The timeout must + * be a positive value (i.e. >0). + * @param legacyType to specify the network type(#TYPE_*). + * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. * * @hide */ + @SystemApi + @Deprecated public void requestNetwork(@NonNull NetworkRequest request, @NonNull NetworkCallback networkCallback, int timeoutMs, int legacyType, @NonNull Handler handler) { diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index 385cb1d68b57..72a6b397a30c 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -57,9 +57,6 @@ interface INetworkPolicyManager { @UnsupportedAppUsage boolean getRestrictBackground(); - /** Callback used to change internal state on tethering */ - void onTetheringChanged(String iface, boolean tethering); - /** Gets the restrict background status based on the caller's UID: 1 - disabled 2 - whitelisted diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 2792c564988a..be8e561fd044 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -21,6 +21,8 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; +import android.net.util.LinkPropertiesUtils; +import android.net.util.LinkPropertiesUtils.CompareResult; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -84,36 +86,6 @@ public final class LinkProperties implements Parcelable { /** * @hide */ - public static class CompareResult<T> { - public final List<T> removed = new ArrayList<>(); - public final List<T> added = new ArrayList<>(); - - public CompareResult() {} - - public CompareResult(Collection<T> oldItems, Collection<T> newItems) { - if (oldItems != null) { - removed.addAll(oldItems); - } - if (newItems != null) { - for (T newItem : newItems) { - if (!removed.remove(newItem)) { - added.add(newItem); - } - } - } - } - - @Override - public String toString() { - return "removed=[" + TextUtils.join(",", removed) - + "] added=[" + TextUtils.join(",", added) - + "]"; - } - } - - /** - * @hide - */ @UnsupportedAppUsage(implicitMember = "values()[Landroid/net/LinkProperties$ProvisioningChange;") public enum ProvisioningChange { @@ -1295,7 +1267,7 @@ public final class LinkProperties implements Parcelable { */ @UnsupportedAppUsage public boolean isIdenticalInterfaceName(@NonNull LinkProperties target) { - return TextUtils.equals(getInterfaceName(), target.getInterfaceName()); + return LinkPropertiesUtils.isIdenticalInterfaceName(target, this); } /** @@ -1318,10 +1290,7 @@ public final class LinkProperties implements Parcelable { */ @UnsupportedAppUsage public boolean isIdenticalAddresses(@NonNull LinkProperties target) { - Collection<InetAddress> targetAddresses = target.getAddresses(); - Collection<InetAddress> sourceAddresses = getAddresses(); - return (sourceAddresses.size() == targetAddresses.size()) ? - sourceAddresses.containsAll(targetAddresses) : false; + return LinkPropertiesUtils.isIdenticalAddresses(target, this); } /** @@ -1333,15 +1302,7 @@ public final class LinkProperties implements Parcelable { */ @UnsupportedAppUsage public boolean isIdenticalDnses(@NonNull LinkProperties target) { - Collection<InetAddress> targetDnses = target.getDnsServers(); - String targetDomains = target.getDomains(); - if (mDomains == null) { - if (targetDomains != null) return false; - } else { - if (!mDomains.equals(targetDomains)) return false; - } - return (mDnses.size() == targetDnses.size()) ? - mDnses.containsAll(targetDnses) : false; + return LinkPropertiesUtils.isIdenticalDnses(target, this); } /** @@ -1394,9 +1355,7 @@ public final class LinkProperties implements Parcelable { */ @UnsupportedAppUsage public boolean isIdenticalRoutes(@NonNull LinkProperties target) { - Collection<RouteInfo> targetRoutes = target.getRoutes(); - return (mRoutes.size() == targetRoutes.size()) ? - mRoutes.containsAll(targetRoutes) : false; + return LinkPropertiesUtils.isIdenticalRoutes(target, this); } /** @@ -1408,8 +1367,7 @@ public final class LinkProperties implements Parcelable { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public boolean isIdenticalHttpProxy(@NonNull LinkProperties target) { - return getHttpProxy() == null ? target.getHttpProxy() == null : - getHttpProxy().equals(target.getHttpProxy()); + return LinkPropertiesUtils.isIdenticalHttpProxy(target, this); } /** @@ -1541,26 +1499,6 @@ public final class LinkProperties implements Parcelable { } /** - * Compares the addresses in this LinkProperties with another - * LinkProperties, examining only addresses on the base link. - * - * @param target a LinkProperties with the new list of addresses - * @return the differences between the addresses. - * @hide - */ - public @NonNull CompareResult<LinkAddress> compareAddresses(@Nullable LinkProperties target) { - /* - * Duplicate the LinkAddresses into removed, we will be removing - * address which are common between mLinkAddresses and target - * leaving the addresses that are different. And address which - * are in target but not in mLinkAddresses are placed in the - * addedAddresses. - */ - return new CompareResult<>(mLinkAddresses, - target != null ? target.getLinkAddresses() : null); - } - - /** * Compares the DNS addresses in this LinkProperties with another * LinkProperties, examining only DNS addresses on the base link. * diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java index 74c9aac05b41..0e10c42e61db 100644 --- a/core/java/android/net/MacAddress.java +++ b/core/java/android/net/MacAddress.java @@ -20,11 +20,11 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; +import android.net.util.MacAddressUtils; import android.net.wifi.WifiInfo; import android.os.Parcel; import android.os.Parcelable; -import com.android.internal.util.BitUtils; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; @@ -33,7 +33,6 @@ import java.net.Inet6Address; import java.net.UnknownHostException; import java.security.SecureRandom; import java.util.Arrays; -import java.util.Random; /** * Representation of a MAC address. @@ -109,21 +108,13 @@ public final class MacAddress implements Parcelable { if (equals(BROADCAST_ADDRESS)) { return TYPE_BROADCAST; } - if (isMulticastAddress()) { + if ((mAddr & MULTICAST_MASK) != 0) { return TYPE_MULTICAST; } return TYPE_UNICAST; } /** - * @return true if this MacAddress is a multicast address. - * @hide - */ - public boolean isMulticastAddress() { - return (mAddr & MULTICAST_MASK) != 0; - } - - /** * @return true if this MacAddress is a locally assigned address. */ public boolean isLocallyAssigned() { @@ -192,7 +183,7 @@ public final class MacAddress implements Parcelable { * @hide */ public static boolean isMacAddress(byte[] addr) { - return addr != null && addr.length == ETHER_ADDR_LEN; + return MacAddressUtils.isMacAddress(addr); } /** @@ -261,26 +252,11 @@ public final class MacAddress implements Parcelable { } private static byte[] byteAddrFromLongAddr(long addr) { - byte[] bytes = new byte[ETHER_ADDR_LEN]; - int index = ETHER_ADDR_LEN; - while (index-- > 0) { - bytes[index] = (byte) addr; - addr = addr >> 8; - } - return bytes; + return MacAddressUtils.byteAddrFromLongAddr(addr); } private static long longAddrFromByteAddr(byte[] addr) { - Preconditions.checkNotNull(addr); - if (!isMacAddress(addr)) { - throw new IllegalArgumentException( - Arrays.toString(addr) + " was not a valid MAC address"); - } - long longAddr = 0; - for (byte b : addr) { - longAddr = (longAddr << 8) + BitUtils.uint8(b); - } - return longAddr; + return MacAddressUtils.longAddrFromByteAddr(addr); } // Internal conversion function equivalent to longAddrFromByteAddr(byteAddrFromStringAddr(addr)) @@ -350,50 +326,7 @@ public final class MacAddress implements Parcelable { * @hide */ public static @NonNull MacAddress createRandomUnicastAddressWithGoogleBase() { - return createRandomUnicastAddress(BASE_GOOGLE_MAC, new SecureRandom()); - } - - /** - * Returns a generated MAC address whose 46 bits, excluding the locally assigned bit and the - * unicast bit, are randomly selected. - * - * The locally assigned bit is always set to 1. The multicast bit is always set to 0. - * - * @return a random locally assigned, unicast MacAddress. - * - * @hide - */ - public static @NonNull MacAddress createRandomUnicastAddress() { - return createRandomUnicastAddress(null, new SecureRandom()); - } - - /** - * Returns a randomly generated MAC address using the given Random object and the same - * OUI values as the given MacAddress. - * - * The locally assigned bit is always set to 1. The multicast bit is always set to 0. - * - * @param base a base MacAddress whose OUI is used for generating the random address. - * If base == null then the OUI will also be randomized. - * @param r a standard Java Random object used for generating the random address. - * @return a random locally assigned MacAddress. - * - * @hide - */ - public static @NonNull MacAddress createRandomUnicastAddress(MacAddress base, Random r) { - long addr; - if (base == null) { - addr = r.nextLong() & VALID_LONG_MASK; - } else { - addr = (base.mAddr & OUI_MASK) | (NIC_MASK & r.nextLong()); - } - addr |= LOCALLY_ASSIGNED_MASK; - addr &= ~MULTICAST_MASK; - MacAddress mac = new MacAddress(addr); - if (mac.equals(DEFAULT_MAC_ADDRESS)) { - return createRandomUnicastAddress(base, r); - } - return mac; + return MacAddressUtils.createRandomUnicastAddress(BASE_GOOGLE_MAC, new SecureRandom()); } // Convenience function for working around the lack of byte literals. diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java index d28620433c21..aae9fd4725b4 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -17,6 +17,8 @@ package android.net; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Build; @@ -43,9 +45,11 @@ import java.util.concurrent.atomic.AtomicBoolean; * * @hide */ +@SystemApi public abstract class NetworkAgent { - // Guaranteed to be non-null, otherwise registerNetworkAgent() would have thrown - // an exception. Be careful in tests when mocking though. + /** + * The {@link Network} corresponding to this object. + */ @NonNull public final Network network; @@ -58,9 +62,14 @@ public abstract class NetworkAgent { private final ArrayList<Message>mPreConnectedQueue = new ArrayList<Message>(); private volatile long mLastBwRefreshTime = 0; private static final long BW_REFRESH_MIN_WIN_MS = 500; - private boolean mPollLceScheduled = false; - private AtomicBoolean mPollLcePending = new AtomicBoolean(false); - public final int mProviderId; + private boolean mBandwidthUpdateScheduled = false; + private AtomicBoolean mBandwidthUpdatePending = new AtomicBoolean(false); + + /** + * The ID of the {@link NetworkProvider} that created this object, or + * {@link NetworkProvider#ID_NONE} if unknown. + */ + public final int providerId; private static final int BASE = Protocol.BASE_NETWORK_AGENT; @@ -68,6 +77,7 @@ public abstract class NetworkAgent { * Sent by ConnectivityService to the NetworkAgent to inform it of * suspected connectivity problems on its network. The NetworkAgent * should take steps to verify and correct connectivity. + * @hide */ public static final int CMD_SUSPECT_BAD = BASE; @@ -76,6 +86,7 @@ public abstract class NetworkAgent { * ConnectivityService to pass the current NetworkInfo (connection state). * Sent when the NetworkInfo changes, mainly due to change of state. * obj = NetworkInfo + * @hide */ public static final int EVENT_NETWORK_INFO_CHANGED = BASE + 1; @@ -83,6 +94,7 @@ public abstract class NetworkAgent { * Sent by the NetworkAgent to ConnectivityService to pass the current * NetworkCapabilties. * obj = NetworkCapabilities + * @hide */ public static final int EVENT_NETWORK_CAPABILITIES_CHANGED = BASE + 2; @@ -90,11 +102,14 @@ public abstract class NetworkAgent { * Sent by the NetworkAgent to ConnectivityService to pass the current * NetworkProperties. * obj = NetworkProperties + * @hide */ public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 3; - /* centralize place where base network score, and network score scaling, will be + /** + * Centralize the place where base network score, and network score scaling, will be * stored, so as we can consistently compare apple and oranges, or wifi, ethernet and LTE + * @hide */ public static final int WIFI_BASE_SCORE = 60; @@ -102,6 +117,7 @@ public abstract class NetworkAgent { * Sent by the NetworkAgent to ConnectivityService to pass the current * network score. * obj = network score Integer + * @hide */ public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 4; @@ -114,12 +130,33 @@ public abstract class NetworkAgent { * obj = Bundle containing map from {@code REDIRECT_URL_KEY} to {@code String} * representing URL that Internet probe was redirect to, if it was redirected, * or mapping to {@code null} otherwise. + * @hide */ public static final int CMD_REPORT_NETWORK_STATUS = BASE + 7; + + /** + * Network validation suceeded. + * Corresponds to {@link NetworkCapabilities.NET_CAPABILITY_VALIDATED}. + */ + public static final int VALIDATION_STATUS_VALID = 1; + + /** + * Network validation was attempted and failed. This may be received more than once as + * subsequent validation attempts are made. + */ + public static final int VALIDATION_STATUS_NOT_VALID = 2; + + // TODO: remove. + /** @hide */ public static final int VALID_NETWORK = 1; + /** @hide */ public static final int INVALID_NETWORK = 2; + /** + * The key for the redirect URL in the Bundle argument of {@code CMD_REPORT_NETWORK_STATUS}. + * @hide + */ public static String REDIRECT_URL_KEY = "redirect URL"; /** @@ -128,6 +165,7 @@ public abstract class NetworkAgent { * CONNECTED so it can be given special treatment at that time. * * obj = boolean indicating whether to use this network even if unvalidated + * @hide */ public static final int EVENT_SET_EXPLICITLY_SELECTED = BASE + 8; @@ -138,12 +176,14 @@ public abstract class NetworkAgent { * responsibility to remember it. * * arg1 = 1 if true, 0 if false + * @hide */ public static final int CMD_SAVE_ACCEPT_UNVALIDATED = BASE + 9; /** * Sent by ConnectivityService to the NetworkAgent to inform the agent to pull * the underlying network connection for updated bandwidth information. + * @hide */ public static final int CMD_REQUEST_BANDWIDTH_UPDATE = BASE + 10; @@ -156,6 +196,7 @@ public abstract class NetworkAgent { * obj = KeepalivePacketData object describing the data to be sent * * Also used internally by ConnectivityService / KeepaliveTracker, with different semantics. + * @hide */ public static final int CMD_START_SOCKET_KEEPALIVE = BASE + 11; @@ -165,6 +206,7 @@ public abstract class NetworkAgent { * arg1 = slot number of the keepalive to stop. * * Also used internally by ConnectivityService / KeepaliveTracker, with different semantics. + * @hide */ public static final int CMD_STOP_SOCKET_KEEPALIVE = BASE + 12; @@ -178,6 +220,7 @@ public abstract class NetworkAgent { * * arg1 = slot number of the keepalive * arg2 = error code + * @hide */ public static final int EVENT_SOCKET_KEEPALIVE = BASE + 13; @@ -186,6 +229,7 @@ public abstract class NetworkAgent { * that when crossed should trigger a system wakeup and a NetworkCapabilities update. * * obj = int[] describing signal strength thresholds. + * @hide */ public static final int CMD_SET_SIGNAL_STRENGTH_THRESHOLDS = BASE + 14; @@ -193,6 +237,7 @@ public abstract class NetworkAgent { * Sent by ConnectivityService to the NeworkAgent to inform the agent to avoid * automatically reconnecting to this network (e.g. via autojoin). Happens * when user selects "No" option on the "Stay connected?" dialog box. + * @hide */ public static final int CMD_PREVENT_AUTOMATIC_RECONNECT = BASE + 15; @@ -205,6 +250,7 @@ public abstract class NetworkAgent { * This does not happen with UDP, so this message is TCP-specific. * arg1 = slot number of the keepalive to filter for. * obj = the keepalive packet to send repeatedly. + * @hide */ public static final int CMD_ADD_KEEPALIVE_PACKET_FILTER = BASE + 16; @@ -212,6 +258,7 @@ public abstract class NetworkAgent { * Sent by the KeepaliveTracker to NetworkAgent to remove a packet filter. See * {@link #CMD_ADD_KEEPALIVE_PACKET_FILTER}. * arg1 = slot number of the keepalive packet filter to remove. + * @hide */ public static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER = BASE + 17; @@ -219,27 +266,32 @@ public abstract class NetworkAgent { // of dependent changes that would conflict throughout the automerger graph. Having these // temporarily helps with the process of going through with all these dependent changes across // the entire tree. + /** @hide TODO: decide which of these to expose. */ public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, NetworkCapabilities nc, LinkProperties lp, int score) { this(looper, context, logTag, ni, nc, lp, score, null, NetworkProvider.ID_NONE); } + + /** @hide TODO: decide which of these to expose. */ public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config) { this(looper, context, logTag, ni, nc, lp, score, config, NetworkProvider.ID_NONE); } + /** @hide TODO: decide which of these to expose. */ public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, NetworkCapabilities nc, LinkProperties lp, int score, int providerId) { this(looper, context, logTag, ni, nc, lp, score, null, providerId); } + /** @hide TODO: decide which of these to expose. */ public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config, int providerId) { mHandler = new NetworkAgentHandler(looper); LOG_TAG = logTag; mContext = context; - mProviderId = providerId; + this.providerId = providerId; if (ni == null || nc == null || lp == null) { throw new IllegalArgumentException(); } @@ -287,7 +339,7 @@ public abstract class NetworkAgent { case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { if (DBG) log("NetworkAgent channel lost"); // let the client know CS is done with us. - unwanted(); + onNetworkUnwanted(); synchronized (mPreConnectedQueue) { mAsyncChannel = null; } @@ -303,16 +355,16 @@ public abstract class NetworkAgent { log("CMD_REQUEST_BANDWIDTH_UPDATE request received."); } if (currentTimeMs >= (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) { - mPollLceScheduled = false; - if (!mPollLcePending.getAndSet(true)) { - pollLceData(); + mBandwidthUpdateScheduled = false; + if (!mBandwidthUpdatePending.getAndSet(true)) { + onBandwidthUpdateRequested(); } } else { // deliver the request at a later time rather than discard it completely. - if (!mPollLceScheduled) { + if (!mBandwidthUpdateScheduled) { long waitTime = mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS - currentTimeMs + 1; - mPollLceScheduled = sendEmptyMessageDelayed( + mBandwidthUpdateScheduled = sendEmptyMessageDelayed( CMD_REQUEST_BANDWIDTH_UPDATE, waitTime); } } @@ -325,19 +377,20 @@ public abstract class NetworkAgent { + (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ") + redirectUrl); } - networkStatus(msg.arg1, redirectUrl); + onValidationStatus(msg.arg1 /* status */, redirectUrl); break; } case CMD_SAVE_ACCEPT_UNVALIDATED: { - saveAcceptUnvalidated(msg.arg1 != 0); + onSaveAcceptUnvalidated(msg.arg1 != 0); break; } case CMD_START_SOCKET_KEEPALIVE: { - startSocketKeepalive(msg); + onStartSocketKeepalive(msg.arg1 /* slot */, msg.arg2 /* interval */, + (KeepalivePacketData) msg.obj /* packet */); break; } case CMD_STOP_SOCKET_KEEPALIVE: { - stopSocketKeepalive(msg); + onStopSocketKeepalive(msg.arg1 /* slot */); break; } @@ -350,19 +403,20 @@ public abstract class NetworkAgent { for (int i = 0; i < intThresholds.length; i++) { intThresholds[i] = thresholds.get(i); } - setSignalStrengthThresholds(intThresholds); + onSignalStrengthThresholdsUpdated(intThresholds); break; } case CMD_PREVENT_AUTOMATIC_RECONNECT: { - preventAutomaticReconnect(); + onAutomaticReconnectDisabled(); break; } case CMD_ADD_KEEPALIVE_PACKET_FILTER: { - addKeepalivePacketFilter(msg); + onAddKeepalivePacketFilter(msg.arg1 /* slot */, + (KeepalivePacketData) msg.obj /* packet */); break; } case CMD_REMOVE_KEEPALIVE_PACKET_FILTER: { - removeKeepalivePacketFilter(msg); + onRemoveKeepalivePacketFilter(msg.arg1 /* slot */); break; } } @@ -397,14 +451,16 @@ public abstract class NetworkAgent { } /** - * Called by the bearer code when it has new LinkProperties data. + * Must be called by the agent when the network's {@link LinkProperties} change. + * @param linkProperties the new LinkProperties. */ - public void sendLinkProperties(LinkProperties linkProperties) { + public void sendLinkProperties(@NonNull LinkProperties linkProperties) { queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, new LinkProperties(linkProperties)); } /** - * Called by the bearer code when it has new NetworkInfo data. + * Must be called by the agent when it has a new NetworkInfo object. + * @hide TODO: expose something better. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public void sendNetworkInfo(NetworkInfo networkInfo) { @@ -412,17 +468,19 @@ public abstract class NetworkAgent { } /** - * Called by the bearer code when it has new NetworkCapabilities data. + * Must be called by the agent when the network's {@link NetworkCapabilities} change. + * @param networkCapabilities the new NetworkCapabilities. */ - public void sendNetworkCapabilities(NetworkCapabilities networkCapabilities) { - mPollLcePending.set(false); + public void sendNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) { + mBandwidthUpdatePending.set(false); mLastBwRefreshTime = System.currentTimeMillis(); queueOrSendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED, new NetworkCapabilities(networkCapabilities)); } /** - * Called by the bearer code when it has a new score for this network. + * Must be called by the agent to update the score of this network. + * @param score the new score. */ public void sendNetworkScore(int score) { if (score < 0) { @@ -434,14 +492,16 @@ public abstract class NetworkAgent { } /** - * Called by the bearer code when it has a new NetworkScore for this network. + * Must be called by the agent when it has a new {@link NetworkScore} for this network. + * @param ns the new score. + * @hide TODO: unhide the NetworkScore class, and rename to sendNetworkScore. */ public void updateScore(@NonNull NetworkScore ns) { queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, new NetworkScore(ns)); } /** - * Called by the bearer to indicate this network was manually selected by the user. + * Must be called by the agent to indicate this network was manually selected by the user. * This should be called before the NetworkInfo is marked CONNECTED so that this * Network can be given special treatment at that time. If {@code acceptUnvalidated} is * {@code true}, then the system will switch to this network. If it is {@code false} and the @@ -450,15 +510,16 @@ public abstract class NetworkAgent { * {@link #saveAcceptUnvalidated} to persist the user's choice. Thus, if the transport ever * calls this method with {@code acceptUnvalidated} set to {@code false}, it must also implement * {@link #saveAcceptUnvalidated} to respect the user's choice. + * @hide should move to NetworkAgentConfig. */ public void explicitlySelected(boolean acceptUnvalidated) { explicitlySelected(true /* explicitlySelected */, acceptUnvalidated); } /** - * Called by the bearer to indicate whether the network was manually selected by the user. - * This should be called before the NetworkInfo is marked CONNECTED so that this - * Network can be given special treatment at that time. + * Must be called by the agent to indicate whether the network was manually selected by the + * user. This should be called before the network becomes connected, so it can be given + * special treatment when it does. * * If {@code explicitlySelected} is {@code true}, and {@code acceptUnvalidated} is {@code true}, * then the system will switch to this network. If {@code explicitlySelected} is {@code true} @@ -473,6 +534,7 @@ public abstract class NetworkAgent { * {@code true}, the system will interpret this as the user having accepted partial connectivity * on this network. Thus, the system will switch to the network and consider it validated even * if it only provides partial connectivity, but the network is not otherwise treated specially. + * @hide should move to NetworkAgentConfig. */ public void explicitlySelected(boolean explicitlySelected, boolean acceptUnvalidated) { queueOrSendMessage(EVENT_SET_EXPLICITLY_SELECTED, @@ -486,73 +548,126 @@ public abstract class NetworkAgent { * as well, either canceling NetworkRequests or altering their score such that this * network won't be immediately requested again. */ - abstract protected void unwanted(); + public void onNetworkUnwanted() { + unwanted(); + } + /** @hide TODO delete once subclasses have moved to onNetworkUnwanted. */ + protected void unwanted() { + } /** * Called when ConnectivityService request a bandwidth update. The parent factory * shall try to overwrite this method and produce a bandwidth update if capable. */ + public void onBandwidthUpdateRequested() { + pollLceData(); + } + /** @hide TODO delete once subclasses have moved to onBandwidthUpdateRequested. */ protected void pollLceData() { } /** * Called when the system determines the usefulness of this network. * - * Networks claiming internet connectivity will have their internet - * connectivity verified. + * The system attempts to validate Internet connectivity on networks that provide the + * {@link NetworkCapabilities#NET_CAPABILITY_INTERNET} capability. * * Currently there are two possible values: - * {@code VALID_NETWORK} if the system is happy with the connection, - * {@code INVALID_NETWORK} if the system is not happy. - * TODO - add indications of captive portal-ness and related success/failure, - * ie, CAPTIVE_SUCCESS_NETWORK, CAPTIVE_NETWORK for successful login and detection + * {@code VALIDATION_STATUS_VALID} if Internet connectivity was validated, + * {@code VALIDATION_STATUS_NOT_VALID} if Internet connectivity was not validated. * - * This may be called multiple times as the network status changes and may - * generate false negatives if we lose ip connectivity before the link is torn down. + * This may be called multiple times as network status changes, or if there are multiple + * subsequent attempts to validate connectivity that fail. * - * @param status one of {@code VALID_NETWORK} or {@code INVALID_NETWORK}. - * @param redirectUrl If the Internet probe was redirected, this is the destination it was - * redirected to, otherwise {@code null}. + * @param status one of {@code VALIDATION_STATUS_VALID} or {@code VALIDATION_STATUS_NOT_VALID}. + * @param redirectUrl If Internet connectivity is being redirected (e.g., on a captive portal), + * this is the destination the probes are being redirected to, otherwise {@code null}. */ + public void onValidationStatus(int status, @Nullable String redirectUrl) { + networkStatus(status, redirectUrl); + } + /** @hide TODO delete once subclasses have moved to onValidationStatus */ protected void networkStatus(int status, String redirectUrl) { } + /** * Called when the user asks to remember the choice to use this network even if unvalidated. * The transport is responsible for remembering the choice, and the next time the user connects * to the network, should explicitlySelected with {@code acceptUnvalidated} set to {@code true}. * This method will only be called if {@link #explicitlySelected} was called with * {@code acceptUnvalidated} set to {@code false}. + * @param accept whether the user wants to use the network even if unvalidated. */ + public void onSaveAcceptUnvalidated(boolean accept) { + saveAcceptUnvalidated(accept); + } + /** @hide TODO delete once subclasses have moved to onSaveAcceptUnvalidated */ protected void saveAcceptUnvalidated(boolean accept) { } /** * Requests that the network hardware send the specified packet at the specified interval. - */ + * + * @param slot the hardware slot on which to start the keepalive. + * @param intervalSeconds the interval between packets + * @param packet the packet to send. + */ + public void onStartSocketKeepalive(int slot, int intervalSeconds, + @NonNull KeepalivePacketData packet) { + Message msg = mHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, slot, intervalSeconds, + packet); + startSocketKeepalive(msg); + msg.recycle(); + } + /** @hide TODO delete once subclasses have moved to onStartSocketKeepalive */ protected void startSocketKeepalive(Message msg) { onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_UNSUPPORTED); } /** - * Requests that the network hardware send the specified packet at the specified interval. + * Requests that the network hardware stop a previously-started keepalive. + * + * @param slot the hardware slot on which to stop the keepalive. */ + public void onStopSocketKeepalive(int slot) { + Message msg = mHandler.obtainMessage(CMD_STOP_SOCKET_KEEPALIVE, slot, 0, null); + stopSocketKeepalive(msg); + msg.recycle(); + } + /** @hide TODO delete once subclasses have moved to onStopSocketKeepalive */ protected void stopSocketKeepalive(Message msg) { onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_UNSUPPORTED); } /** - * Called by the network when a socket keepalive event occurs. + * Must be called by the agent when a socket keepalive event occurs. + * + * @param slot the hardware slot on which the event occurred. + * @param event the event that occurred. */ + public void sendSocketKeepaliveEvent(int slot, int event) { + queueOrSendMessage(EVENT_SOCKET_KEEPALIVE, slot, event); + } + /** @hide TODO delete once callers have moved to sendSocketKeepaliveEvent */ public void onSocketKeepaliveEvent(int slot, int reason) { - queueOrSendMessage(EVENT_SOCKET_KEEPALIVE, slot, reason); + sendSocketKeepaliveEvent(slot, reason); } /** * Called by ConnectivityService to add specific packet filter to network hardware to block - * ACKs matching the sent keepalive packets. Implementations that support this feature must - * override this method. + * replies (e.g., TCP ACKs) matching the sent keepalive packets. Implementations that support + * this feature must override this method. + * + * @param slot the hardware slot on which the keepalive should be sent. + * @param packet the packet that is being sent. */ + public void onAddKeepalivePacketFilter(int slot, @NonNull KeepalivePacketData packet) { + Message msg = mHandler.obtainMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0, packet); + addKeepalivePacketFilter(msg); + msg.recycle(); + } + /** @hide TODO delete once subclasses have moved to onAddKeepalivePacketFilter */ protected void addKeepalivePacketFilter(Message msg) { } @@ -560,14 +675,28 @@ public abstract class NetworkAgent { * Called by ConnectivityService to remove a packet filter installed with * {@link #addKeepalivePacketFilter(Message)}. Implementations that support this feature * must override this method. + * + * @param slot the hardware slot on which the keepalive is being sent. */ + public void onRemoveKeepalivePacketFilter(int slot) { + Message msg = mHandler.obtainMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER, slot, 0, null); + removeKeepalivePacketFilter(msg); + msg.recycle(); + } + /** @hide TODO delete once subclasses have moved to onRemoveKeepalivePacketFilter */ protected void removeKeepalivePacketFilter(Message msg) { } /** * Called by ConnectivityService to inform this network transport of signal strength thresholds * that when crossed should trigger a system wakeup and a NetworkCapabilities update. + * + * @param thresholds the array of thresholds that should trigger wakeups. */ + public void onSignalStrengthThresholdsUpdated(@NonNull int[] thresholds) { + setSignalStrengthThresholds(thresholds); + } + /** @hide TODO delete once subclasses have moved to onSetSignalStrengthThresholds */ protected void setSignalStrengthThresholds(int[] thresholds) { } @@ -577,9 +706,14 @@ public abstract class NetworkAgent { * responsible for making sure the device does not automatically reconnect to the same network * after the {@code unwanted} call. */ + public void onAutomaticReconnectDisabled() { + preventAutomaticReconnect(); + } + /** @hide TODO delete once subclasses have moved to onAutomaticReconnectDisabled */ protected void preventAutomaticReconnect() { } + /** @hide */ protected void log(String s) { Log.d(LOG_TAG, "NetworkAgent: " + s); } diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java index 824ddb8dd260..e27103755e6d 100644 --- a/core/java/android/net/NetworkFactory.java +++ b/core/java/android/net/NetworkFactory.java @@ -115,13 +115,6 @@ public class NetworkFactory extends Handler { */ private static final int CMD_SET_FILTER = BASE + 3; - /** - * Sent by NetworkFactory to ConnectivityService to indicate that a request is - * unfulfillable. - * @see #releaseRequestAsUnfulfillableByAnyFactory(NetworkRequest). - */ - public static final int EVENT_UNFULFILLABLE_REQUEST = BASE + 4; - private final Context mContext; private final ArrayList<Message> mPreConnectedQueue = new ArrayList<Message>(); private final String LOG_TAG; diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index 9731f3ca186d..7fd86f68876a 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -455,6 +455,19 @@ public class NetworkRequest implements Parcelable { } /** + * Returns true iff. the capabilities requested in this NetworkRequest are satisfied by the + * provided {@link NetworkCapabilities}. + * + * @param nc Capabilities that should satisfy this NetworkRequest. null capabilities do not + * satisfy any request. + * @hide + */ + @SystemApi + public boolean satisfiedBy(@Nullable NetworkCapabilities nc) { + return networkCapabilities.satisfiedByNetworkCapabilities(nc); + } + + /** * @see Builder#addTransportType(int) */ public boolean hasTransport(@Transport int transportType) { diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java index 08cc4e24b245..779f7bc91e8f 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/core/java/android/net/NetworkUtils.java @@ -31,7 +31,6 @@ import android.util.Pair; import java.io.FileDescriptor; import java.math.BigInteger; import java.net.Inet4Address; -import java.net.Inet6Address; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException; @@ -313,15 +312,6 @@ public class NetworkUtils { } /** - * Check if IP address type is consistent between two InetAddress. - * @return true if both are the same type. False otherwise. - */ - public static boolean addressTypeMatches(InetAddress left, InetAddress right) { - return (((left instanceof Inet4Address) && (right instanceof Inet4Address)) || - ((left instanceof Inet6Address) && (right instanceof Inet6Address))); - } - - /** * Convert a 32 char hex string into a Inet6Address. * throws a runtime exception if the string isn't 32 chars, isn't hex or can't be * made into an Inet6Address diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java index ea6002ca29e6..e08809457649 100644 --- a/core/java/android/net/RouteInfo.java +++ b/core/java/android/net/RouteInfo.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; +import android.net.util.NetUtils; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -441,21 +442,7 @@ public final class RouteInfo implements Parcelable { @UnsupportedAppUsage @Nullable public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) { - if ((routes == null) || (dest == null)) return null; - - RouteInfo bestRoute = null; - // pick a longest prefix match under same address type - for (RouteInfo route : routes) { - if (NetworkUtils.addressTypeMatches(route.mDestination.getAddress(), dest)) { - if ((bestRoute != null) && - (bestRoute.mDestination.getPrefixLength() >= - route.mDestination.getPrefixLength())) { - continue; - } - if (route.matches(dest)) bestRoute = route; - } - } - return bestRoute; + return NetUtils.selectBestRoute(routes, dest); } /** diff --git a/core/java/android/net/SocketKeepalive.java b/core/java/android/net/SocketKeepalive.java index fb224fbe1318..fc9a8f63c131 100644 --- a/core/java/android/net/SocketKeepalive.java +++ b/core/java/android/net/SocketKeepalive.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Binder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -53,7 +54,11 @@ import java.util.concurrent.Executor; public abstract class SocketKeepalive implements AutoCloseable { static final String TAG = "SocketKeepalive"; - /** @hide */ + /** + * No errors. + * @hide + */ + @SystemApi public static final int SUCCESS = 0; /** @hide */ diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl index 7b2d148bfa37..416d69229536 100644 --- a/core/java/android/os/IVibratorService.aidl +++ b/core/java/android/os/IVibratorService.aidl @@ -24,7 +24,7 @@ interface IVibratorService { boolean hasVibrator(); boolean hasAmplitudeControl(); - boolean setAlwaysOnEffect(int id, in VibrationEffect effect, + boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, in VibrationEffect effect, in VibrationAttributes attributes); void vibrate(int uid, String opPkg, in VibrationEffect effect, in VibrationAttributes attributes, String reason, IBinder token); diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java index c1542c7ee68f..8050454a8ac3 100644 --- a/core/java/android/os/SystemVibrator.java +++ b/core/java/android/os/SystemVibrator.java @@ -70,14 +70,15 @@ public class SystemVibrator extends Vibrator { } @Override - public boolean setAlwaysOnEffect(int id, VibrationEffect effect, AudioAttributes attributes) { + public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, VibrationEffect effect, + AudioAttributes attributes) { if (mService == null) { Log.w(TAG, "Failed to set always-on effect; no vibrator service."); return false; } try { VibrationAttributes atr = new VibrationAttributes.Builder(attributes, effect).build(); - return mService.setAlwaysOnEffect(id, effect, atr); + return mService.setAlwaysOnEffect(uid, opPkg, alwaysOnId, effect, atr); } catch (RemoteException e) { Log.w(TAG, "Failed to set always-on effect.", e); } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index d8fadfb41189..2eaefca0efa3 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -192,7 +192,11 @@ public class UserManager { /** * Specifies if a user is disallowed from changing Wi-Fi * access points. The default value is <code>false</code>. - * <p>This restriction has no effect in a managed profile. + * <p> + * Device owner and profile owner can set this restriction, although the restriction has no + * effect in a managed profile. When it is set by the profile owner of an organization-owned + * managed profile on the parent profile, it will disallow the personal user from changing + * Wi-Fi access points. * * <p>Key for user restrictions. * <p>Type: Boolean @@ -242,8 +246,13 @@ public class UserManager { /** * Specifies if a user is disallowed from turning on location sharing. * The default value is <code>false</code>. - * <p>In a managed profile, location sharing always reflects the primary user's setting, but + * <p> + * In a managed profile, location sharing always reflects the primary user's setting, but * can be overridden and forced off by setting this restriction to true in the managed profile. + * <p> + * Device owner and profile owner can set this restriction. When it is set by the profile + * owner of an organization-owned managed profile on the parent profile, it will prevent the + * user from turning on location sharing in the personal profile. * * <p>Key for user restrictions. * <p>Type: Boolean @@ -349,9 +358,14 @@ public class UserManager { * Specifies if a user is disallowed from configuring bluetooth. * This does <em>not</em> restrict the user from turning bluetooth on or off. * The default value is <code>false</code>. - * <p>This restriction doesn't prevent the user from using bluetooth. For disallowing usage of + * <p> + * This restriction doesn't prevent the user from using bluetooth. For disallowing usage of * bluetooth completely on the device, use {@link #DISALLOW_BLUETOOTH}. - * <p>This restriction has no effect in a managed profile. + * <p> + * Device owner and profile owner can set this restriction, although the restriction has no + * effect in a managed profile. When it is set by the profile owner of an organization-owned + * managed profile on the parent profile, it will disallow the personal user from configuring + * bluetooth. * * <p>Key for user restrictions. * <p>Type: Boolean @@ -364,8 +378,10 @@ public class UserManager { /** * Specifies if bluetooth is disallowed on the device. * - * <p> This restriction can only be set by the device owner and the profile owner on the - * primary user and it applies globally - i.e. it disables bluetooth on the entire device. + * <p> This restriction can only be set by the device owner, the profile owner on the + * primary user or the profile owner of an organization-owned managed profile on the + * parent profile and it applies globally - i.e. it disables bluetooth on the entire + * device. * <p>The default value is <code>false</code>. * <p>Key for user restrictions. * <p>Type: Boolean @@ -377,8 +393,9 @@ public class UserManager { /** * Specifies if outgoing bluetooth sharing is disallowed on the device. Device owner and profile - * owner can set this restriction. When it is set by device owner, all users on this device will - * be affected. + * owner can set this restriction. When it is set by device owner or the profile owner of an + * organization-owned managed profile on the parent profile, all users on this device will be + * affected. * * <p>Default is <code>true</code> for managed profiles and false for otherwise. When a device * upgrades to {@link android.os.Build.VERSION_CODES#O}, the system sets it for all existing @@ -394,7 +411,8 @@ public class UserManager { /** * Specifies if a user is disallowed from transferring files over - * USB. This can only be set by device owners and profile owners on the primary user. + * USB. This can only be set by device owners, profile owners on the primary user or + * profile owners of organization-owned managed profiles on the parent profile. * The default value is <code>false</code>. * * <p>Key for user restrictions. @@ -453,8 +471,9 @@ public class UserManager { /** * Specifies if a user is disallowed from enabling or accessing debugging features. When set on - * the primary user, disables debugging features altogether, including USB debugging. When set - * on a managed profile or a secondary user, blocks debugging for that user only, including + * the primary user or by the profile owner of an organization-owned managed profile on the + * parent profile, disables debugging features altogether, including USB debugging. When set on + * a managed profile or a secondary user, blocks debugging for that user only, including * starting activities, making service calls, accessing content providers, sending broadcasts, * installing/uninstalling packages, clearing user data, etc. * The default value is <code>false</code>. @@ -485,18 +504,19 @@ public class UserManager { /** * Specifies if a user is disallowed from enabling or disabling location providers. As a - * result, user is disallowed from turning on or off location. Device owner and profile owners - * can set this restriction and it only applies on the managed user. + * result, user is disallowed from turning on or off location. * - * <p>In a managed profile, location sharing is forced off when it's off on primary user, so - * user can still turn off location sharing on managed profile when the restriction is set by - * profile owner on managed profile. - * - * <p>This user restriction is different from {@link #DISALLOW_SHARE_LOCATION}, + * <p> + * In a managed profile, location sharing is forced off when it is turned off on the primary + * user or by the profile owner of an organization-owned managed profile on the parent profile. + * The user can still turn off location sharing on a managed profile when the restriction is + * set by the profile owner on a managed profile. + * <p> + * This user restriction is different from {@link #DISALLOW_SHARE_LOCATION}, * as the device owner or profile owner can still enable or disable location mode via * {@link DevicePolicyManager#setLocationEnabled} when this restriction is on. - * - * <p>The default value is <code>false</code>. + * <p> + * The default value is <code>false</code>. * * <p>Key for user restrictions. * <p>Type: Boolean @@ -510,7 +530,8 @@ public class UserManager { /** * Specifies if date, time and timezone configuring is disallowed. * - * <p>When restriction is set by device owners, it applies globally - i.e., it disables date, + * <p>When restriction is set by device owners or profile owners of organization-owned + * managed profiles on the parent profile, it applies globally - i.e., it disables date, * time and timezone setting on the entire device and all users will be affected. When it's set * by profile owners, it's only applied to the managed user. * <p>The default value is <code>false</code>. @@ -526,8 +547,9 @@ public class UserManager { /** * Specifies if a user is disallowed from configuring Tethering - * & portable hotspots. This can only be set by device owners and profile owners on the - * primary user. The default value is <code>false</code>. + * & portable hotspots. This can only be set by device owners, profile owners on the + * primary user or profile owners of organization-owned managed profiles on the parent profile. + * The default value is <code>false</code>. * <p>In Android 9.0 or higher, if tethering is enabled when this restriction is set, * tethering will be automatically turned off. * @@ -571,8 +593,8 @@ public class UserManager { /** * Specifies if a user is disallowed from adding new users. This can only be set by device - * owners and profile owners on the primary user. - * The default value is <code>false</code>. + * owners, profile owners on the primary user or profile owners of organization-owned managed + * profiles on the parent profile. The default value is <code>false</code>. * <p>This restriction has no effect on secondary users and managed profiles since only the * primary user can add other users. * @@ -621,7 +643,8 @@ public class UserManager { /** * Specifies if a user is disallowed from configuring cell - * broadcasts. This can only be set by device owners and profile owners on the primary user. + * broadcasts. This can only be set by device owners, profile owners on the primary user or + * profile owners of organization-owned managed profiles on the parent profile. * The default value is <code>false</code>. * <p>This restriction has no effect on secondary users and managed profiles since only the * primary user can configure cell broadcasts. @@ -636,7 +659,8 @@ public class UserManager { /** * Specifies if a user is disallowed from configuring mobile - * networks. This can only be set by device owners and profile owners on the primary user. + * networks. This can only be set by device owners, profile owners on the primary user or + * profile owners of organization-owned managed profiles on the parent profile. * The default value is <code>false</code>. * <p>This restriction has no effect on secondary users and managed profiles since only the * primary user can configure mobile networks. @@ -739,6 +763,10 @@ public class UserManager { /** * Specifies that the user is not allowed to send or receive * SMS messages. The default value is <code>false</code>. + * <p> + * Device owner and profile owner can set this restriction. When it is set by the + * profile owner of an organization-owned managed profile on the parent profile, + * it will disable SMS in the personal profile. * * <p>Key for user restrictions. * <p>Type: Boolean @@ -857,7 +885,8 @@ public class UserManager { /** * Specifies if the user is not allowed to reboot the device into safe boot mode. - * This can only be set by device owners and profile owners on the primary user. + * This can only be set by device owners, profile owners on the primary user or profile + * owners of organization-owned managed profiles on the parent profile. * The default value is <code>false</code>. * * <p>Key for user restrictions. @@ -896,6 +925,12 @@ public class UserManager { /** * Specifies if a user is not allowed to use the camera. + * <p> + * Device owner and profile owner can set this restriction. When the restriction is set by + * the device owner or the profile owner of an organization-owned managed profile on the + * parent profile, it is applied globally. + * <p> + * The default value is <code>false</code>. * * @see DevicePolicyManager#addUserRestriction(ComponentName, String) * @see DevicePolicyManager#clearUserRestriction(ComponentName, String) @@ -916,7 +951,8 @@ public class UserManager { /** * Specifies if a user is not allowed to use cellular data when roaming. This can only be set by - * device owners. The default value is <code>false</code>. + * device owners or profile owners of organization-owned managed profiles on the parent profile. + * The default value is <code>false</code>. * * @see DevicePolicyManager#addUserRestriction(ComponentName, String) * @see DevicePolicyManager#clearUserRestriction(ComponentName, String) @@ -1011,8 +1047,9 @@ public class UserManager { * Specifies if the contents of a user's screen is not allowed to be captured for artificial * intelligence purposes. * - * <p>Device owner and profile owner can set this restriction. When it is set by device owner, - * only the target user will be affected. + * <p>Device owner and profile owner can set this restriction. When it is set by the + * device owner or the profile owner of an organization-owned managed profile on the parent + * profile, only the target user will be affected. * * <p>The default value is <code>false</code>. * @@ -1026,8 +1063,9 @@ public class UserManager { * Specifies if the current user is able to receive content suggestions for selections based on * the contents of their screen. * - * <p>Device owner and profile owner can set this restriction. When it is set by device owner, - * only the target user will be affected. + * <p>Device owner and profile owner can set this restriction. When it is set by the + * device owner or the profile owner of an organization-owned managed profile on the parent + * profile, only the target user will be affected. * * <p>The default value is <code>false</code>. * @@ -1093,7 +1131,9 @@ public class UserManager { * * <p>The default value is <code>false</code>. * - * <p>This user restriction can only be applied by the Device Owner. + * <p>This user restriction can only be applied by the device owner or the profile owner + * of an organization-owned managed profile on the parent profile. + * * <p>Key for user restrictions. * <p>Type: Boolean * @see DevicePolicyManager#addUserRestriction(ComponentName, String) @@ -2413,6 +2453,13 @@ public class UserManager { * by {@link #createUser(String, String, int)} or {@link #createGuest(Context, String)}), it * takes less time. * + * <p>This method completes the majority of work necessary for user creation: it + * creates user data, CE and DE encryption keys, app data directories, initializes the user and + * grants default permissions. When pre-created users become "real" users, only then are + * components notified of new user creation by firing user creation broadcasts. + * + * <p>All pre-created users are removed during system upgrade. + * * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} permission. * * @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_GUEST}. @@ -2424,6 +2471,7 @@ public class UserManager { * * @hide */ + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public @Nullable UserInfo preCreateUser(@NonNull String userType) { try { return mService.preCreateUser(userType); diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index ccbb0f191f6f..ae75f3d0d7e6 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -155,7 +155,7 @@ public abstract class Vibrator { /** * Configure an always-on haptics effect. * - * @param id The board-specific always-on ID to configure. + * @param alwaysOnId The board-specific always-on ID to configure. * @param effect Vibration effect to assign to always-on id. Passing null will disable it. * @param attributes {@link AudioAttributes} corresponding to the vibration. For example, * specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or @@ -164,8 +164,17 @@ public abstract class Vibrator { * @hide */ @RequiresPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON) - public boolean setAlwaysOnEffect(int id, @Nullable VibrationEffect effect, - @Nullable AudioAttributes attributes) { + public boolean setAlwaysOnEffect(int alwaysOnId, @Nullable VibrationEffect effect, + @Nullable AudioAttributes attributes) { + return setAlwaysOnEffect(Process.myUid(), mPackageName, alwaysOnId, effect, attributes); + } + + /** + * @hide + */ + @RequiresPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON) + public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, + @Nullable VibrationEffect effect, @Nullable AudioAttributes attributes) { Log.w(TAG, "Always-on effects aren't supported"); return false; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 089122ddb023..e2b33e01d45f 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6420,6 +6420,15 @@ public final class Settings { "accessibility_button_target_component"; /** + * The system class name of magnification controller which is a target to be toggled via + * accessibility shortcut or accessibility button. + * + * @hide + */ + public static final String ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER = + "com.android.server.accessibility.MagnificationController"; + + /** * If touch exploration is enabled. */ public static final String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled"; diff --git a/core/java/android/timezone/CountryTimeZones.java b/core/java/android/timezone/CountryTimeZones.java new file mode 100644 index 000000000000..ada59d6d7d55 --- /dev/null +++ b/core/java/android/timezone/CountryTimeZones.java @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.timezone; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.icu.util.TimeZone; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * Information about a country's time zones. + * + * @hide + */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public final class CountryTimeZones { + + /** + * A mapping to a time zone ID with some associated metadata. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final class TimeZoneMapping { + + private libcore.timezone.CountryTimeZones.TimeZoneMapping mDelegate; + + TimeZoneMapping(libcore.timezone.CountryTimeZones.TimeZoneMapping delegate) { + this.mDelegate = Objects.requireNonNull(delegate); + } + + /** + * Returns the ID for this mapping. See also {@link #getTimeZone()} which handles when the + * ID is unrecognized. + */ + @NonNull + public String getTimeZoneId() { + return mDelegate.timeZoneId; + } + + /** + * Returns a {@link TimeZone} object for this mapping, or {@code null} if the ID is + * unrecognized. + */ + @Nullable + public TimeZone getTimeZone() { + return mDelegate.getTimeZone(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TimeZoneMapping that = (TimeZoneMapping) o; + return this.mDelegate.equals(that.mDelegate); + } + + @Override + public int hashCode() { + return this.mDelegate.hashCode(); + } + + @Override + public String toString() { + return mDelegate.toString(); + } + } + + /** + * The result of lookup up a time zone using offset information (and possibly more). + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final class OffsetResult { + + private final TimeZone mTimeZone; + private final boolean mIsOnlyMatch; + + /** Creates an instance with the supplied information. */ + public OffsetResult(@NonNull TimeZone timeZone, boolean isOnlyMatch) { + mTimeZone = Objects.requireNonNull(timeZone); + mIsOnlyMatch = isOnlyMatch; + } + + /** + * Returns a time zone that matches the supplied criteria. + */ + @NonNull + public TimeZone getTimeZone() { + return mTimeZone; + } + + /** + * Returns {@code true} if there is only one matching time zone for the supplied criteria. + */ + public boolean isOnlyMatch() { + return mIsOnlyMatch; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + OffsetResult that = (OffsetResult) o; + return mIsOnlyMatch == that.mIsOnlyMatch + && mTimeZone.getID().equals(that.mTimeZone.getID()); + } + + @Override + public int hashCode() { + return Objects.hash(mTimeZone, mIsOnlyMatch); + } + + @Override + public String toString() { + return "OffsetResult{" + + "mTimeZone=" + mTimeZone + + ", mIsOnlyMatch=" + mIsOnlyMatch + + '}'; + } + } + + @NonNull + private final libcore.timezone.CountryTimeZones mDelegate; + + CountryTimeZones(libcore.timezone.CountryTimeZones delegate) { + mDelegate = delegate; + } + + /** + * Returns true if the ISO code for the country is a match for the one specified. + */ + public boolean isForCountryCode(@NonNull String countryIso) { + return mDelegate.isForCountryCode(countryIso); + } + + /** + * Returns the default time zone ID for the country. Can return {@code null} in cases when no + * data is available or the time zone ID was not recognized. + */ + @Nullable + public String getDefaultTimeZoneId() { + return mDelegate.getDefaultTimeZoneId(); + } + + /** + * Returns the default time zone for the country. Can return {@code null} in cases when no data + * is available or the time zone ID was not recognized. + */ + @Nullable + public TimeZone getDefaultTimeZone() { + return mDelegate.getDefaultTimeZone(); + } + + /** + * Qualifier for a country's default time zone. {@code true} indicates whether the default + * would be a good choice <em>generally</em> when there's no other information available. + */ + public boolean isDefaultTimeZoneBoosted() { + return mDelegate.getDefaultTimeZoneBoost(); + } + + /** + * Returns true if the country has at least one zone that is the same as UTC at the given time. + */ + public boolean hasUtcZone(long whenMillis) { + return mDelegate.hasUtcZone(whenMillis); + } + + /** + * Returns a time zone for the country, if there is one, that matches the desired properties. If + * there are multiple matches and the {@code bias} is one of them then it is returned, otherwise + * an arbitrary match is returned based on the {@link #getEffectiveTimeZoneMappingsAt(long)} + * ordering. + * + * @param totalOffsetMillis the offset from UTC at {@code whenMillis} + * @param isDst the Daylight Savings Time state at {@code whenMillis}. {@code true} means DST, + * {@code false} means not DST, {@code null} means unknown + * @param dstOffsetMillis the part of {@code totalOffsetMillis} contributed by DST, only used if + * {@code isDst} is {@code true}. The value can be {@code null} if the DST offset is + * unknown + * @param whenMillis the UTC time to match against + * @param bias the time zone to prefer, can be {@code null} + */ + @Nullable + public OffsetResult lookupByOffsetWithBias(int totalOffsetMillis, @Nullable Boolean isDst, + @SuppressLint("AutoBoxing") @Nullable Integer dstOffsetMillis, long whenMillis, + @Nullable TimeZone bias) { + libcore.timezone.CountryTimeZones.OffsetResult delegateOffsetResult = + mDelegate.lookupByOffsetWithBias( + totalOffsetMillis, isDst, dstOffsetMillis, whenMillis, bias); + return delegateOffsetResult == null ? null : + new OffsetResult(delegateOffsetResult.mTimeZone, delegateOffsetResult.mOneMatch); + } + + /** + * Returns an immutable, ordered list of time zone mappings for the country in an undefined but + * "priority" order, filtered so that only "effective" time zone IDs are returned. An + * "effective" time zone is one that differs from another time zone used in the country after + * {@code whenMillis}. The list can be empty if there were no zones configured or the configured + * zone IDs were not recognized. + */ + @NonNull + public List<TimeZoneMapping> getEffectiveTimeZoneMappingsAt(long whenMillis) { + List<libcore.timezone.CountryTimeZones.TimeZoneMapping> delegateList = + mDelegate.getEffectiveTimeZoneMappingsAt(whenMillis); + + List<TimeZoneMapping> toReturn = new ArrayList<>(delegateList.size()); + for (libcore.timezone.CountryTimeZones.TimeZoneMapping delegateMapping : delegateList) { + toReturn.add(new TimeZoneMapping(delegateMapping)); + } + return Collections.unmodifiableList(toReturn); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CountryTimeZones that = (CountryTimeZones) o; + return mDelegate.equals(that.mDelegate); + } + + @Override + public int hashCode() { + return Objects.hash(mDelegate); + } + + @Override + public String toString() { + return mDelegate.toString(); + } +} diff --git a/core/java/android/timezone/TelephonyLookup.java b/core/java/android/timezone/TelephonyLookup.java new file mode 100644 index 000000000000..39dbe85cb485 --- /dev/null +++ b/core/java/android/timezone/TelephonyLookup.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.timezone; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; + +import com.android.internal.annotations.GuardedBy; + +import java.util.Objects; + +/** + * A class that can find time zone-related information about telephony networks. + * + * @hide + */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public class TelephonyLookup { + + private static Object sLock = new Object(); + @GuardedBy("sLock") + private static TelephonyLookup sInstance; + + @NonNull + private final libcore.timezone.TelephonyLookup mDelegate; + + /** + * Obtains an instance for use when resolving telephony time zone information. This method never + * returns {@code null}. + */ + @NonNull + public static TelephonyLookup getInstance() { + synchronized (sLock) { + if (sInstance == null) { + sInstance = new TelephonyLookup(libcore.timezone.TelephonyLookup.getInstance()); + } + return sInstance; + } + } + + private TelephonyLookup(@NonNull libcore.timezone.TelephonyLookup delegate) { + mDelegate = Objects.requireNonNull(delegate); + } + + /** + * Returns an object capable of querying telephony network information. This method can return + * {@code null} in the event of an error while reading the underlying data files. + */ + @Nullable + public TelephonyNetworkFinder getTelephonyNetworkFinder() { + libcore.timezone.TelephonyNetworkFinder telephonyNetworkFinderDelegate = + mDelegate.getTelephonyNetworkFinder(); + return telephonyNetworkFinderDelegate != null + ? new TelephonyNetworkFinder(telephonyNetworkFinderDelegate) : null; + } +} diff --git a/core/java/android/timezone/TelephonyNetwork.java b/core/java/android/timezone/TelephonyNetwork.java new file mode 100644 index 000000000000..ae39fbddfa1c --- /dev/null +++ b/core/java/android/timezone/TelephonyNetwork.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.timezone; + +import android.annotation.NonNull; +import android.annotation.SystemApi; + +import java.util.Objects; + +/** + * Information about a telephony network. + * + * @hide + */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public class TelephonyNetwork { + + @NonNull + private final libcore.timezone.TelephonyNetwork mDelegate; + + TelephonyNetwork(@NonNull libcore.timezone.TelephonyNetwork delegate) { + mDelegate = Objects.requireNonNull(delegate); + } + + /** + * Returns the Mobile Country Code of the network. + */ + @NonNull + public String getMcc() { + return mDelegate.getMcc(); + } + + /** + * Returns the Mobile Network Code of the network. + */ + @NonNull + public String getMnc() { + return mDelegate.getMnc(); + } + + /** + * Returns the country in which the network operates as an ISO 3166 alpha-2 (lower case). + */ + @NonNull + public String getCountryIsoCode() { + return mDelegate.getCountryIsoCode(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TelephonyNetwork that = (TelephonyNetwork) o; + return mDelegate.equals(that.mDelegate); + } + + @Override + public int hashCode() { + return Objects.hash(mDelegate); + } + + @Override + public String toString() { + return "TelephonyNetwork{" + + "mDelegate=" + mDelegate + + '}'; + } +} diff --git a/core/java/android/timezone/TelephonyNetworkFinder.java b/core/java/android/timezone/TelephonyNetworkFinder.java new file mode 100644 index 000000000000..a81a516c4b33 --- /dev/null +++ b/core/java/android/timezone/TelephonyNetworkFinder.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.timezone; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; + +import java.util.Objects; + +/** + * A class that can find telephony networks loaded via {@link TelephonyLookup}. + * + * @hide + */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public class TelephonyNetworkFinder { + + @NonNull + private final libcore.timezone.TelephonyNetworkFinder mDelegate; + + TelephonyNetworkFinder(libcore.timezone.TelephonyNetworkFinder delegate) { + mDelegate = Objects.requireNonNull(delegate); + } + + /** + * Returns information held about a specific MCC + MNC combination. It is expected for this + * method to return {@code null}. Only known, unusual networks will typically have information + * returned, e.g. if they operate in countries other than the one suggested by their MCC. + */ + @Nullable + public TelephonyNetwork findNetworkByMccMnc(@NonNull String mcc, @NonNull String mnc) { + Objects.requireNonNull(mcc); + Objects.requireNonNull(mnc); + + libcore.timezone.TelephonyNetwork telephonyNetworkDelegate = + mDelegate.findNetworkByMccMnc(mcc, mnc); + return telephonyNetworkDelegate != null + ? new TelephonyNetwork(telephonyNetworkDelegate) : null; + } +} diff --git a/core/java/android/timezone/TimeZoneFinder.java b/core/java/android/timezone/TimeZoneFinder.java new file mode 100644 index 000000000000..15dfe62bb789 --- /dev/null +++ b/core/java/android/timezone/TimeZoneFinder.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.timezone; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; + +import com.android.internal.annotations.GuardedBy; + +/** + * A class that can be used to find time zones. + * + * @hide + */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public final class TimeZoneFinder { + + private static Object sLock = new Object(); + @GuardedBy("sLock") + private static TimeZoneFinder sInstance; + + private final libcore.timezone.TimeZoneFinder mDelegate; + + private TimeZoneFinder(libcore.timezone.TimeZoneFinder delegate) { + mDelegate = delegate; + } + + /** + * Obtains an instance for use when resolving telephony time zone information. This method never + * returns {@code null}. + */ + @NonNull + public static TimeZoneFinder getInstance() { + synchronized (sLock) { + if (sInstance == null) { + sInstance = new TimeZoneFinder(libcore.timezone.TimeZoneFinder.getInstance()); + } + } + return sInstance; + } + + /** + * Returns a {@link CountryTimeZones} object associated with the specified country code. + * Caching is handled as needed. If the country code is not recognized or there is an error + * during lookup this method can return null. + */ + @Nullable + public CountryTimeZones lookupCountryTimeZones(@NonNull String countryIso) { + libcore.timezone.CountryTimeZones delegate = mDelegate.lookupCountryTimeZones(countryIso); + return delegate == null ? null : new CountryTimeZones(delegate); + } +} diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 4368115917e5..178b3c0cb94e 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -19,6 +19,7 @@ package android.view; import static android.Manifest.permission.CONFIGURE_DISPLAY_COLOR_MODE; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; @@ -1010,6 +1011,9 @@ public final class Display { * @return Supported WCG color spaces. * @hide */ + @SuppressLint("VisiblySynchronized") + @NonNull + @TestApi public @ColorMode ColorSpace[] getSupportedWideColorGamut() { synchronized (this) { final ColorSpace[] defaultColorSpaces = new ColorSpace[0]; diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java index c67ff6ea0111..7986ceb988db 100644 --- a/core/java/android/view/InputEventReceiver.java +++ b/core/java/android/view/InputEventReceiver.java @@ -128,6 +128,19 @@ public abstract class InputEventReceiver { } /** + * Called when a focus event is received. + * + * @param hasFocus if true, the window associated with this input channel has just received + * focus + * if false, the window associated with this input channel has just lost focus + * @param inTouchMode if true, the device is in touch mode + * if false, the device is not in touch mode + */ + // Called from native code. + public void onFocusEvent(boolean hasFocus, boolean inTouchMode) { + } + + /** * Called when a batched input event is pending. * * The batched input event will continue to accumulate additional movement @@ -213,8 +226,13 @@ public abstract class InputEventReceiver { onBatchedInputEventPending(); } - public static interface Factory { - public InputEventReceiver createInputEventReceiver( - InputChannel inputChannel, Looper looper); + /** + * Factory for InputEventReceiver + */ + public interface Factory { + /** + * Create a new InputReceiver for a given inputChannel + */ + InputEventReceiver createInputEventReceiver(InputChannel inputChannel, Looper looper); } } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 17825444a524..0de1a4f038ff 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -42,6 +42,7 @@ import android.os.SystemClock; import android.util.AttributeSet; import android.util.Log; import android.view.SurfaceControl.Transaction; +import android.view.accessibility.AccessibilityNodeInfo; import com.android.internal.view.SurfaceCallbackHelper; @@ -201,6 +202,9 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall private SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction(); private int mParentSurfaceGenerationId; + // The token of embedded windowless view hierarchy. + private IBinder mEmbeddedViewHierarchy; + public SurfaceView(Context context) { this(context, null); } @@ -1531,4 +1535,27 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall if (viewRoot == null) return; viewRoot.setUseBLASTSyncTransaction(); } + + /** + * Add the token of embedded view hierarchy. Set {@code null} to clear the embedded view + * hierarchy. + * + * @param token IBinder token. + * @hide + */ + public void setEmbeddedViewHierarchy(IBinder token) { + mEmbeddedViewHierarchy = token; + } + + /** @hide */ + @Override + public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfoInternal(info); + if (mEmbeddedViewHierarchy == null) { + return; + } + // Add a leashed child when this SurfaceView embeds another view hierarchy. Getting this + // leashed child would return the root node in the embedded hierarchy + info.addChild(mEmbeddedViewHierarchy); + } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index e0f6e0668f8b..2ef944f35982 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -8044,6 +8044,11 @@ public final class ViewRootImpl implements ViewParent, } @Override + public void onFocusEvent(boolean hasFocus, boolean inTouchMode) { + windowFocusChanged(hasFocus, inTouchMode); + } + + @Override public void dispose() { unscheduleConsumeBatchedInput(); super.dispose(); diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index 914ff1871d69..b9f08ada3152 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -17,10 +17,14 @@ package android.view.accessibility; import android.accessibilityservice.IAccessibilityServiceConnection; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; +import android.graphics.Bitmap; import android.os.Binder; import android.os.Build; import android.os.Bundle; +import android.os.IBinder; import android.os.Message; import android.os.Process; import android.os.RemoteException; @@ -337,6 +341,48 @@ public final class AccessibilityInteractionClient return emptyWindows; } + + /** + * Finds an {@link AccessibilityNodeInfo} by accessibility id and given leash token instead of + * window id. This method is used to find the leashed node on the embedded view hierarchy. + * + * @param connectionId The id of a connection for interacting with the system. + * @param leashToken The token of the embedded hierarchy. + * @param accessibilityNodeId A unique view id or virtual descendant id from + * where to start the search. Use + * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID} + * to start from the root. + * @param bypassCache Whether to bypass the cache while looking for the node. + * @param prefetchFlags flags to guide prefetching. + * @param arguments Optional action arguments. + * @return An {@link AccessibilityNodeInfo} if found, null otherwise. + */ + public @Nullable AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId( + int connectionId, @NonNull IBinder leashToken, long accessibilityNodeId, + boolean bypassCache, int prefetchFlags, Bundle arguments) { + if (leashToken == null) { + return null; + } + int windowId = -1; + try { + IAccessibilityServiceConnection connection = getConnection(connectionId); + if (connection != null) { + windowId = connection.getWindowIdForLeashToken(leashToken); + } else { + if (DEBUG) { + Log.w(LOG_TAG, "No connection for connection id: " + connectionId); + } + } + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error while calling remote getWindowIdForLeashToken", re); + } + if (windowId == -1) { + return null; + } + return findAccessibilityNodeInfoByAccessibilityId(connectionId, windowId, + accessibilityNodeId, bypassCache, prefetchFlags, arguments); + } + /** * Finds an {@link AccessibilityNodeInfo} by accessibility id. * @@ -783,6 +829,31 @@ public final class AccessibilityInteractionClient } /** + * Takes the screenshot of the specified display and returns it by bitmap format. + * + * @param connectionId The id of a connection for interacting with the system. + * @param displayId The logic display id, use {@link Display#DEFAULT_DISPLAY} for + * default display. + * @return The screenshot bitmap on success, null otherwise. + */ + public Bitmap takeScreenshot(int connectionId, int displayId) { + Bitmap screenShot = null; + try { + IAccessibilityServiceConnection connection = getConnection(connectionId); + if (connection != null) { + screenShot = connection.takeScreenshot(displayId); + } else { + if (DEBUG) { + Log.w(LOG_TAG, "No connection for connection id: " + connectionId); + } + } + } catch (RemoteException re) { + Log.w(LOG_TAG, "Error while calling remote takeScreenshot", re); + } + return screenShot; + } + + /** * Clears the result state. */ private void clearResultLocked() { diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index 2e5a4b57da18..3dfeffbf9e6a 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -1195,6 +1195,19 @@ public final class AccessibilityManager { @TestApi @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) public void performAccessibilityShortcut() { + performAccessibilityShortcut(null); + } + + /** + * Perform the accessibility shortcut for the given target which is assigned to the shortcut. + * + * @param targetName The flattened {@link ComponentName} string or the class name of a system + * class implementing a supported accessibility feature, or {@code null} if there's no + * specified target. + * @hide + */ + @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) + public void performAccessibilityShortcut(@Nullable String targetName) { final IAccessibilityManager service; synchronized (mLock) { service = getServiceLocked(); @@ -1203,7 +1216,7 @@ public final class AccessibilityManager { } } try { - service.performAccessibilityShortcut(); + service.performAccessibilityShortcut(targetName); } catch (RemoteException re) { Log.e(LOG_TAG, "Error performing accessibility shortcut. ", re); } @@ -1270,7 +1283,22 @@ public final class AccessibilityManager { * @param displayId The logical display id. * @hide */ + @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public void notifyAccessibilityButtonClicked(int displayId) { + notifyAccessibilityButtonClicked(displayId, null); + } + + /** + * Perform the accessibility button for the given target which is assigned to the button. + * + * @param displayId displayId The logical display id. + * @param targetName The flattened {@link ComponentName} string or the class name of a system + * class implementing a supported accessibility feature, or {@code null} if there's no + * specified target. + * @hide + */ + @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) + public void notifyAccessibilityButtonClicked(int displayId, @Nullable String targetName) { final IAccessibilityManager service; synchronized (mLock) { service = getServiceLocked(); @@ -1279,7 +1307,7 @@ public final class AccessibilityManager { } } try { - service.notifyAccessibilityButtonClicked(displayId); + service.notifyAccessibilityButtonClicked(displayId, targetName); } catch (RemoteException re) { Log.e(LOG_TAG, "Error while dispatching accessibility button click", re); } diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 92aa7d523da0..184f3302ae8d 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -31,6 +31,7 @@ import android.graphics.Rect; import android.graphics.Region; import android.os.Build; import android.os.Bundle; +import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.text.InputType; @@ -110,6 +111,9 @@ public class AccessibilityNodeInfo implements Parcelable { public static final int ROOT_ITEM_ID = Integer.MAX_VALUE - 1; /** @hide */ + public static final int LEASHED_ITEM_ID = Integer.MAX_VALUE - 2; + + /** @hide */ public static final long UNDEFINED_NODE_ID = makeNodeId(UNDEFINED_ITEM_ID, UNDEFINED_ITEM_ID); /** @hide */ @@ -117,6 +121,10 @@ public class AccessibilityNodeInfo implements Parcelable { AccessibilityNodeProvider.HOST_VIEW_ID); /** @hide */ + public static final long LEASHED_NODE_ID = makeNodeId(LEASHED_ITEM_ID, + AccessibilityNodeProvider.HOST_VIEW_ID); + + /** @hide */ public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001; /** @hide */ @@ -788,6 +796,10 @@ public class AccessibilityNodeInfo implements Parcelable { private TouchDelegateInfo mTouchDelegateInfo; + private IBinder mLeashedChild; + private IBinder mLeashedParent; + private long mLeashedParentNodeId = UNDEFINED_NODE_ID; + /** * Creates a new {@link AccessibilityNodeInfo}. */ @@ -1039,7 +1051,12 @@ public class AccessibilityNodeInfo implements Parcelable { return null; } final long childId = mChildNodeIds.get(index); - AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); + final AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); + if (mLeashedChild != null && childId == LEASHED_NODE_ID) { + return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mLeashedChild, + ROOT_NODE_ID, false, FLAG_PREFETCH_DESCENDANTS, null); + } + return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId, childId, false, FLAG_PREFETCH_DESCENDANTS, null); } @@ -1062,6 +1079,43 @@ public class AccessibilityNodeInfo implements Parcelable { } /** + * Adds a view root from leashed content as a child. This method is used to embedded another + * view hierarchy. + * <p> + * <strong>Note:</strong> Only one leashed child is permitted. + * </p> + * <p> + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * Note that a view cannot be made its own child. + * </p> + * + * @param token The token to which a view root is added. + * + * @throws IllegalStateException If called from an AccessibilityService. + * @hide + */ + @TestApi + public void addChild(@NonNull IBinder token) { + enforceNotSealed(); + if (token == null) { + return; + } + if (mChildNodeIds == null) { + mChildNodeIds = new LongArray(); + } + + mLeashedChild = token; + // Checking uniqueness. + // Since only one leashed child is permitted, skip adding ID if the ID already exists. + if (mChildNodeIds.indexOf(LEASHED_NODE_ID) >= 0) { + return; + } + mChildNodeIds.add(LEASHED_NODE_ID); + } + + /** * Unchecked version of {@link #addChild(View)} that does not verify * uniqueness. For framework use only. * @@ -1090,6 +1144,38 @@ public class AccessibilityNodeInfo implements Parcelable { } /** + * Removes a leashed child. If the child was not previously added to the node, + * calling this method has no effect. + * <p> + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * + * @param token The token of the leashed child + * @return true if the child was present + * + * @throws IllegalStateException If called from an AccessibilityService. + * @hide + */ + public boolean removeChild(IBinder token) { + enforceNotSealed(); + if (mChildNodeIds == null || mLeashedChild == null) { + return false; + } + if (!mLeashedChild.equals(token)) { + return false; + } + final int index = mChildNodeIds.indexOf(LEASHED_NODE_ID); + mLeashedChild = null; + if (index < 0) { + return false; + } + mChildNodeIds.remove(index); + return true; + } + + /** * Adds a virtual child which is a descendant of the given <code>root</code>. * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root * is added as a child. @@ -1668,6 +1754,9 @@ public class AccessibilityNodeInfo implements Parcelable { */ public AccessibilityNodeInfo getParent() { enforceSealed(); + if (mLeashedParent != null && mLeashedParentNodeId != UNDEFINED_NODE_ID) { + return getNodeForAccessibilityId(mConnectionId, mLeashedParent, mLeashedParentNodeId); + } return getNodeForAccessibilityId(mConnectionId, mWindowId, mParentNodeId); } @@ -3257,6 +3346,40 @@ public class AccessibilityNodeInfo implements Parcelable { } /** + * Sets the token and node id of the leashed parent. + * + * @param token The token. + * @param viewId The accessibility view id. + * @hide + */ + @TestApi + public void setLeashedParent(@Nullable IBinder token, int viewId) { + enforceNotSealed(); + mLeashedParent = token; + mLeashedParentNodeId = makeNodeId(viewId, AccessibilityNodeProvider.HOST_VIEW_ID); + } + + /** + * Gets the token of the leashed parent. + * + * @return The token. + * @hide + */ + public @Nullable IBinder getLeashedParent() { + return mLeashedParent; + } + + /** + * Gets the node id of the leashed parent. + * + * @return The accessibility node id. + * @hide + */ + public long getLeashedParentNodeId() { + return mLeashedParentNodeId; + } + + /** * Sets if this instance is sealed. * * @param sealed Whether is sealed. @@ -3559,6 +3682,18 @@ public class AccessibilityNodeInfo implements Parcelable { if (!Objects.equals(mTouchDelegateInfo, DEFAULT.mTouchDelegateInfo)) { nonDefaultFields |= bitAt(fieldIndex); } + fieldIndex++; + if (mLeashedChild != DEFAULT.mLeashedChild) { + nonDefaultFields |= bitAt(fieldIndex); + } + fieldIndex++; + if (mLeashedParent != DEFAULT.mLeashedParent) { + nonDefaultFields |= bitAt(fieldIndex); + } + fieldIndex++; + if (mLeashedParentNodeId != DEFAULT.mLeashedParentNodeId) { + nonDefaultFields |= bitAt(fieldIndex); + } int totalFields = fieldIndex; parcel.writeLong(nonDefaultFields); @@ -3685,6 +3820,16 @@ public class AccessibilityNodeInfo implements Parcelable { mTouchDelegateInfo.writeToParcel(parcel, flags); } + if (isBitSet(nonDefaultFields, fieldIndex++)) { + parcel.writeStrongBinder(mLeashedChild); + } + if (isBitSet(nonDefaultFields, fieldIndex++)) { + parcel.writeStrongBinder(mLeashedParent); + } + if (isBitSet(nonDefaultFields, fieldIndex++)) { + parcel.writeLong(mLeashedParentNodeId); + } + if (DEBUG) { fieldIndex--; if (totalFields != fieldIndex) { @@ -3768,6 +3913,10 @@ public class AccessibilityNodeInfo implements Parcelable { final TouchDelegateInfo otherInfo = other.mTouchDelegateInfo; mTouchDelegateInfo = (otherInfo != null) ? new TouchDelegateInfo(otherInfo.mTargetMap, true) : null; + + mLeashedChild = other.mLeashedChild; + mLeashedParent = other.mLeashedParent; + mLeashedParentNodeId = other.mLeashedParentNodeId; } private void initPoolingInfos(AccessibilityNodeInfo other) { @@ -3921,6 +4070,16 @@ public class AccessibilityNodeInfo implements Parcelable { mTouchDelegateInfo = TouchDelegateInfo.CREATOR.createFromParcel(parcel); } + if (isBitSet(nonDefaultFields, fieldIndex++)) { + mLeashedChild = parcel.readStrongBinder(); + } + if (isBitSet(nonDefaultFields, fieldIndex++)) { + mLeashedParent = parcel.readStrongBinder(); + } + if (isBitSet(nonDefaultFields, fieldIndex++)) { + mLeashedParentNodeId = parcel.readLong(); + } + mSealed = sealed; } @@ -4200,6 +4359,19 @@ public class AccessibilityNodeInfo implements Parcelable { | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS, null); } + private static AccessibilityNodeInfo getNodeForAccessibilityId(int connectionId, + IBinder leashToken, long accessibilityId) { + if (!((leashToken != null) + && (getAccessibilityViewId(accessibilityId) != UNDEFINED_ITEM_ID) + && (connectionId != UNDEFINED_CONNECTION_ID))) { + return null; + } + AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); + return client.findAccessibilityNodeInfoByAccessibilityId(connectionId, + leashToken, accessibilityId, false, FLAG_PREFETCH_PREDECESSORS + | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS, null); + } + /** @hide */ public static String idToString(long accessibilityId) { int accessibilityViewId = getAccessibilityViewId(accessibilityId); diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl index 392db574d988..fcaaa2e74778 100644 --- a/core/java/android/view/accessibility/IAccessibilityManager.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -66,12 +66,12 @@ interface IAccessibilityManager { // Used by UiAutomation IBinder getWindowToken(int windowId, int userId); - void notifyAccessibilityButtonClicked(int displayId); + void notifyAccessibilityButtonClicked(int displayId, String targetName); void notifyAccessibilityButtonVisibilityChanged(boolean available); // Requires Manifest.permission.MANAGE_ACCESSIBILITY - void performAccessibilityShortcut(); + void performAccessibilityShortcut(String targetName); // Requires Manifest.permission.MANAGE_ACCESSIBILITY List<String> getAccessibilityShortcutTargets(int shortcutType); diff --git a/core/java/android/webkit/PacProcessor.java b/core/java/android/webkit/PacProcessor.java new file mode 100644 index 000000000000..fe8bbb8b6c82 --- /dev/null +++ b/core/java/android/webkit/PacProcessor.java @@ -0,0 +1,58 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.webkit; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; + + +/** + * Class to evaluate PAC scripts. + * @hide + */ + +@SystemApi +public interface PacProcessor { + + /** + * Returns the default PacProcessor instance. + * + * @return the default PacProcessor instance. + */ + @NonNull + static PacProcessor getInstance() { + return WebViewFactory.getProvider().getPacProcessor(); + } + + /** + * Set PAC script to use. + * + * @param script PAC script. + * @return true if PAC script is successfully set. + */ + boolean setProxyScript(@NonNull String script); + + /** + * Gets a list of proxy servers to use. + * @param url The URL being accessed. + * @return a PAC-style semicolon-separated list of valid proxy servers. + * For example: "PROXY xxx.xxx.xxx.xxx:xx; SOCKS yyy.yyy.yyy:yy". + */ + @Nullable + String makeProxyRequest(@NonNull String url); +} diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java index 6a1ed39e25b3..f7c3ec09dd67 100644 --- a/core/java/android/webkit/WebViewFactoryProvider.java +++ b/core/java/android/webkit/WebViewFactoryProvider.java @@ -175,6 +175,15 @@ public interface WebViewFactoryProvider { WebViewDatabase getWebViewDatabase(Context context); /** + * Gets the singleton PacProcessor instance. + * @return the PacProcessor instance + */ + @NonNull + default PacProcessor getPacProcessor() { + throw new UnsupportedOperationException("Not implemented"); + } + + /** * Gets the classloader used to load internal WebView implementation classes. This interface * should only be used by the WebView Support Library. */ diff --git a/core/java/android/widget/EditorTouchState.java b/core/java/android/widget/EditorTouchState.java index 6277afe2f613..b13ca4210612 100644 --- a/core/java/android/widget/EditorTouchState.java +++ b/core/java/android/widget/EditorTouchState.java @@ -173,6 +173,13 @@ public class EditorTouchState { mIsDragCloseToVertical = (4 * deltaXSquared) <= distanceSquared; } } + } else if (action == MotionEvent.ACTION_CANCEL) { + mLastDownMillis = 0; + mLastUpMillis = 0; + mMultiTapStatus = MultiTapStatus.NONE; + mMultiTapInSameArea = false; + mMovedEnoughForDrag = false; + mIsDragCloseToVertical = false; } } diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java index 5731e502ae45..85654931a975 100644 --- a/core/java/android/widget/TextClock.java +++ b/core/java/android/widget/TextClock.java @@ -422,7 +422,7 @@ public class TextClock extends TextView { /** * Update the displayed time if necessary and invalidate the view. */ - public void refresh() { + public void refreshTime() { onTimeChanged(); invalidate(); } diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index 9bdb4c10067c..d119b2e1992b 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -16,6 +16,8 @@ package android.widget; +import static com.android.internal.util.Preconditions.checkNotNull; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -42,8 +44,12 @@ import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; +import com.android.internal.annotations.GuardedBy; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; /** * A toast is a view containing a quick little message for the user. The toast class @@ -262,6 +268,29 @@ public class Toast { } /** + * Adds a callback to be notified when the toast is shown or hidden. + * + * Note that if the toast is blocked for some reason you won't get a call back. + * + * @see #removeCallback(Callback) + */ + public void addCallback(@NonNull Callback callback) { + checkNotNull(callback); + synchronized (mTN.mCallbacks) { + mTN.mCallbacks.add(callback); + } + } + + /** + * Removes a callback previously added with {@link #addCallback(Callback)}. + */ + public void removeCallback(@NonNull Callback callback) { + synchronized (mTN.mCallbacks) { + mTN.mCallbacks.remove(callback); + } + } + + /** * Gets the LayoutParams for the Toast window. * @hide */ @@ -389,6 +418,9 @@ public class Toast { String mPackageName; + @GuardedBy("mCallbacks") + private final List<Callback> mCallbacks = new ArrayList<>(); + static final long SHORT_DURATION_TIMEOUT = 4000; static final long LONG_DURATION_TIMEOUT = 7000; @@ -449,6 +481,12 @@ public class Toast { }; } + private List<Callback> getCallbacks() { + synchronized (mCallbacks) { + return new ArrayList<>(mCallbacks); + } + } + /** * schedule handleShow into the right thread */ @@ -522,6 +560,9 @@ public class Toast { try { mWM.addView(mView, mParams); trySendAccessibilityEvent(); + for (Callback callback : getCallbacks()) { + callback.onToastShown(); + } } catch (WindowManager.BadTokenException e) { /* ignore */ } @@ -564,8 +605,30 @@ public class Toast { } catch (RemoteException e) { } + for (Callback callback : getCallbacks()) { + callback.onToastHidden(); + } mView = null; } } } + + /** + * Callback object to be called when the toast is shown or hidden. + * + * Callback methods will be called on the looper thread provided on construction. + * + * @see #addCallback(Callback) + */ + public abstract static class Callback { + /** + * Called when the toast is displayed on the screen. + */ + public void onToastShown() {} + + /** + * Called when the toast is hidden. + */ + public void onToastHidden() {} + } } diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java index de204badfd0d..b6703168264e 100644 --- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java +++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java @@ -19,6 +19,15 @@ import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTT import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; import static android.view.accessibility.AccessibilityManager.ShortcutType; +import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME; +import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME; +import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; +import static com.android.internal.app.AccessibilityButtonChooserActivity.WhiteListingFeatureElementIndex.COMPONENT_ID; +import static com.android.internal.app.AccessibilityButtonChooserActivity.WhiteListingFeatureElementIndex.FRAGMENT_TYPE; +import static com.android.internal.app.AccessibilityButtonChooserActivity.WhiteListingFeatureElementIndex.ICON_ID; +import static com.android.internal.app.AccessibilityButtonChooserActivity.WhiteListingFeatureElementIndex.LABEL_ID; +import static com.android.internal.app.AccessibilityButtonChooserActivity.WhiteListingFeatureElementIndex.SETTINGS_KEY; + import android.accessibilityservice.AccessibilityServiceInfo; import android.annotation.IntDef; import android.annotation.NonNull; @@ -63,10 +72,6 @@ import java.util.StringJoiner; * Activity used to display and persist a service or feature target for the Accessibility button. */ public class AccessibilityButtonChooserActivity extends Activity { - - private static final String MAGNIFICATION_COMPONENT_ID = - "com.android.server.accessibility.MagnificationController"; - private static final char SERVICES_SEPARATOR = ':'; private static final TextUtils.SimpleStringSplitter sStringColonSplitter = new TextUtils.SimpleStringSplitter(SERVICES_SEPARATOR); @@ -76,7 +81,7 @@ public class AccessibilityButtonChooserActivity extends Activity { ACCESSIBILITY_SHORTCUT_KEY); private int mShortcutType; - private List<AccessibilityButtonTarget> mTargets = new ArrayList<>(); + private final List<AccessibilityButtonTarget> mTargets = new ArrayList<>(); private AlertDialog mAlertDialog; private TargetAdapter mTargetAdapter; @@ -99,7 +104,7 @@ public class AccessibilityButtonChooserActivity extends Activity { UserShortcutType.TRIPLETAP, }) /** Denotes the user shortcut type. */ - public @interface UserShortcutType { + private @interface UserShortcutType { int DEFAULT = 0; int SOFTWARE = 1; // 1 << 0 int HARDWARE = 2; // 1 << 1 @@ -122,7 +127,7 @@ public class AccessibilityButtonChooserActivity extends Activity { AccessibilityServiceFragmentType.INTUITIVE, AccessibilityServiceFragmentType.BOUNCE, }) - public @interface AccessibilityServiceFragmentType { + private @interface AccessibilityServiceFragmentType { int LEGACY = 0; int INVISIBLE = 1; int INTUITIVE = 2; @@ -140,11 +145,61 @@ public class AccessibilityButtonChooserActivity extends Activity { ShortcutMenuMode.LAUNCH, ShortcutMenuMode.EDIT, }) - public @interface ShortcutMenuMode { + private @interface ShortcutMenuMode { int LAUNCH = 0; int EDIT = 1; } + /** + * Annotation for align the element index of white listing feature + * {@code WHITE_LISTING_FEATURES}. + * + * {@code COMPONENT_ID} is to get the service component name. + * {@code LABEL_ID} is to get the service label text. + * {@code ICON_ID} is to get the service icon. + * {@code FRAGMENT_TYPE} is to get the service fragment type. + * {@code SETTINGS_KEY} is to get the service settings key. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + WhiteListingFeatureElementIndex.COMPONENT_ID, + WhiteListingFeatureElementIndex.LABEL_ID, + WhiteListingFeatureElementIndex.ICON_ID, + WhiteListingFeatureElementIndex.FRAGMENT_TYPE, + WhiteListingFeatureElementIndex.SETTINGS_KEY, + }) + @interface WhiteListingFeatureElementIndex { + int COMPONENT_ID = 0; + int LABEL_ID = 1; + int ICON_ID = 2; + int FRAGMENT_TYPE = 3; + int SETTINGS_KEY = 4; + } + + private static final String[][] WHITE_LISTING_FEATURES = { + { + COLOR_INVERSION_COMPONENT_NAME.flattenToString(), + String.valueOf(R.string.color_inversion_feature_name), + String.valueOf(R.drawable.ic_accessibility_color_inversion), + String.valueOf(AccessibilityServiceFragmentType.INTUITIVE), + Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, + }, + { + DALTONIZER_COMPONENT_NAME.flattenToString(), + String.valueOf(R.string.color_correction_feature_name), + String.valueOf(R.drawable.ic_accessibility_color_correction), + String.valueOf(AccessibilityServiceFragmentType.INTUITIVE), + Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, + }, + { + MAGNIFICATION_CONTROLLER_NAME, + String.valueOf(R.string.accessibility_magnification_chooser_text), + String.valueOf(R.drawable.ic_accessibility_magnification), + String.valueOf(AccessibilityServiceFragmentType.INVISIBLE), + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, + }, + }; + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -199,6 +254,20 @@ public class AccessibilityButtonChooserActivity extends Activity { private static List<AccessibilityButtonTarget> getServiceTargets(@NonNull Context context, @ShortcutType int shortcutType) { + final List<AccessibilityButtonTarget> targets = new ArrayList<>(); + targets.addAll(getAccessibilityServiceTargets(context)); + targets.addAll(getWhiteListingServiceTargets(context)); + + final AccessibilityManager ams = (AccessibilityManager) context.getSystemService( + Context.ACCESSIBILITY_SERVICE); + final List<String> requiredTargets = ams.getAccessibilityShortcutTargets(shortcutType); + targets.removeIf(target -> !requiredTargets.contains(target.getId())); + + return targets; + } + + private static List<AccessibilityButtonTarget> getAccessibilityServiceTargets( + @NonNull Context context) { final AccessibilityManager ams = (AccessibilityManager) context.getSystemService( Context.ACCESSIBILITY_SERVICE); final List<AccessibilityServiceInfo> installedServices = @@ -209,29 +278,73 @@ public class AccessibilityButtonChooserActivity extends Activity { final List<AccessibilityButtonTarget> targets = new ArrayList<>(installedServices.size()); for (AccessibilityServiceInfo info : installedServices) { - if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) { - targets.add(new AccessibilityButtonTarget(context, info)); - } + targets.add(new AccessibilityButtonTarget(context, info)); } - final List<String> requiredTargets = ams.getAccessibilityShortcutTargets(shortcutType); - targets.removeIf(target -> !requiredTargets.contains(target.getId())); + return targets; + } - // TODO(b/146815874): Will replace it with white list services. - if (Settings.Secure.getInt(context.getContentResolver(), - Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 0) == 1) { - final AccessibilityButtonTarget magnificationTarget = new AccessibilityButtonTarget( + private static List<AccessibilityButtonTarget> getWhiteListingServiceTargets( + @NonNull Context context) { + final List<AccessibilityButtonTarget> targets = new ArrayList<>(); + + for (int i = 0; i < WHITE_LISTING_FEATURES.length; i++) { + final AccessibilityButtonTarget target = new AccessibilityButtonTarget( context, - MAGNIFICATION_COMPONENT_ID, - R.string.accessibility_magnification_chooser_text, - R.drawable.ic_accessibility_magnification, - AccessibilityServiceFragmentType.INTUITIVE); - targets.add(magnificationTarget); + WHITE_LISTING_FEATURES[i][COMPONENT_ID], + Integer.parseInt(WHITE_LISTING_FEATURES[i][LABEL_ID]), + Integer.parseInt(WHITE_LISTING_FEATURES[i][ICON_ID]), + Integer.parseInt(WHITE_LISTING_FEATURES[i][FRAGMENT_TYPE])); + targets.add(target); } return targets; } + private static boolean isWhiteListingServiceEnabled(@NonNull Context context, + AccessibilityButtonTarget target) { + + for (int i = 0; i < WHITE_LISTING_FEATURES.length; i++) { + if (WHITE_LISTING_FEATURES[i][COMPONENT_ID].equals(target.getId())) { + return Settings.Secure.getInt(context.getContentResolver(), + WHITE_LISTING_FEATURES[i][SETTINGS_KEY], + /* settingsValueOff */ 0) == /* settingsValueOn */ 1; + } + } + + return false; + } + + private static boolean isWhiteListingService(String componentId) { + for (int i = 0; i < WHITE_LISTING_FEATURES.length; i++) { + if (WHITE_LISTING_FEATURES[i][COMPONENT_ID].equals(componentId)) { + return true; + } + } + + return false; + } + + private void disableWhiteListingService(String componentId) { + for (int i = 0; i < WHITE_LISTING_FEATURES.length; i++) { + if (WHITE_LISTING_FEATURES[i][COMPONENT_ID].equals(componentId)) { + Settings.Secure.putInt(getContentResolver(), + WHITE_LISTING_FEATURES[i][SETTINGS_KEY], /* settingsValueOn */ 1); + return; + } + } + } + + private void disableService(ComponentName componentName) { + final String componentId = componentName.flattenToString(); + + if (isWhiteListingService(componentId)) { + disableWhiteListingService(componentName.flattenToString()); + } else { + setAccessibilityServiceState(this, componentName, /* enabled= */ false); + } + } + private static class ViewHolder { ImageView mIconView; TextView mLabelView; @@ -350,11 +463,14 @@ public class AccessibilityButtonChooserActivity extends Activity { private void updateIntuitiveActionItemVisibility(@NonNull Context context, @NonNull ViewHolder holder, AccessibilityButtonTarget target) { final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT); + final boolean isServiceEnabled = isWhiteListingService(target.getId()) + ? isWhiteListingServiceEnabled(context, target) + : isAccessibilityServiceEnabled(context, target); holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item)); holder.mViewItem.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE); holder.mSwitchItem.setVisibility(isEditMenuMode ? View.GONE : View.VISIBLE); - holder.mSwitchItem.setChecked(!isEditMenuMode && isServiceEnabled(context, target)); + holder.mSwitchItem.setChecked(!isEditMenuMode && isServiceEnabled); holder.mItemContainer.setVisibility(View.VISIBLE); } @@ -411,7 +527,7 @@ public class AccessibilityButtonChooserActivity extends Activity { } } - private static boolean isServiceEnabled(@NonNull Context context, + private static boolean isAccessibilityServiceEnabled(@NonNull Context context, AccessibilityButtonTarget target) { final AccessibilityManager ams = (AccessibilityManager) context.getSystemService( Context.ACCESSIBILITY_SERVICE); @@ -470,14 +586,14 @@ public class AccessibilityButtonChooserActivity extends Activity { if (!hasValueInSettings(this, ACCESSIBILITY_SHORTCUT_KEY_USER_TYPE, componentName)) { - setAccessibilityServiceState(this, componentName, /* enabled= */ false); + disableService(componentName); } } else if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) { optOutValueFromSettings(this, ACCESSIBILITY_SHORTCUT_KEY_USER_TYPE, componentName); if (!hasValueInSettings(this, ACCESSIBILITY_BUTTON_USER_TYPE, componentName)) { - setAccessibilityServiceState(this, componentName, /* enabled= */ false); + disableService(componentName); } } else { throw new IllegalArgumentException("Unsupported shortcut type:" + mShortcutType); diff --git a/core/java/com/android/internal/app/BlockedAppActivity.java b/core/java/com/android/internal/app/BlockedAppActivity.java new file mode 100644 index 000000000000..fbdbbfb06b78 --- /dev/null +++ b/core/java/com/android/internal/app/BlockedAppActivity.java @@ -0,0 +1,86 @@ +/* + * 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.internal.app; + +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Slog; + +import com.android.internal.R; + +/** + * A dialog shown to the user when they try to launch an app that is not allowed in lock task + * mode. The intent to start this activity must be created with the static factory method provided + * below. + */ +public class BlockedAppActivity extends AlertActivity { + + private static final String TAG = "BlockedAppActivity"; + private static final String PACKAGE_NAME = "com.android.internal.app"; + private static final String EXTRA_BLOCKED_PACKAGE = PACKAGE_NAME + ".extra.BLOCKED_PACKAGE"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Intent intent = getIntent(); + int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, /* defaultValue= */ -1); + if (userId < 0) { + Slog.wtf(TAG, "Invalid user: " + userId); + finish(); + return; + } + + String packageName = intent.getStringExtra(EXTRA_BLOCKED_PACKAGE); + if (TextUtils.isEmpty(packageName)) { + Slog.wtf(TAG, "Invalid package: " + packageName); + finish(); + return; + } + + CharSequence appLabel = getAppLabel(userId, packageName); + + mAlertParams.mTitle = getString(R.string.app_blocked_title); + mAlertParams.mMessage = getString(R.string.app_blocked_message, appLabel); + mAlertParams.mPositiveButtonText = getString(android.R.string.ok); + setupAlert(); + } + + private CharSequence getAppLabel(int userId, String packageName) { + PackageManager pm = getPackageManager(); + try { + ApplicationInfo aInfo = + pm.getApplicationInfoAsUser(packageName, /* flags= */ 0, userId); + return aInfo.loadLabel(pm); + } catch (PackageManager.NameNotFoundException ne) { + Slog.e(TAG, "Package " + packageName + " not found", ne); + } + return packageName; + } + + + /** Creates an intent that launches {@link BlockedAppActivity}. */ + public static Intent createIntent(int userId, String packageName) { + return new Intent() + .setClassName("android", BlockedAppActivity.class.getName()) + .putExtra(Intent.EXTRA_USER_ID, userId) + .putExtra(EXTRA_BLOCKED_PACKAGE, packageName); + } +} diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp index 59dab0c82162..649e5d2f49fe 100644 --- a/core/jni/android_view_InputEventReceiver.cpp +++ b/core/jni/android_view_InputEventReceiver.cpp @@ -40,10 +40,15 @@ namespace android { static const bool kDebugDispatchCycle = false; +static const char* toString(bool value) { + return value ? "true" : "false"; +} + static struct { jclass clazz; jmethodID dispatchInputEvent; + jmethodID onFocusEvent; jmethodID dispatchBatchedInputEventPending; } gInputEventReceiverClassInfo; @@ -219,8 +224,7 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) { if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%" PRId64, - getInputChannelName().c_str(), - consumeBatches ? "true" : "false", frameTime); + getInputChannelName().c_str(), toString(consumeBatches), frameTime); } if (consumeBatches) { @@ -235,6 +239,7 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, for (;;) { uint32_t seq; InputEvent* inputEvent; + status_t status = mInputConsumer.consume(&mInputEventFactory, consumeBatches, frameTime, &seq, &inputEvent); if (status) { @@ -302,6 +307,19 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent); break; } + case AINPUT_EVENT_TYPE_FOCUS: { + FocusEvent* focusEvent = static_cast<FocusEvent*>(inputEvent); + if (kDebugDispatchCycle) { + ALOGD("channel '%s' ~ Received focus event: hasFocus=%s, inTouchMode=%s.", + getInputChannelName().c_str(), toString(focusEvent->getHasFocus()), + toString(focusEvent->getInTouchMode())); + } + env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.onFocusEvent, + jboolean(focusEvent->getHasFocus()), + jboolean(focusEvent->getInTouchMode())); + finishInputEvent(seq, true /* handled */); + return OK; + } default: assert(false); // InputConsumer should prevent this from ever happening @@ -421,6 +439,8 @@ int register_android_view_InputEventReceiver(JNIEnv* env) { gInputEventReceiverClassInfo.dispatchInputEvent = GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "dispatchInputEvent", "(ILandroid/view/InputEvent;)V"); + gInputEventReceiverClassInfo.onFocusEvent = + GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onFocusEvent", "(ZZ)V"); gInputEventReceiverClassInfo.dispatchBatchedInputEventPending = GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "dispatchBatchedInputEventPending", "()V"); diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 673772a52212..39ea45a6557e 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -1431,7 +1431,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, // Isolated process / webview / app zygote should be gated by SELinux and file permission // so they can't even traverse CE / DE directories. if (pkg_data_info_list != nullptr - && GetBoolProperty(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, false)) { + && GetBoolProperty(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true)) { isolateAppData(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn); } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 7ecd9b702e53..2665e8a8dd8b 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4976,6 +4976,12 @@ android:process=":ui"> </activity> + <activity android:name="com.android.internal.app.BlockedAppActivity" + android:theme="@style/Theme.Dialog.Confirmation" + android:excludeFromRecents="true" + android:process=":ui"> + </activity> + <activity android:name="com.android.settings.notification.NotificationAccessConfirmationActivity" android:theme="@style/Theme.Dialog.Confirmation" android:excludeFromRecents="true"> diff --git a/core/res/res/drawable/ic_accessibility_color_correction.xml b/core/res/res/drawable/ic_accessibility_color_correction.xml new file mode 100644 index 000000000000..02fa4b807155 --- /dev/null +++ b/core/res/res/drawable/ic_accessibility_color_correction.xml @@ -0,0 +1,37 @@ +<vector android:height="24dp" android:viewportHeight="192" + android:viewportWidth="192" android:width="24dp" + xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android"> + <path android:fillColor="#00BCD4" android:pathData="M37.14,173.74L8.61,83.77c-1.72,-5.75 0.26,-11.97 4.97,-15.63L87.15,11c5.2,-4.04 12.46,-3.99 17.61,0.12l73.81,58.94c4.63,3.7 6.53,9.88 4.8,15.57l-28.48,88.18c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84C44.53,184 38.97,179.83 37.14,173.74z"/> + <path android:fillAlpha="0.2" android:fillColor="#FFFFFF" + android:pathData="M13.58,69.14L87.15,12c5.2,-4.04 12.46,-3.99 17.61,0.12l73.81,58.94c3.36,2.68 5.27,6.67 5.41,10.82c0.15,-4.51 -1.79,-8.93 -5.41,-11.82l-73.81,-58.94C99.61,7.01 92.35,6.96 87.15,11L13.58,68.14c-3.71,2.88 -5.72,7.36 -5.56,11.94C8.17,75.85 10.14,71.81 13.58,69.14z" android:strokeAlpha="0.2"/> + <path android:fillAlpha="0.2" android:fillColor="#263238" + android:pathData="M183.35,84.63l-28.48,88.18c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84c-6.31,0 -11.87,-4.17 -13.7,-10.26L8.61,82.77c-0.36,-1.22 -0.55,-2.46 -0.59,-3.69c-0.06,1.56 0.13,3.14 0.59,4.69l28.53,89.97c1.83,6.09 7.39,10.26 13.7,10.26h90.37c6.28,0 11.82,-4.13 13.67,-10.19l28.48,-88.18c0.48,-1.57 0.67,-3.17 0.61,-4.75C183.92,82.13 183.73,83.39 183.35,84.63z" android:strokeAlpha="0.2"/> + <path android:pathData="M60.01,135L60.01,135H60l48.04,49h33.17c6.28,0 11.82,-4.13 13.67,-10.19l18.68,-57.84L129.51,72.2l-0.01,0c0.27,0.27 0.52,0.5 0.77,0.77l0.55,0.55c1.57,1.56 1.57,4.08 -0.04,5.68l-12.56,12.49l6.36,6.3l1.38,1.37l-5.67,5.64l-5.71,-5.68L79.15,135H60.42H60.01z"> + <aapt:attr name="android:fillColor"> + <gradient android:endX="155.9627" android:endY="165.1493" + android:startX="98.4649" android:startY="107.6516" android:type="linear"> + <item android:color="#19263238" android:offset="0"/> + <item android:color="#00212121" android:offset="1"/> + </gradient> + </aapt:attr> + </path> + <path android:fillColor="#0097A7" android:pathData="M68.55,120.35l32.173,-32.173l7.658,7.658l-32.173,32.173z"/> + <path android:fillColor="#FFFFFF" android:pathData="M130.83,73.52l-9.42,-9.36c-1.57,-1.56 -4.1,-1.56 -5.67,0l-12.56,12.48L95.42,69l-5.67,5.64l5.71,5.68L60,116.97V135h19.15l35.42,-35.68l5.71,5.68l5.67,-5.64l-7.73,-7.68l12.56,-12.48C132.4,77.6 132.4,75.08 130.83,73.52zM74.98,126.77l-6.43,-6.43l32.17,-32.17l6.43,6.43L74.98,126.77z"/> + <path android:fillAlpha="0.1" android:fillColor="#263238" + android:pathData="M120.28,105l-5.71,-5.68l-35.42,35.68l-19.15,0l1,1l19.15,0l35.42,-35.68l5.71,5.68l5.68,-5.64l-1,-1z" android:strokeAlpha="0.1"/> + <path android:fillAlpha="0.1" android:fillColor="#263238" + android:pathData="M90.75,75.64l-0.01,0l4.71,4.68l0.01,0z" android:strokeAlpha="0.1"/> + <path android:fillAlpha="0.1" android:fillColor="#263238" + android:pathData="M131.83,74.52l-0.97,-0.97c1.54,1.56 1.53,4.06 -0.07,5.65l-12.56,12.48l1,1l12.55,-12.48C133.4,78.6 133.4,76.08 131.83,74.52z" android:strokeAlpha="0.1"/> + <path android:fillAlpha="0.1" android:fillColor="#263238" + android:pathData="M101.72,89.17l6.67,6.66l0,0l-7.67,-7.66l-32.17,32.17l1,1z" android:strokeAlpha="0.1"/> + <path android:pathData="M37.13,173.7L8.62,83.69c-1.7,-5.8 0.3,-12 5,-15.6l73.52,-57.1c5.2,-4 12.5,-4 17.6,0.1l73.82,58.9c4.6,3.7 6.5,9.9 4.8,15.6l-28.51,88.21c-1.8,6.1 -7.4,10.2 -13.7,10.2H50.83C44.53,184 38.93,179.8 37.13,173.7z"> + <aapt:attr name="android:fillColor"> + <gradient android:centerX="21.977" android:centerY="23.8809" + android:gradientRadius="158.0384" android:type="radial"> + <item android:color="#19FFFFFF" android:offset="0"/> + <item android:color="#00FFFFFF" android:offset="1"/> + </gradient> + </aapt:attr> + </path> +</vector> diff --git a/core/res/res/drawable/ic_accessibility_color_inversion.xml b/core/res/res/drawable/ic_accessibility_color_inversion.xml new file mode 100644 index 000000000000..97b30b0df4d5 --- /dev/null +++ b/core/res/res/drawable/ic_accessibility_color_inversion.xml @@ -0,0 +1,47 @@ +<vector android:height="24dp" android:viewportHeight="192" + android:viewportWidth="192" android:width="24dp" + xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android"> + <path android:fillColor="#546E7A" android:pathData="M37.14,173.74L8.61,83.77c-1.72,-5.75 0.26,-11.97 4.97,-15.63L87.15,11c5.2,-4.04 12.46,-3.99 17.61,0.12l73.81,58.94c4.63,3.7 6.53,9.88 4.8,15.57l-28.48,88.18c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84C44.53,184 38.97,179.83 37.14,173.74z"/> + <path android:fillAlpha="0.2" android:fillColor="#263238" + android:pathData="M183.37,84.63l-28.48,88.18c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84c-6.31,0 -11.87,-4.17 -13.7,-10.26L8.61,82.77c-0.36,-1.22 -0.55,-2.46 -0.59,-3.69c-0.06,1.56 0.13,3.14 0.59,4.69l28.53,89.97c1.83,6.09 7.39,10.26 13.7,10.26h90.38c6.28,0 11.82,-4.13 13.67,-10.19l28.48,-88.18c0.48,-1.57 0.67,-3.17 0.61,-4.75C183.94,82.13 183.74,83.39 183.37,84.63z" android:strokeAlpha="0.2"/> + <path android:fillAlpha="0.2" android:fillColor="#FFFFFF" + android:pathData="M13.58,69.14L87.15,12c5.2,-4.04 12.46,-3.99 17.61,0.12l73.81,58.94c3.36,2.68 5.27,6.67 5.41,10.82c0.15,-4.51 -1.79,-8.93 -5.41,-11.82l-73.81,-58.94C99.61,7.01 92.35,6.96 87.15,11L13.58,68.14c-3.71,2.88 -5.72,7.36 -5.56,11.94C8.17,75.85 10.14,71.81 13.58,69.14z" android:strokeAlpha="0.2"/> + <path android:fillAlpha="0.2" android:fillColor="#FFFFFF" + android:pathData="M53,130.05l5.03,-4.79L44.16,112c-0.05,0.78 -0.14,1.52 -0.15,2.34C43.62,136.61 61.27,154.56 84,156v-5.31C70.13,149.64 58.56,141.54 53,130.05z" android:strokeAlpha="0.2"/> + <path android:fillAlpha="0.2" android:fillColor="#FFFFFF" + android:pathData="M109,52v5.31c13.65,1.05 24.67,9.15 30.11,20.64l-4.92,4.79L147.81,96c0.09,-0.78 0.17,-1.53 0.19,-2.34C148.38,71.39 131.36,53.44 109,52z" android:strokeAlpha="0.2"/> + <path android:pathData="M154.89,173.81l13.57,-42.02l-46.53,-46.56C125.75,90.5 128,96.98 128,104c0,17.7 -14.3,32 -32,32c-8.64,0 -16.47,-3.42 -22.22,-8.97l0.9,0.91L130.73,184h10.49C147.5,184 153.04,179.87 154.89,173.81z"> + <aapt:attr name="android:fillColor"> + <gradient android:endX="153.3523" android:endY="161.6371" + android:startX="90.6075" android:startY="98.8923" android:type="linear"> + <item android:color="#33263238" android:offset="0"/> + <item android:color="#05263238" android:offset="1"/> + </gradient> + </aapt:attr> + </path> + <path android:pathData="M96,129.6V78.4c-14.11,0 -25.6,11.49 -25.6,25.6S81.89,129.6 96,129.6z"> + <aapt:attr name="android:fillColor"> + <gradient android:endX="150.8492" android:endY="164.1401" + android:startX="88.1044" android:startY="101.3954" android:type="linear"> + <item android:color="#33263238" android:offset="0"/> + <item android:color="#05263238" android:offset="1"/> + </gradient> + </aapt:attr> + </path> + <path android:fillAlpha="0.2" android:fillColor="#263238" + android:pathData="M96,136c-17.53,0 -31.72,-14.04 -31.99,-31.5c0,0.17 -0.01,0.33 -0.01,0.5c0,17.7 14.3,32 32,32s32,-14.3 32,-32c0,-0.17 -0.01,-0.33 -0.01,-0.5C127.72,121.96 113.53,136 96,136z" android:strokeAlpha="0.2"/> + <path android:fillAlpha="0.2" android:fillColor="#263238" + android:pathData="M70.4,104c0,0.17 0.01,0.33 0.01,0.5C70.68,90.62 82.06,79.4 96,79.4v-1C81.89,78.4 70.4,89.88 70.4,104z" android:strokeAlpha="0.2"/> + <path android:fillAlpha="0.2" android:fillColor="#FFFFFF" + android:pathData="M96,72c-17.7,0 -32,14.3 -32,32s14.3,32 32,32s32,-14.3 32,-32S113.7,72 96,72zM70.4,104c0,-14.11 11.49,-25.6 25.6,-25.6v51.2C81.89,129.6 70.4,118.11 70.4,104z" android:strokeAlpha="0.2"/> + <path android:fillColor="#FFFFFF" android:pathData="M96,72c-17.7,0 -32,14.3 -32,32s14.3,32 32,32s32,-14.3 32,-32S113.7,72 96,72zM70.4,104c0,-14.11 11.49,-25.6 25.6,-25.6v51.2C81.89,129.6 70.4,118.11 70.4,104z"/> + <path android:pathData="M37.14,173.74L8.61,83.77c-1.72,-5.75 0.26,-11.97 4.97,-15.63L87.15,11c5.2,-4.04 12.46,-3.99 17.61,0.12l73.81,58.94c4.63,3.7 6.53,9.88 4.8,15.57l-28.48,88.18c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84C44.53,184 38.97,179.83 37.14,173.74z"> + <aapt:attr name="android:fillColor"> + <gradient android:endX="156.2451" android:endY="171.4516" + android:startX="37.0633" android:startY="52.269802" android:type="linear"> + <item android:color="#19FFFFFF" android:offset="0"/> + <item android:color="#00FFFFFF" android:offset="1"/> + </gradient> + </aapt:attr> + </path> +</vector> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 9c08728b559e..44754157c5b5 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3752,6 +3752,8 @@ </p> --> <attr name="canRequestFingerprintGestures" format="boolean" /> + <!-- Attribute whether the accessibility service wants to be able to take screenshot. --> + <attr name="canTakeScreenshot" format="boolean" /> <!-- Animated image of the accessibility service purpose or behavior, to help users understand how the service can help them.--> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 6cf6a6828237..98608300e17f 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3007,6 +3007,7 @@ <public name="featureId" /> <public name="supportsInlineSuggestions" /> <public name="crossProfile" /> + <public name="canTakeScreenshot"/> </public-group> <public-group type="drawable" first-id="0x010800b5"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index a0e40646ead9..f977ea857fc8 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -794,6 +794,11 @@ <string name="capability_desc_canCaptureFingerprintGestures">Can capture gestures performed on the device\'s fingerprint sensor.</string> + <!-- Title for the capability of an accessibility service to take screenshot. [CHAR LIMIT=32] --> + <string name="capability_title_canTakeScreenshot">Take screenshot</string> + <!-- Description for the capability of an accessibility service to take screenshot. [CHAR LIMIT=NONE] --> + <string name="capability_desc_canTakeScreenshot">Can take a screenshot of the display.</string> + <!-- Permissions --> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> @@ -4922,6 +4927,13 @@ <!-- Title for button to turn on work profile. [CHAR LIMIT=NONE] --> <string name="work_mode_turn_on">Turn on</string> + <!-- Title of the dialog that is shown when the user tries to launch a suspended application [CHAR LIMIT=50] --> + <string name="app_blocked_title">App is not available</string> + <!-- Default message shown in the dialog that is shown when the user tries to launch a suspended application [CHAR LIMIT=NONE] --> + <string name="app_blocked_message"> + <xliff:g id="app_name" example="Gmail">%1$s</xliff:g> is not available right now. + </string> + <!-- Message displayed in dialog when app is too old to run on this verison of android. [CHAR LIMIT=NONE] --> <string name="deprecated_target_sdk_message">This app was built for an older version of Android and may not work properly. Try checking for updates, or contact the developer.</string> <!-- Title for button to see application detail in app store which it came from - it may allow user to update to newer version. [CHAR LIMIT=50] --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index f01c2e8e2f48..30dbfc711932 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3036,6 +3036,9 @@ <java-symbol type="string" name="app_suspended_more_details" /> <java-symbol type="string" name="app_suspended_default_message" /> + <java-symbol type="string" name="app_blocked_title" /> + <java-symbol type="string" name="app_blocked_message" /> + <!-- Used internally for assistant to launch activity transitions --> <java-symbol type="id" name="cross_task_transition" /> @@ -3214,6 +3217,8 @@ <java-symbol type="string" name="edit_accessibility_shortcut_menu_button" /> <java-symbol type="string" name="cancel_accessibility_shortcut_menu_button" /> + <java-symbol type="drawable" name="ic_accessibility_color_inversion" /> + <java-symbol type="drawable" name="ic_accessibility_color_correction" /> <java-symbol type="drawable" name="ic_accessibility_magnification" /> <java-symbol type="drawable" name="ic_delete_item" /> @@ -3806,4 +3811,9 @@ <java-symbol type="dimen" name="waterfall_display_top_edge_size" /> <java-symbol type="dimen" name="waterfall_display_right_edge_size" /> <java-symbol type="dimen" name="waterfall_display_bottom_edge_size" /> + + <!-- Accessibility take screenshot --> + <java-symbol type="string" name="capability_desc_canTakeScreenshot" /> + <java-symbol type="string" name="capability_title_canTakeScreenshot" /> + </resources> diff --git a/core/tests/coretests/src/android/app/appsearch/AppSearchSchemaTest.java b/core/tests/coretests/src/android/app/appsearch/AppSearchSchemaTest.java new file mode 100644 index 000000000000..0be52c180338 --- /dev/null +++ b/core/tests/coretests/src/android/app/appsearch/AppSearchSchemaTest.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.appsearch; + +import static com.google.common.truth.Truth.assertThat; + +import static org.testng.Assert.assertThrows; +import static org.testng.Assert.expectThrows; + +import android.app.appsearch.AppSearchSchema.IndexingConfig; +import android.app.appsearch.AppSearchSchema.PropertyConfig; + +import androidx.test.filters.SmallTest; + +import com.google.android.icing.proto.IndexingConfig.TokenizerType; +import com.google.android.icing.proto.PropertyConfigProto; +import com.google.android.icing.proto.SchemaProto; +import com.google.android.icing.proto.SchemaTypeConfigProto; +import com.google.android.icing.proto.TermMatchType; + +import org.junit.Test; + +@SmallTest +public class AppSearchSchemaTest { + @Test + public void testSuccess() { + AppSearchSchema schema = AppSearchSchema.newBuilder() + .addType(AppSearchSchema.newSchemaTypeBuilder("Email") + .addProperty(AppSearchSchema.newPropertyBuilder("subject") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingConfig(AppSearchSchema.newIndexingConfigBuilder() + .setTokenizerType(IndexingConfig.TOKENIZER_TYPE_PLAIN) + .setTermMatchType(IndexingConfig.TERM_MATCH_TYPE_PREFIX) + .build() + ).build() + ).addProperty(AppSearchSchema.newPropertyBuilder("body") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingConfig(AppSearchSchema.newIndexingConfigBuilder() + .setTokenizerType(IndexingConfig.TOKENIZER_TYPE_PLAIN) + .setTermMatchType(IndexingConfig.TERM_MATCH_TYPE_PREFIX) + .build() + ).build() + ).build() + + ).addType(AppSearchSchema.newSchemaTypeBuilder("MusicRecording") + .addProperty(AppSearchSchema.newPropertyBuilder("artist") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_REPEATED) + .setIndexingConfig(AppSearchSchema.newIndexingConfigBuilder() + .setTokenizerType(IndexingConfig.TOKENIZER_TYPE_PLAIN) + .setTermMatchType(IndexingConfig.TERM_MATCH_TYPE_PREFIX) + .build() + ).build() + ).addProperty(AppSearchSchema.newPropertyBuilder("pubDate") + .setDataType(PropertyConfig.DATA_TYPE_INT64) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingConfig(AppSearchSchema.newIndexingConfigBuilder() + .setTokenizerType(IndexingConfig.TOKENIZER_TYPE_NONE) + .setTermMatchType(IndexingConfig.TERM_MATCH_TYPE_UNKNOWN) + .build() + ).build() + ).build() + ).build(); + + SchemaProto expectedProto = SchemaProto.newBuilder() + .addTypes(SchemaTypeConfigProto.newBuilder() + .setSchemaType("Email") + .addProperties(PropertyConfigProto.newBuilder() + .setPropertyName("subject") + .setDataType(PropertyConfigProto.DataType.Code.STRING) + .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) + .setIndexingConfig( + com.google.android.icing.proto.IndexingConfig.newBuilder() + .setTokenizerType(TokenizerType.Code.PLAIN) + .setTermMatchType(TermMatchType.Code.PREFIX) + ) + ).addProperties(PropertyConfigProto.newBuilder() + .setPropertyName("body") + .setDataType(PropertyConfigProto.DataType.Code.STRING) + .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) + .setIndexingConfig( + com.google.android.icing.proto.IndexingConfig.newBuilder() + .setTokenizerType(TokenizerType.Code.PLAIN) + .setTermMatchType(TermMatchType.Code.PREFIX) + ) + ) + + ).addTypes(SchemaTypeConfigProto.newBuilder() + .setSchemaType("MusicRecording") + .addProperties(PropertyConfigProto.newBuilder() + .setPropertyName("artist") + .setDataType(PropertyConfigProto.DataType.Code.STRING) + .setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED) + .setIndexingConfig( + com.google.android.icing.proto.IndexingConfig.newBuilder() + .setTokenizerType(TokenizerType.Code.PLAIN) + .setTermMatchType(TermMatchType.Code.PREFIX) + ) + ).addProperties(PropertyConfigProto.newBuilder() + .setPropertyName("pubDate") + .setDataType(PropertyConfigProto.DataType.Code.INT64) + .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) + .setIndexingConfig( + com.google.android.icing.proto.IndexingConfig.newBuilder() + .setTokenizerType(TokenizerType.Code.NONE) + .setTermMatchType(TermMatchType.Code.UNKNOWN) + ) + ) + ).build(); + + assertThat(schema.getProto()).isEqualTo(expectedProto); + } + + @Test + public void testInvalidEnums() { + PropertyConfig.Builder builder = AppSearchSchema.newPropertyBuilder("test"); + assertThrows(IllegalArgumentException.class, () -> builder.setDataType(99)); + assertThrows(IllegalArgumentException.class, () -> builder.setCardinality(99)); + } + + @Test + public void testMissingFields() { + PropertyConfig.Builder builder = AppSearchSchema.newPropertyBuilder("test"); + Exception e = expectThrows(IllegalSchemaException.class, builder::build); + assertThat(e).hasMessageThat().contains("Missing field: dataType"); + + builder.setDataType(PropertyConfig.DATA_TYPE_DOCUMENT); + e = expectThrows(IllegalSchemaException.class, builder::build); + assertThat(e).hasMessageThat().contains("Missing field: schemaType"); + + builder.setSchemaType("TestType"); + e = expectThrows(IllegalSchemaException.class, builder::build); + assertThat(e).hasMessageThat().contains("Missing field: cardinality"); + + builder.setCardinality(PropertyConfig.CARDINALITY_REPEATED); + builder.build(); + } +} diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java index 1bd52af09a4d..ade1e0de7102 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java @@ -46,7 +46,7 @@ public class AccessibilityNodeInfoTest { // The number of fields tested in the corresponding CTS AccessibilityNodeInfoTest: // See fullyPopulateAccessibilityNodeInfo, assertEqualsAccessibilityNodeInfo, // and assertAccessibilityNodeInfoCleared in that class. - private static final int NUM_MARSHALLED_PROPERTIES = 35; + private static final int NUM_MARSHALLED_PROPERTIES = 38; /** * The number of properties that are purposely not marshalled diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java index 93f4b5143c57..f151b810eea3 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java @@ -19,9 +19,11 @@ package android.view.accessibility; import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.IAccessibilityServiceConnection; import android.content.pm.ParceledListSlice; +import android.graphics.Bitmap; import android.graphics.Region; import android.os.Bundle; import android.os.IBinder; +import android.os.RemoteCallback; /** * Stub implementation of IAccessibilityServiceConnection so each test doesn't need to implement @@ -143,4 +145,14 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon public IBinder getOverlayWindowToken(int displayId) { return null; } + + public int getWindowIdForLeashToken(IBinder token) { + return -1; + } + + public Bitmap takeScreenshot(int displayId) { + return null; + } + + public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) {} } diff --git a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java index 215d0b800074..3dc001d68a02 100644 --- a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java +++ b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java @@ -48,24 +48,32 @@ public class EditorTouchStateTest { } @Test + public void testIsDistanceWithin() throws Exception { + assertTrue(EditorTouchState.isDistanceWithin(0, 0, 0, 0, 8)); + assertTrue(EditorTouchState.isDistanceWithin(3, 9, 5, 11, 8)); + assertTrue(EditorTouchState.isDistanceWithin(5, 11, 3, 9, 8)); + assertFalse(EditorTouchState.isDistanceWithin(5, 10, 5, 20, 8)); + } + + @Test public void testUpdate_singleTap() throws Exception { // Simulate an ACTION_DOWN event. long event1Time = 1000; MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f); mTouchState.update(event1, mConfig); - assertSingleTap(mTouchState, 20f, 30f, 0, 0, false); + assertSingleTap(mTouchState, 20f, 30f, 0, 0); // Simulate an ACTION_UP event. long event2Time = 1001; MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f); mTouchState.update(event2, mConfig); - assertSingleTap(mTouchState, 20f, 30f, 20f, 30f, false); + assertSingleTap(mTouchState, 20f, 30f, 20f, 30f); // Generate an ACTION_DOWN event whose time is after the double-tap timeout. long event3Time = event2Time + ViewConfiguration.getDoubleTapTimeout() + 1; MotionEvent event3 = downEvent(event3Time, event3Time, 22f, 33f); mTouchState.update(event3, mConfig); - assertSingleTap(mTouchState, 22f, 33f, 20f, 30f, false); + assertSingleTap(mTouchState, 22f, 33f, 20f, 30f); } @Test @@ -74,13 +82,13 @@ public class EditorTouchStateTest { long event1Time = 1000; MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f); mTouchState.update(event1, mConfig); - assertSingleTap(mTouchState, 20f, 30f, 0, 0, false); + assertSingleTap(mTouchState, 20f, 30f, 0, 0); // Simulate an ACTION_UP event. long event2Time = 1001; MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f); mTouchState.update(event2, mConfig); - assertSingleTap(mTouchState, 20f, 30f, 20f, 30f, false); + assertSingleTap(mTouchState, 20f, 30f, 20f, 30f); // Generate an ACTION_DOWN event whose time is within the double-tap timeout. long event3Time = 1002; @@ -96,13 +104,13 @@ public class EditorTouchStateTest { long event1Time = 1000; MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f); mTouchState.update(event1, mConfig); - assertSingleTap(mTouchState, 20f, 30f, 0, 0, false); + assertSingleTap(mTouchState, 20f, 30f, 0, 0); // Simulate an ACTION_UP event. long event2Time = 1001; MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f); mTouchState.update(event2, mConfig); - assertSingleTap(mTouchState, 20f, 30f, 20f, 30f, false); + assertSingleTap(mTouchState, 20f, 30f, 20f, 30f); // Generate an ACTION_DOWN event whose time is within the double-tap timeout. long event3Time = 1002; @@ -125,13 +133,13 @@ public class EditorTouchStateTest { long event1Time = 1000; MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f); mTouchState.update(event1, mConfig); - assertSingleTap(mTouchState, 20f, 30f, 0, 0, false); + assertSingleTap(mTouchState, 20f, 30f, 0, 0); // Simulate an ACTION_UP event with a delay that's longer than the double-tap timeout. long event2Time = 1000 + ViewConfiguration.getDoubleTapTimeout() + 1; MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f); mTouchState.update(event2, mConfig); - assertSingleTap(mTouchState, 20f, 30f, 20f, 30f, false); + assertSingleTap(mTouchState, 20f, 30f, 20f, 30f); // Generate an ACTION_DOWN event whose time is within the double-tap timeout when // calculated from the last ACTION_UP event time. Even though the time between the last up @@ -140,7 +148,7 @@ public class EditorTouchStateTest { long event3Time = event2Time + 1; MotionEvent event3 = downEvent(event3Time, event3Time, 22f, 33f); mTouchState.update(event3, mConfig); - assertSingleTap(mTouchState, 22f, 33f, 20f, 30f, false); + assertSingleTap(mTouchState, 22f, 33f, 20f, 30f); } @Test @@ -149,19 +157,19 @@ public class EditorTouchStateTest { long event1Time = 1000; MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f); mTouchState.update(event1, mConfig); - assertSingleTap(mTouchState, 20f, 30f, 0, 0, false); + assertSingleTap(mTouchState, 20f, 30f, 0, 0); // Simulate an ACTION_MOVE event. long event2Time = 1001; MotionEvent event2 = moveEvent(event1Time, event2Time, 200f, 31f); mTouchState.update(event2, mConfig); - assertSingleTap(mTouchState, 20f, 30f, 0, 0, true); + assertDrag(mTouchState, 20f, 30f, 0, 0, false); // Simulate an ACTION_UP event with a delay that's longer than the double-tap timeout. long event3Time = 5000; MotionEvent event3 = upEvent(event1Time, event3Time, 200f, 31f); mTouchState.update(event3, mConfig); - assertSingleTap(mTouchState, 20f, 30f, 200f, 31f, false); + assertSingleTap(mTouchState, 20f, 30f, 200f, 31f); // Generate an ACTION_DOWN event whose time is within the double-tap timeout when // calculated from the last ACTION_UP event time. Even though the time between the last up @@ -170,7 +178,7 @@ public class EditorTouchStateTest { long event4Time = event3Time + 1; MotionEvent event4 = downEvent(event4Time, event4Time, 200f, 31f); mTouchState.update(event4, mConfig); - assertSingleTap(mTouchState, 200f, 31f, 200f, 31f, false); + assertSingleTap(mTouchState, 200f, 31f, 200f, 31f); } @Test @@ -180,14 +188,14 @@ public class EditorTouchStateTest { MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f); event1.setSource(InputDevice.SOURCE_MOUSE); mTouchState.update(event1, mConfig); - assertSingleTap(mTouchState, 20f, 30f, 0, 0, false); + assertSingleTap(mTouchState, 20f, 30f, 0, 0); // Simulate an ACTION_UP event. long event2Time = 1001; MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f); event2.setSource(InputDevice.SOURCE_MOUSE); mTouchState.update(event2, mConfig); - assertSingleTap(mTouchState, 20f, 30f, 20f, 30f, false); + assertSingleTap(mTouchState, 20f, 30f, 20f, 30f); // Generate a second ACTION_DOWN event whose time is within the double-tap timeout. long event3Time = 1002; @@ -220,13 +228,13 @@ public class EditorTouchStateTest { long event1Time = 1000; MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f); mTouchState.update(event1, mConfig); - assertSingleTap(mTouchState, 20f, 30f, 0, 0, false); + assertSingleTap(mTouchState, 20f, 30f, 0, 0); // Simulate an ACTION_UP event. long event2Time = 1001; MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f); mTouchState.update(event2, mConfig); - assertSingleTap(mTouchState, 20f, 30f, 20f, 30f, false); + assertSingleTap(mTouchState, 20f, 30f, 20f, 30f); // Generate a second ACTION_DOWN event whose time is within the double-tap timeout. long event3Time = 1002; @@ -246,7 +254,7 @@ public class EditorTouchStateTest { long event5Time = 1004; MotionEvent event5 = downEvent(event5Time, event5Time, 22f, 32f); mTouchState.update(event5, mConfig); - assertSingleTap(mTouchState, 22f, 32f, 21f, 31f, false); + assertSingleTap(mTouchState, 22f, 32f, 21f, 31f); } @Test @@ -255,13 +263,13 @@ public class EditorTouchStateTest { long event1Time = 1000; MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f); mTouchState.update(event1, mConfig); - assertSingleTap(mTouchState, 20f, 30f, 0, 0, false); + assertSingleTap(mTouchState, 20f, 30f, 0, 0); // Simulate an ACTION_MOVE event whose location is not far enough to start a drag. long event2Time = 1001; MotionEvent event2 = moveEvent(event1Time, event2Time, 21f, 30f); mTouchState.update(event2, mConfig); - assertSingleTap(mTouchState, 20f, 30f, 0, 0, false); + assertSingleTap(mTouchState, 20f, 30f, 0, 0); // Simulate another ACTION_MOVE event whose location is far enough to start a drag. int touchSlop = mConfig.getScaledTouchSlop(); @@ -270,21 +278,135 @@ public class EditorTouchStateTest { long event3Time = 1002; MotionEvent event3 = moveEvent(event3Time, event3Time, newX, newY); mTouchState.update(event3, mConfig); - assertSingleTap(mTouchState, 20f, 30f, 0, 0, true); + assertDrag(mTouchState, 20f, 30f, 0, 0, false); // Simulate an ACTION_UP event. long event4Time = 1003; MotionEvent event4 = upEvent(event3Time, event4Time, 200f, 300f); mTouchState.update(event4, mConfig); - assertSingleTap(mTouchState, 20f, 30f, 200f, 300f, false); + assertSingleTap(mTouchState, 20f, 30f, 200f, 300f); } @Test - public void testIsDistanceWithin() throws Exception { - assertTrue(EditorTouchState.isDistanceWithin(0, 0, 0, 0, 8)); - assertTrue(EditorTouchState.isDistanceWithin(3, 9, 5, 11, 8)); - assertTrue(EditorTouchState.isDistanceWithin(5, 11, 3, 9, 8)); - assertFalse(EditorTouchState.isDistanceWithin(5, 10, 5, 20, 8)); + public void testUpdate_drag_startsCloseToVerticalThenHorizontal() throws Exception { + // Simulate an ACTION_DOWN event. + long event1Time = 1001; + MotionEvent event1 = downEvent(event1Time, event1Time, 0f, 0f); + mTouchState.update(event1, mConfig); + assertSingleTap(mTouchState, 0f, 0f, 0, 0); + + // Simulate an ACTION_MOVE event that is < 30 deg from vertical. + long event2Time = 1002; + MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 174f); + mTouchState.update(event2, mConfig); + assertDrag(mTouchState, 0f, 0f, 0, 0, true); + + // Simulate another ACTION_MOVE event that is horizontal from the original down event. + // The value of `isDragCloseToVertical` should NOT change since it should only reflect the + // initial direction of movement. + long event3Time = 1003; + MotionEvent event3 = moveEvent(event1Time, event3Time, 200f, 0f); + mTouchState.update(event3, mConfig); + assertDrag(mTouchState, 0f, 0f, 0, 0, true); + + // Simulate an ACTION_UP event. + long event4Time = 1004; + MotionEvent event4 = upEvent(event1Time, event4Time, 200f, 0f); + mTouchState.update(event4, mConfig); + assertSingleTap(mTouchState, 0f, 0f, 200f, 0f); + } + + @Test + public void testUpdate_drag_startsHorizontalThenVertical() throws Exception { + // Simulate an ACTION_DOWN event. + long event1Time = 1001; + MotionEvent event1 = downEvent(event1Time, event1Time, 0f, 0f); + mTouchState.update(event1, mConfig); + assertSingleTap(mTouchState, 0f, 0f, 0, 0); + + // Simulate an ACTION_MOVE event that is > 30 deg from vertical. + long event2Time = 1002; + MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 173f); + mTouchState.update(event2, mConfig); + assertDrag(mTouchState, 0f, 0f, 0, 0, false); + + // Simulate another ACTION_MOVE event that is vertical from the original down event. + // The value of `isDragCloseToVertical` should NOT change since it should only reflect the + // initial direction of movement. + long event3Time = 1003; + MotionEvent event3 = moveEvent(event1Time, event3Time, 0f, 200f); + mTouchState.update(event3, mConfig); + assertDrag(mTouchState, 0f, 0f, 0, 0, false); + + // Simulate an ACTION_UP event. + long event4Time = 1004; + MotionEvent event4 = upEvent(event1Time, event4Time, 0f, 200f); + mTouchState.update(event4, mConfig); + assertSingleTap(mTouchState, 0f, 0f, 0f, 200f); + } + + @Test + public void testUpdate_cancelAfterDown() throws Exception { + // Simulate an ACTION_DOWN event. + long event1Time = 1001; + MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f); + mTouchState.update(event1, mConfig); + assertSingleTap(mTouchState, 20f, 30f, 0, 0); + + // Simulate an ACTION_CANCEL event. + long event2Time = 1002; + MotionEvent event2 = cancelEvent(event1Time, event2Time, 20f, 30f); + mTouchState.update(event2, mConfig); + assertSingleTap(mTouchState, 20f, 30f, 0, 0); + } + + @Test + public void testUpdate_cancelAfterDrag() throws Exception { + // Simulate an ACTION_DOWN event. + long event1Time = 1001; + MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f); + mTouchState.update(event1, mConfig); + assertSingleTap(mTouchState, 20f, 30f, 0, 0); + + // Simulate another ACTION_MOVE event whose location is far enough to start a drag. + long event2Time = 1002; + MotionEvent event2 = moveEvent(event2Time, event2Time, 200f, 30f); + mTouchState.update(event2, mConfig); + assertDrag(mTouchState, 20f, 30f, 0, 0, false); + + // Simulate an ACTION_CANCEL event. + long event3Time = 1003; + MotionEvent event3 = cancelEvent(event1Time, event3Time, 200f, 30f); + mTouchState.update(event3, mConfig); + assertSingleTap(mTouchState, 20f, 30f, 0, 0); + } + + @Test + public void testUpdate_cancelAfterMultitap() throws Exception { + // Simulate an ACTION_DOWN event. + long event1Time = 1001; + MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f); + mTouchState.update(event1, mConfig); + assertSingleTap(mTouchState, 20f, 30f, 0, 0); + + // Simulate an ACTION_UP event. + long event2Time = 1002; + MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f); + mTouchState.update(event2, mConfig); + assertSingleTap(mTouchState, 20f, 30f, 20f, 30f); + + // Generate an ACTION_DOWN event whose time is within the double-tap timeout. + long event3Time = 1003; + MotionEvent event3 = downEvent(event3Time, event3Time, 22f, 33f); + mTouchState.update(event3, mConfig); + assertMultiTap(mTouchState, 22f, 33f, 20f, 30f, + MultiTapStatus.DOUBLE_TAP, true); + + // Simulate an ACTION_CANCEL event. + long event4Time = 1004; + MotionEvent event4 = cancelEvent(event3Time, event4Time, 20f, 30f); + mTouchState.update(event4, mConfig); + assertSingleTap(mTouchState, 22f, 33f, 20f, 30f); } private static MotionEvent downEvent(long downTime, long eventTime, float x, float y) { @@ -299,8 +421,25 @@ public class EditorTouchStateTest { return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 0); } + private static MotionEvent cancelEvent(long downTime, long eventTime, float x, float y) { + return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_CANCEL, x, y, 0); + } + private static void assertSingleTap(EditorTouchState touchState, float lastDownX, - float lastDownY, float lastUpX, float lastUpY, boolean isMovedEnoughForDrag) { + float lastDownY, float lastUpX, float lastUpY) { + assertThat(touchState.getLastDownX(), is(lastDownX)); + assertThat(touchState.getLastDownY(), is(lastDownY)); + assertThat(touchState.getLastUpX(), is(lastUpX)); + assertThat(touchState.getLastUpY(), is(lastUpY)); + assertThat(touchState.isDoubleTap(), is(false)); + assertThat(touchState.isTripleClick(), is(false)); + assertThat(touchState.isMultiTap(), is(false)); + assertThat(touchState.isMultiTapInSameArea(), is(false)); + assertThat(touchState.isMovedEnoughForDrag(), is(false)); + } + + private static void assertDrag(EditorTouchState touchState, float lastDownX, + float lastDownY, float lastUpX, float lastUpY, boolean isDragCloseToVertical) { assertThat(touchState.getLastDownX(), is(lastDownX)); assertThat(touchState.getLastDownY(), is(lastDownY)); assertThat(touchState.getLastUpX(), is(lastUpX)); @@ -309,7 +448,8 @@ public class EditorTouchStateTest { assertThat(touchState.isTripleClick(), is(false)); assertThat(touchState.isMultiTap(), is(false)); assertThat(touchState.isMultiTapInSameArea(), is(false)); - assertThat(touchState.isMovedEnoughForDrag(), is(isMovedEnoughForDrag)); + assertThat(touchState.isMovedEnoughForDrag(), is(true)); + assertThat(touchState.isDragCloseToVertical(), is(isDragCloseToVertical)); } private static void assertMultiTap(EditorTouchState touchState, @@ -325,5 +465,6 @@ public class EditorTouchStateTest { || multiTapStatus == MultiTapStatus.TRIPLE_CLICK)); assertThat(touchState.isMultiTapInSameArea(), is(isMultiTapInSameArea)); assertThat(touchState.isMovedEnoughForDrag(), is(false)); + assertThat(touchState.isDragCloseToVertical(), is(false)); } } diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java index aa55e08dbcdc..8bfd0a010cbc 100644 --- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java +++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java @@ -47,7 +47,6 @@ import android.view.textclassifier.TextClassificationManager; import android.view.textclassifier.TextClassifier; import androidx.test.filters.MediumTest; -import androidx.test.filters.Suppress; import androidx.test.rule.ActivityTestRule; import androidx.test.runner.AndroidJUnit4; @@ -193,7 +192,6 @@ public class TextViewActivityMouseTest { } @Test - @Suppress // Consistently failing. b/29591177 public void testDragAndDrop_longClick() { final String text = "abc def ghi."; onView(withId(R.id.textview)).perform(mouseClick()); 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 82854e5b8a9d..6784ede5b5da 100644 --- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java +++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java @@ -348,7 +348,7 @@ public class AccessibilityShortcutControllerTest { verify(mAlertDialog).show(); verify(mAccessibilityManagerService, atLeastOnce()).getInstalledAccessibilityServiceList( anyInt()); - verify(mAccessibilityManagerService, times(0)).performAccessibilityShortcut(); + verify(mAccessibilityManagerService, times(0)).performAccessibilityShortcut(null); verify(mFrameworkObjectProvider, times(0)).getTextToSpeech(any(), any()); } @@ -365,7 +365,7 @@ public class AccessibilityShortcutControllerTest { assertEquals(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS, mLayoutParams.privateFlags & WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS); - verify(mAccessibilityManagerService, times(1)).performAccessibilityShortcut(); + verify(mAccessibilityManagerService, times(1)).performAccessibilityShortcut(null); } @Test @@ -433,7 +433,7 @@ public class AccessibilityShortcutControllerTest { verifyZeroInteractions(mAlertDialogBuilder, mAlertDialog); verify(mToast).show(); - verify(mAccessibilityManagerService).performAccessibilityShortcut(); + verify(mAccessibilityManagerService).performAccessibilityShortcut(null); } @Test @@ -459,7 +459,7 @@ public class AccessibilityShortcutControllerTest { when(mServiceInfo.loadSummary(any())).thenReturn(null); Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1); getController().performAccessibilityShortcut(); - verify(mAccessibilityManagerService).performAccessibilityShortcut(); + verify(mAccessibilityManagerService).performAccessibilityShortcut(null); } @Test @@ -471,7 +471,7 @@ public class AccessibilityShortcutControllerTest { getController().performAccessibilityShortcut(); verifyZeroInteractions(mToast); - verify(mAccessibilityManagerService).performAccessibilityShortcut(); + verify(mAccessibilityManagerService).performAccessibilityShortcut(null); } @Test @@ -485,7 +485,7 @@ public class AccessibilityShortcutControllerTest { getController().performAccessibilityShortcut(); verifyZeroInteractions(mToast); - verify(mAccessibilityManagerService).performAccessibilityShortcut(); + verify(mAccessibilityManagerService).performAccessibilityShortcut(null); } @Test diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp index 26a40d3dea8c..dfb7a16e6771 100644 --- a/data/etc/car/Android.bp +++ b/data/etc/car/Android.bp @@ -135,3 +135,10 @@ prebuilt_etc { src: "com.android.car.floatingcardslauncher.xml", filename_from_src: true, } + +prebuilt_etc { + name: "privapp_whitelist_com.android.car.ui.paintbooth", + sub_dir: "permissions", + src: "com.android.car.ui.paintbooth.xml", + filename_from_src: true, +} diff --git a/data/etc/car/com.android.car.ui.paintbooth.xml b/data/etc/car/com.android.car.ui.paintbooth.xml new file mode 100644 index 000000000000..11bf304fe8f1 --- /dev/null +++ b/data/etc/car/com.android.car.ui.paintbooth.xml @@ -0,0 +1,29 @@ +<?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 + --> +<permissions> + + <privapp-permissions package="com.android.car.ui.paintbooth"> + <!-- For enabling/disabling, and getting list of RROs --> + <permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/> + <!-- For showing the current active activity --> + <permission name="android.permission.REAL_GET_TASKS"/> + <!-- For getting list of RROs for current user --> + <permission name="android.permission.MANAGE_USERS"/> + <!-- For getting list of RROs for current user--> + <permission name="android.permission.INTERACT_ACROSS_USERS"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 9930ea262b32..ad99ab335145 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -245,6 +245,7 @@ applications that come with the platform <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/> <permission name="android.permission.TETHER_PRIVILEGED"/> <permission name="android.permission.UPDATE_APP_OPS_STATS"/> + <permission name="android.permission.UPDATE_DEVICE_STATS"/> </privapp-permissions> <privapp-permissions package="com.android.server.telecom"> diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java index 17aacb9756aa..fedde422e0be 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java @@ -308,6 +308,9 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { if (spec.isStrongBoxBacked()) { flags |= KeyStore.FLAG_STRONGBOX; } + if (spec.isCriticalToDeviceEncryption()) { + flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION; + } String keyAliasInKeystore = Credentials.USER_PRIVATE_KEY + spec.getKeystoreAlias(); KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics(); boolean success = false; diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java index 91aac8367976..c52fd48459cb 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -18,10 +18,8 @@ package android.security.keystore; import android.annotation.Nullable; import android.security.Credentials; -import android.security.GateKeeper; import android.security.KeyPairGeneratorSpec; import android.security.KeyStore; -import android.security.KeyStoreException; import android.security.keymaster.KeyCharacteristics; import android.security.keymaster.KeymasterArguments; import android.security.keymaster.KeymasterCertificateChain; @@ -458,6 +456,9 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato if (mSpec.isStrongBoxBacked()) { flags |= KeyStore.FLAG_STRONGBOX; } + if (mSpec.isCriticalToDeviceEncryption()) { + flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION; + } byte[] additionalEntropy = KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng( diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index 52ff9e0449ca..450dd3301253 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -271,6 +271,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private final boolean mIsStrongBoxBacked; private final boolean mUserConfirmationRequired; private final boolean mUnlockedDeviceRequired; + private final boolean mCriticalToDeviceEncryption; /* * ***NOTE***: All new fields MUST also be added to the following: * ParcelableKeyGenParameterSpec class. @@ -307,7 +308,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu boolean invalidatedByBiometricEnrollment, boolean isStrongBoxBacked, boolean userConfirmationRequired, - boolean unlockedDeviceRequired) { + boolean unlockedDeviceRequired, + boolean criticalToDeviceEncryption) { if (TextUtils.isEmpty(keyStoreAlias)) { throw new IllegalArgumentException("keyStoreAlias must not be empty"); } @@ -357,6 +359,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mIsStrongBoxBacked = isStrongBoxBacked; mUserConfirmationRequired = userConfirmationRequired; mUnlockedDeviceRequired = unlockedDeviceRequired; + mCriticalToDeviceEncryption = criticalToDeviceEncryption; } /** @@ -710,6 +713,16 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu } /** + * Return whether this key is critical to the device encryption flow. + * + * @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION + * @hide + */ + public boolean isCriticalToDeviceEncryption() { + return mCriticalToDeviceEncryption; + } + + /** * Builder of {@link KeyGenParameterSpec} instances. */ public final static class Builder { @@ -741,6 +754,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private boolean mIsStrongBoxBacked = false; private boolean mUserConfirmationRequired; private boolean mUnlockedDeviceRequired = false; + private boolean mCriticalToDeviceEncryption = false; /** * Creates a new instance of the {@code Builder}. @@ -804,6 +818,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mIsStrongBoxBacked = sourceSpec.isStrongBoxBacked(); mUserConfirmationRequired = sourceSpec.isUserConfirmationRequired(); mUnlockedDeviceRequired = sourceSpec.isUnlockedDeviceRequired(); + mCriticalToDeviceEncryption = sourceSpec.isCriticalToDeviceEncryption(); } /** @@ -1339,6 +1354,20 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu } /** + * Set whether this key is critical to the device encryption flow + * + * This is a special flag only available to system servers to indicate the current key + * is part of the device encryption flow. + * + * @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION + * @hide + */ + public Builder setCriticalToDeviceEncryption(boolean critical) { + mCriticalToDeviceEncryption = critical; + return this; + } + + /** * Builds an instance of {@code KeyGenParameterSpec}. */ @NonNull @@ -1370,7 +1399,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mInvalidatedByBiometricEnrollment, mIsStrongBoxBacked, mUserConfirmationRequired, - mUnlockedDeviceRequired); + mUnlockedDeviceRequired, + mCriticalToDeviceEncryption); } } } diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java index d8030fb8ab79..98e458930a7f 100644 --- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java @@ -16,8 +16,8 @@ package android.security.keystore; -import android.os.Parcelable; import android.os.Parcel; +import android.os.Parcelable; import java.math.BigInteger; import java.security.spec.AlgorithmParameterSpec; @@ -105,6 +105,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { out.writeBoolean(mSpec.isStrongBoxBacked()); out.writeBoolean(mSpec.isUserConfirmationRequired()); out.writeBoolean(mSpec.isUnlockedDeviceRequired()); + out.writeBoolean(mSpec.isCriticalToDeviceEncryption()); } private static Date readDateOrNull(Parcel in) { @@ -160,6 +161,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { final boolean isStrongBoxBacked = in.readBoolean(); final boolean userConfirmationRequired = in.readBoolean(); final boolean unlockedDeviceRequired = in.readBoolean(); + final boolean criticalToDeviceEncryption = in.readBoolean(); // The KeyGenParameterSpec is intentionally not constructed using a Builder here: // The intention is for this class to break if new parameters are added to the // KeyGenParameterSpec constructor (whereas using a builder would silently drop them). @@ -190,7 +192,8 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { invalidatedByBiometricEnrollment, isStrongBoxBacked, userConfirmationRequired, - unlockedDeviceRequired); + unlockedDeviceRequired, + criticalToDeviceEncryption); } public static final @android.annotation.NonNull Creator<ParcelableKeyGenParameterSpec> CREATOR = new Creator<ParcelableKeyGenParameterSpec>() { diff --git a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java index fca2775a34bb..b7d72fce6eba 100644 --- a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java +++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java @@ -84,6 +84,7 @@ public final class ParcelableKeyGenParameterSpecTest { .setIsStrongBoxBacked(true) .setUserConfirmationRequired(true) .setUnlockedDeviceRequired(true) + .setCriticalToDeviceEncryption(true) .build(); } @@ -115,6 +116,7 @@ public final class ParcelableKeyGenParameterSpecTest { assertThat(spec.isStrongBoxBacked(), is(true)); assertThat(spec.isUserConfirmationRequired(), is(true)); assertThat(spec.isUnlockedDeviceRequired(), is(true)); + assertThat(spec.isCriticalToDeviceEncryption(), is(true)); } private Parcel parcelForReading(ParcelableKeyGenParameterSpec spec) { diff --git a/media/OWNERS b/media/OWNERS index 8bd037a14150..be605831a24b 100644 --- a/media/OWNERS +++ b/media/OWNERS @@ -5,6 +5,7 @@ elaurent@google.com etalvala@google.com gkasten@google.com hdmoon@google.com +hkuang@google.com hunga@google.com insun@google.com jaewan@google.com diff --git a/media/java/android/media/VolumeProvider.java b/media/java/android/media/VolumeProvider.java index 8f68cbda3e3c..ed272d54dac8 100644 --- a/media/java/android/media/VolumeProvider.java +++ b/media/java/android/media/VolumeProvider.java @@ -16,6 +16,7 @@ package android.media; import android.annotation.IntDef; +import android.annotation.Nullable; import android.media.session.MediaSession; import java.lang.annotation.Retention; @@ -60,6 +61,7 @@ public abstract class VolumeProvider { private final int mControlType; private final int mMaxVolume; + private final String mControlId; private int mCurrentVolume; private Callback mCallback; @@ -73,10 +75,28 @@ public abstract class VolumeProvider { * @param maxVolume The maximum allowed volume. * @param currentVolume The current volume on the output. */ + public VolumeProvider(@ControlType int volumeControl, int maxVolume, int currentVolume) { + this(volumeControl, maxVolume, currentVolume, null); + } + + /** + * Create a new volume provider for handling volume events. You must specify + * the type of volume control, the maximum volume that can be used, and the + * current volume on the output. + * + * @param volumeControl The method for controlling volume that is used by + * this provider. + * @param maxVolume The maximum allowed volume. + * @param currentVolume The current volume on the output. + * @param volumeControlId The volume control id of this provider. + */ + public VolumeProvider(@ControlType int volumeControl, int maxVolume, int currentVolume, + @Nullable String volumeControlId) { mControlType = volumeControl; mMaxVolume = maxVolume; mCurrentVolume = currentVolume; + mControlId = volumeControlId; } /** @@ -122,6 +142,17 @@ public abstract class VolumeProvider { } /** + * Gets the volume control id. It can be used to identify which volume provider is + * used by the session. + * + * @return the volume control id or {@code null} if it isn't set. + */ + @Nullable + public final String getVolumeControlId() { + return mControlId; + } + + /** * Override to handle requests to set the volume of the current output. * After the volume has been modified {@link #setCurrentVolume} must be * called to notify the system. diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl index 4d68a6ae52e4..21378c80fd56 100644 --- a/media/java/android/media/session/ISession.aidl +++ b/media/java/android/media/session/ISession.aidl @@ -48,6 +48,6 @@ interface ISession { // These commands relate to volume handling void setPlaybackToLocal(in AudioAttributes attributes); - void setPlaybackToRemote(int control, int max); + void setPlaybackToRemote(int control, int max, @nullable String controlId); void setCurrentVolume(int currentVolume); } diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java index 1812d9ce0739..c2f620608b20 100644 --- a/media/java/android/media/session/MediaController.java +++ b/media/java/android/media/session/MediaController.java @@ -964,16 +964,26 @@ public final class MediaController { private final int mMaxVolume; private final int mCurrentVolume; private final AudioAttributes mAudioAttrs; + private final String mVolumeControlId; /** * @hide */ public PlaybackInfo(int type, int control, int max, int current, AudioAttributes attrs) { + this(type, control, max, current, attrs, null); + } + + /** + * @hide + */ + public PlaybackInfo(int type, int control, int max, int current, AudioAttributes attrs, + String volumeControlId) { mVolumeType = type; mVolumeControl = control; mMaxVolume = max; mCurrentVolume = current; mAudioAttrs = attrs; + mVolumeControlId = volumeControlId; } PlaybackInfo(Parcel in) { @@ -982,6 +992,7 @@ public final class MediaController { mMaxVolume = in.readInt(); mCurrentVolume = in.readInt(); mAudioAttrs = in.readParcelable(null); + mVolumeControlId = in.readString(); } /** @@ -1042,11 +1053,24 @@ public final class MediaController { return mAudioAttrs; } + /** + * Gets the volume control ID for this session. It can be used to identify which + * volume provider is used by the session. + * + * @return the volume control ID for this session or {@code null} if it's local playback + * or not set. + * @see VolumeProvider#getVolumeControlId() + */ + @Nullable + public String getVolumeControlId() { + return mVolumeControlId; + } + @Override public String toString() { return "volumeType=" + mVolumeType + ", volumeControl=" + mVolumeControl + ", maxVolume=" + mMaxVolume + ", currentVolume=" + mCurrentVolume - + ", audioAttrs=" + mAudioAttrs; + + ", audioAttrs=" + mAudioAttrs + ", volumeControlId=" + mVolumeControlId; } @Override @@ -1061,6 +1085,7 @@ public final class MediaController { dest.writeInt(mMaxVolume); dest.writeInt(mCurrentVolume); dest.writeParcelable(mAudioAttrs, flags); + dest.writeString(mVolumeControlId); } public static final @android.annotation.NonNull Parcelable.Creator<PlaybackInfo> CREATOR = diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 3adee590c125..9953626f0a7a 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -335,7 +335,7 @@ public final class MediaSession { try { mBinder.setPlaybackToRemote(volumeProvider.getVolumeControl(), - volumeProvider.getMaxVolume()); + volumeProvider.getMaxVolume(), volumeProvider.getVolumeControlId()); mBinder.setCurrentVolume(volumeProvider.getCurrentVolume()); } catch (RemoteException e) { Log.wtf(TAG, "Failure in setPlaybackToRemote.", e); diff --git a/media/java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl b/media/java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl index 149c1cd0447b..726af7681979 100644 --- a/media/java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl +++ b/media/java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl @@ -44,4 +44,11 @@ oneway interface ISoundTriggerCallback { * and this event will be sent in addition to the abort event. */ void onRecognitionAvailabilityChange(boolean available); + /** + * Notifies the client that the associated module has crashed and restarted. The module instance + * is no longer usable and will throw a ServiceSpecificException with a Status.DEAD_OBJECT code + * for every call. The client should detach, then re-attach to the module in order to get a new, + * usable instance. All state for this module has been lost. + */ + void onModuleDied(); } diff --git a/media/java/android/media/soundtrigger_middleware/Status.aidl b/media/java/android/media/soundtrigger_middleware/Status.aidl index 5d082e272295..85ccacf21c8d 100644 --- a/media/java/android/media/soundtrigger_middleware/Status.aidl +++ b/media/java/android/media/soundtrigger_middleware/Status.aidl @@ -28,4 +28,6 @@ enum Status { OPERATION_NOT_SUPPORTED = 2, /** Temporary lack of permission. */ TEMPORARY_PERMISSION_DENIED = 3, + /** The object on which this method is called is dead and all of its state is lost. */ + DEAD_OBJECT = 4, } diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java index c7cc9e6ddb7f..8e579bf897dd 100644 --- a/media/java/android/media/tv/tuner/Lnb.java +++ b/media/java/android/media/tv/tuner/Lnb.java @@ -23,7 +23,6 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.Context; import android.hardware.tv.tuner.V1_0.Constants; -import android.media.tv.tuner.Tuner.LnbCallback; import android.media.tv.tuner.TunerConstants.Result; import java.lang.annotation.Retention; diff --git a/media/java/android/media/tv/tuner/LnbCallback.java b/media/java/android/media/tv/tuner/LnbCallback.java new file mode 100644 index 000000000000..99bbf866aa69 --- /dev/null +++ b/media/java/android/media/tv/tuner/LnbCallback.java @@ -0,0 +1,39 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.tuner; + + +/** + * Callback interface for receiving information from LNBs. + * + * @hide + */ +public interface LnbCallback { + /** + * Invoked when there is a LNB event. + */ + void onEvent(int lnbEventType); + + /** + * Invoked when there is a new DiSEqC message. + * + * @param diseqcMessage a byte array of data for DiSEqC (Digital Satellite + * Equipment Control) message which is specified by EUTELSAT Bus Functional + * Specification Version 4.2. + */ + void onDiseqcMessage(byte[] diseqcMessage); +} diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index e3dfaeb10948..6f5ba849f8f8 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -137,29 +137,10 @@ public final class Tuner implements AutoCloseable { private static native DemuxCapabilities nativeGetDemuxCapabilities(); - /** - * LNB Callback. - * - * @hide - */ - public interface LnbCallback { - /** - * Invoked when there is a LNB event. - */ - void onEvent(int lnbEventType); - - /** - * Invoked when there is a new DiSEqC message. - * - * @param diseqcMessage a byte array of data for DiSEqC (Digital Satellite - * Equipment Control) message which is specified by EUTELSAT Bus Functional - * Specification Version 4.2. - */ - void onDiseqcMessage(byte[] diseqcMessage); - } /** * Callback interface for receiving information from the corresponding filters. + * TODO: remove */ public interface FilterCallback { /** @@ -178,22 +159,6 @@ public final class Tuner implements AutoCloseable { void onFilterStatusChanged(@NonNull Filter filter, @FilterStatus int status); } - /** - * DVR Callback. - * - * @hide - */ - public interface DvrCallback { - /** - * Invoked when record status changed. - */ - void onRecordStatus(int status); - /** - * Invoked when playback status changed. - */ - void onPlaybackStatus(int status); - } - @Nullable private EventHandler createEventHandler() { Looper looper; diff --git a/media/java/android/media/tv/tuner/dvr/Dvr.java b/media/java/android/media/tv/tuner/dvr/Dvr.java index f90042b8e745..95508d3b366d 100644 --- a/media/java/android/media/tv/tuner/dvr/Dvr.java +++ b/media/java/android/media/tv/tuner/dvr/Dvr.java @@ -18,7 +18,6 @@ package android.media.tv.tuner.dvr; import android.annotation.BytesLong; import android.annotation.NonNull; -import android.media.tv.tuner.Tuner.DvrCallback; import android.media.tv.tuner.Tuner.Filter; import android.media.tv.tuner.TunerConstants.Result; import android.os.ParcelFileDescriptor; diff --git a/media/java/android/media/tv/tuner/dvr/DvrCallback.java b/media/java/android/media/tv/tuner/dvr/DvrCallback.java new file mode 100644 index 000000000000..3d140f0accac --- /dev/null +++ b/media/java/android/media/tv/tuner/dvr/DvrCallback.java @@ -0,0 +1,33 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.tuner.dvr; + +/** + * Callback interface for receiving information from DVR interfaces. + * + * @hide + */ +public interface DvrCallback { + /** + * Invoked when record status changed. + */ + void onRecordStatusChanged(int status); + /** + * Invoked when playback status changed. + */ + void onPlaybackStatusChanged(int status); +} diff --git a/media/java/android/media/tv/tuner/filter/FilterCallback.java b/media/java/android/media/tv/tuner/filter/FilterCallback.java new file mode 100644 index 000000000000..888adc5f51bb --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/FilterCallback.java @@ -0,0 +1,42 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.tuner.filter; + +import android.annotation.NonNull; +import android.media.tv.tuner.TunerConstants.FilterStatus; + +/** + * Callback interface for receiving information from the corresponding filters. + * + * @hide + */ +public interface FilterCallback { + /** + * Invoked when there are filter events. + * + * @param filter the corresponding filter which sent the events. + * @param events the filter events sent from the filter. + */ + void onFilterEvent(@NonNull Filter filter, @NonNull FilterEvent[] events); + /** + * Invoked when filter status changed. + * + * @param filter the corresponding filter whose status is changed. + * @param status the new status of the filter. + */ + void onFilterStatusChanged(@NonNull Filter filter, @FilterStatus int status); +} diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 747ceb190a5d..50d3a5daeb84 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -262,13 +262,28 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } } + /** + * Connect this device. + * + * @param connectAllProfiles {@code true} to connect all profile, {@code false} otherwise. + * + * @deprecated use {@link #connect()} instead. + */ + @Deprecated public void connect(boolean connectAllProfiles) { + connect(); + } + + /** + * Connect this device. + */ + public void connect() { if (!ensurePaired()) { return; } mConnectAttempted = SystemClock.elapsedRealtime(); - connectWithoutResettingTimer(connectAllProfiles); + connectAllEnabledProfiles(); } public long getHiSyncId() { @@ -289,10 +304,10 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> void onBondingDockConnect() { // Attempt to connect if UUIDs are available. Otherwise, // we will connect when the ACTION_UUID intent arrives. - connect(false); + connect(); } - private void connectWithoutResettingTimer(boolean connectAllProfiles) { + private void connectAllEnabledProfiles() { synchronized (mProfileLock) { // Try to initialize the profiles if they were not. if (mProfiles.isEmpty()) { @@ -307,36 +322,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> return; } - int preferredProfiles = 0; - for (LocalBluetoothProfile profile : mProfiles) { - if (connectAllProfiles ? profile.accessProfileEnabled() - : profile.isAutoConnectable()) { - if (profile.isPreferred(mDevice)) { - ++preferredProfiles; - connectInt(profile); - } - } - } - if (BluetoothUtils.D) Log.d(TAG, "Preferred profiles = " + preferredProfiles); - - if (preferredProfiles == 0) { - connectAutoConnectableProfiles(); - } - } - } - - private void connectAutoConnectableProfiles() { - if (!ensurePaired()) { - return; - } - - synchronized (mProfileLock) { - for (LocalBluetoothProfile profile : mProfiles) { - if (profile.isAutoConnectable()) { - profile.setPreferred(mDevice, true); - connectInt(profile); - } - } + mLocalAdapter.connectAllEnabledProfiles(mDevice); } } @@ -703,7 +689,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> */ if (!mProfiles.isEmpty() && ((mConnectAttempted + timeout) > SystemClock.elapsedRealtime())) { - connectWithoutResettingTimer(false); + connectAllEnabledProfiles(); } dispatchAttributesChanged(); @@ -722,7 +708,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> refresh(); if (bondState == BluetoothDevice.BOND_BONDED && mDevice.isBondingInitiatedLocally()) { - connect(false); + connect(); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java index 61e47f8f8dd8..6e7a429e6b7a 100644 --- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java +++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java @@ -87,8 +87,10 @@ public class VisibilityLoggerMixin implements LifecycleObserver, OnAttach { if (mMetricsFeature == null || mMetricsCategory == METRICS_CATEGORY_UNKNOWN) { return; } - final int elapse = (int) (SystemClock.elapsedRealtime() - mCreationTimestamp); - mMetricsFeature.action(METRICS_CATEGORY_UNKNOWN, action, mMetricsCategory, key, elapse); + if (mCreationTimestamp != 0L) { + final int elapse = (int) (SystemClock.elapsedRealtime() - mCreationTimestamp); + mMetricsFeature.action(METRICS_CATEGORY_UNKNOWN, action, mMetricsCategory, key, elapse); + } } /** diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java index 4ab9a9ac5915..b07fc2bee3f9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java @@ -61,6 +61,8 @@ public final class CategoryKey { "com.android.settings.category.ia.my_device_info"; public static final String CATEGORY_BATTERY_SAVER_SETTINGS = "com.android.settings.category.ia.battery_saver_settings"; + public static final String CATEGORY_SMART_BATTERY_SETTINGS = + "com.android.settings.category.ia.smart_battery_settings"; public static final Map<String, String> KEY_COMPAT_MAP; diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index 70b56ed0b391..e85a102294d8 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -122,7 +122,7 @@ public class LocalMediaManager implements BluetoothCallback { final CachedBluetoothDevice cachedDevice = ((BluetoothMediaDevice) device).getCachedDevice(); if (!cachedDevice.isConnected() && !cachedDevice.isBusy()) { - cachedDevice.connect(true); + cachedDevice.connect(); return; } } diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java index 853c77efd4e9..05a6ce4578db 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java @@ -40,7 +40,7 @@ public class DataUsageUtils { final NetworkTemplate mobileAll = NetworkTemplate.buildTemplateMobileAll( telephonyManager.createForSubscriptionId(subId).getSubscriberId()); - if (!subscriptionManager.isActiveSubId(subId)) { + if (!subscriptionManager.isActiveSubscriptionId(subId)) { Log.i(TAG, "Subscription is not active: " + subId); return mobileAll; } diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/OWNER b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/OWNER new file mode 100644 index 000000000000..5c2a7b892f8f --- /dev/null +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/OWNER @@ -0,0 +1,4 @@ +# People who can approve changes for submission +arcwang@google.com +govenliu@google.com +qal@google.com diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java index 605c861fa07f..340a6c7f5d10 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java @@ -59,8 +59,9 @@ public class CategoryKeyTest { allKeys.add(CategoryKey.CATEGORY_SYSTEM_DEVELOPMENT); allKeys.add(CategoryKey.CATEGORY_GESTURES); allKeys.add(CategoryKey.CATEGORY_NIGHT_DISPLAY); + allKeys.add(CategoryKey.CATEGORY_SMART_BATTERY_SETTINGS); // DO NOT REMOVE ANYTHING ABOVE - assertThat(allKeys.size()).isEqualTo(18); + assertThat(allKeys.size()).isEqualTo(19); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java index 894aa78a978e..c780a64c2fb4 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java @@ -127,7 +127,7 @@ public class LocalMediaManagerTest { mLocalMediaManager.registerCallback(mCallback); mLocalMediaManager.connectDevice(device); - verify(cachedDevice).connect(true); + verify(cachedDevice).connect(); } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java index 7be9fee027a3..b0a647e0dd2f 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java @@ -72,13 +72,13 @@ public class DataUsageUtilsTest { when(mTelephonyManager.getSubscriberId(SUB_ID)).thenReturn(SUBSCRIBER_ID); when(mTelephonyManager.getSubscriberId(SUB_ID_2)).thenReturn(SUBSCRIBER_ID_2); when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager); - when(mSubscriptionManager.isActiveSubId(anyInt())).thenReturn(true); + when(mSubscriptionManager.isActiveSubscriptionId(anyInt())).thenReturn(true); } @Test @Ignore public void getMobileTemplate_infoNull_returnMobileAll() { - when(mSubscriptionManager.isActiveSubId(SUB_ID)).thenReturn(false); + when(mSubscriptionManager.isActiveSubscriptionId(SUB_ID)).thenReturn(false); final NetworkTemplate networkTemplate = DataUsageUtils.getMobileTemplate(mContext, SUB_ID); assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID)).isTrue(); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java index c288d5110580..2bd20a933c58 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java @@ -26,7 +26,6 @@ import android.content.Context; import android.graphics.drawable.ColorDrawable; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -59,7 +58,6 @@ public class AccessPointPreferenceTest { } @Test - @Ignore public void refresh_openNetwork_updateContentDescription() { final String ssid = "ssid"; final String summary = "connected"; @@ -74,7 +72,7 @@ public class AccessPointPreferenceTest { assertThat(AccessPointPreference.buildContentDescription( RuntimeEnvironment.application, pref, ap)) - .isEqualTo("ssid,connected,Wifi signal full.,Secure network"); + .isEqualTo("ssid,connected,Wifi disconnected.,Secure network"); } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/OWNER b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/OWNER new file mode 100644 index 000000000000..5c2a7b892f8f --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/OWNER @@ -0,0 +1,4 @@ +# People who can approve changes for submission +arcwang@google.com +govenliu@google.com +qal@google.com diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 1e0c1d877f67..4443524d2098 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -20,6 +20,7 @@ import static android.os.Process.INVALID_UID; import static android.os.Process.ROOT_UID; import static android.os.Process.SHELL_UID; import static android.os.Process.SYSTEM_UID; +import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY; import android.Manifest; @@ -3304,7 +3305,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 185; + private static final int SETTINGS_VERSION = 186; private final int mUserId; @@ -4547,6 +4548,32 @@ public class SettingsProvider extends ContentProvider { currentVersion = 185; } + if (currentVersion == 185) { + // Deprecate ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, and migrate it + // to ACCESSIBILITY_BUTTON_TARGET_COMPONENT. + final SettingsState secureSettings = getSecureSettingsLocked(userId); + final Setting magnifyNavbarEnabled = secureSettings.getSettingLocked( + Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED); + if ("1".equals(magnifyNavbarEnabled.getValue())) { + secureSettings.insertSettingLocked( + Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, + ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER, + null /* tag */, false /* makeDefault */, + SettingsState.SYSTEM_PACKAGE_NAME); + } else { + // Clear a11y button targets list setting. A11yManagerService will end up + // adding all legacy enabled services that want the button to the list, so + // there's no need to keep tracking them. + secureSettings.insertSettingLocked( + Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, + null, null /* tag */, false /* makeDefault */, + SettingsState.SYSTEM_PACKAGE_NAME); + } + secureSettings.deleteSettingLocked( + Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED); + currentVersion = 186; + } + // vXXX: Add new settings above this point. if (currentVersion != newVersion) { diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 72782258d237..1a1a480d59e2 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -734,7 +734,8 @@ public class SettingsBackupTest { Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE, Settings.Secure.FACE_UNLOCK_RE_ENROLL, Settings.Secure.TAP_GESTURE, - Settings.Secure.WINDOW_MAGNIFICATION); + Settings.Secure.WINDOW_MAGNIFICATION, + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER); @Test public void systemSettingsBackedUpOrBlacklisted() { diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 2a1e74e18fb7..df77b5bfb895 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -211,6 +211,7 @@ <!-- accessibility --> <uses-permission android:name="android.permission.MODIFY_ACCESSIBILITY_DATA" /> + <uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY" /> <!-- to control accessibility volume --> <uses-permission android:name="android.permission.CHANGE_ACCESSIBILITY_VOLUME" /> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index df0dc467f87e..e475ef1d9761 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -387,8 +387,8 @@ public class KeyguardClockSwitch extends RelativeLayout { * Refresh the time of the clock, due to either time tick broadcast or doze time tick alarm. */ public void refresh() { - mClockView.refresh(); - mClockViewBold.refresh(); + mClockView.refreshTime(); + mClockViewBold.refreshTime(); if (mClockPlugin != null) { mClockPlugin.onTimeTick(); } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java index eba24004e387..99e122ef74e9 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java @@ -188,7 +188,7 @@ public class AnalogClockController implements ClockPlugin { public void onTimeTick() { mAnalogClock.onTimeChanged(); mBigClockView.onTimeChanged(); - mLockClock.refresh(); + mLockClock.refreshTime(); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java index 3a2fbe5a9653..fac923c01af5 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java @@ -195,7 +195,7 @@ public class BubbleClockController implements ClockPlugin { public void onTimeTick() { mAnalogClock.onTimeChanged(); mView.onTimeChanged(); - mLockClock.refresh(); + mLockClock.refreshTime(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt index 3bbd722517f7..820c042e42ff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt @@ -176,12 +176,14 @@ open class NotificationRankingManager @Inject constructor( } entry.ranking = newRanking - val oldSbn = entry.sbn.cloneLight() val newOverrideGroupKey = newRanking.overrideGroupKey - if (!Objects.equals(oldSbn.overrideGroupKey, newOverrideGroupKey)) { + if (!Objects.equals(entry.sbn.overrideGroupKey, newOverrideGroupKey)) { + val oldGroupKey = entry.sbn.groupKey + val oldIsGroup = entry.sbn.isGroup + val oldIsGroupSummary = entry.sbn.notification.isGroupSummary entry.sbn.overrideGroupKey = newOverrideGroupKey - // TODO: notify group manager here? - groupManager.onEntryUpdated(entry, oldSbn) + groupManager.onEntryUpdated(entry, oldGroupKey, oldIsGroup, + oldIsGroupSummary) } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index a6842badc153..4f56f56735a5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -157,7 +157,6 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback private int mNavigationIconHints = 0; private @TransitionMode int mNavigationBarMode; private AccessibilityManager mAccessibilityManager; - private MagnificationContentObserver mMagnificationObserver; private ContentResolver mContentResolver; private boolean mAssistantAvailable; @@ -303,11 +302,6 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback mWindowManager = getContext().getSystemService(WindowManager.class); mAccessibilityManager = getContext().getSystemService(AccessibilityManager.class); mContentResolver = getContext().getContentResolver(); - mMagnificationObserver = new MagnificationContentObserver( - getContext().getMainThreadHandler()); - mContentResolver.registerContentObserver(Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED), false, - mMagnificationObserver, UserHandle.USER_ALL); mContentResolver.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.ASSISTANT), false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL); @@ -329,7 +323,6 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback super.onDestroy(); mNavigationModeController.removeListener(this); mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener); - mContentResolver.unregisterContentObserver(mMagnificationObserver); mContentResolver.unregisterContentObserver(mAssistContentObserver); } @@ -969,28 +962,18 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback * @param outFeedbackEnabled if non-null, sets it to true if accessibility feedback is enabled. */ public int getA11yButtonState(@Nullable boolean[] outFeedbackEnabled) { - int requestingServices = 0; - try { - if (Settings.Secure.getIntForUser(mContentResolver, - Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, - UserHandle.USER_CURRENT) == 1) { - requestingServices++; - } - } catch (Settings.SettingNotFoundException e) { - } - boolean feedbackEnabled = false; // AccessibilityManagerService resolves services for the current user since the local // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission final List<AccessibilityServiceInfo> services = mAccessibilityManager.getEnabledAccessibilityServiceList( AccessibilityServiceInfo.FEEDBACK_ALL_MASK); + final List<String> a11yButtonTargets = + mAccessibilityManager.getAccessibilityShortcutTargets( + AccessibilityManager.ACCESSIBILITY_BUTTON); + final int requestingServices = a11yButtonTargets.size(); for (int i = services.size() - 1; i >= 0; --i) { AccessibilityServiceInfo info = services.get(i); - if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) { - requestingServices++; - } - if (info.feedbackType != 0 && info.feedbackType != AccessibilityServiceInfo.FEEDBACK_GENERIC) { feedbackEnabled = true; @@ -1114,18 +1097,6 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback private final AccessibilityServicesStateChangeListener mAccessibilityListener = this::updateAccessibilityServicesState; - private class MagnificationContentObserver extends ContentObserver { - - public MagnificationContentObserver(Handler handler) { - super(handler); - } - - @Override - public void onChange(boolean selfChange) { - NavigationBarFragment.this.updateAccessibilityServicesState(mAccessibilityManager); - } - } - private final Consumer<Integer> mRotationWatcher = rotation -> { if (mNavigationBarView != null && mNavigationBarView.needsReorient(rotation)) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java index e11fc1b46a5b..8c947edd9a83 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java @@ -116,7 +116,13 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State */ private void onEntryRemovedInternal(NotificationEntry removed, final StatusBarNotification sbn) { - String groupKey = getGroupKey(sbn); + onEntryRemovedInternal(removed, sbn.getGroupKey(), sbn.isGroup(), + sbn.getNotification().isGroupSummary()); + } + + private void onEntryRemovedInternal(NotificationEntry removed, String notifGroupKey, boolean + isGroup, boolean isGroupSummary) { + String groupKey = getGroupKey(removed.getKey(), notifGroupKey); final NotificationGroup group = mGroupMap.get(groupKey); if (group == null) { // When an app posts 2 different notifications as summary of the same group, then a @@ -125,7 +131,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State // the close future. See b/23676310 for reference. return; } - if (isGroupChild(sbn)) { + if (isGroupChild(removed.getKey(), isGroup, isGroupSummary)) { group.children.remove(removed.getKey()); } else { group.summary = null; @@ -229,7 +235,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State private int getNumberOfIsolatedChildren(String groupKey) { int count = 0; for (StatusBarNotification sbn : mIsolatedEntries.values()) { - if (sbn.getGroupKey().equals(groupKey) && isIsolated(sbn)) { + if (sbn.getGroupKey().equals(groupKey) && isIsolated(sbn.getKey())) { count++; } } @@ -238,31 +244,47 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State private NotificationEntry getIsolatedChild(String groupKey) { for (StatusBarNotification sbn : mIsolatedEntries.values()) { - if (sbn.getGroupKey().equals(groupKey) && isIsolated(sbn)) { + if (sbn.getGroupKey().equals(groupKey) && isIsolated(sbn.getKey())) { return mGroupMap.get(sbn.getKey()).summary; } } return null; } - public void onEntryUpdated(NotificationEntry entry, - StatusBarNotification oldNotification) { - String oldKey = oldNotification.getGroupKey(); - String newKey = entry.getSbn().getGroupKey(); - boolean groupKeysChanged = !oldKey.equals(newKey); - boolean wasGroupChild = isGroupChild(oldNotification); + /** + * Update an entry's group information + * @param entry notification entry to update + * @param oldNotification previous notification info before this update + */ + public void onEntryUpdated(NotificationEntry entry, StatusBarNotification oldNotification) { + onEntryUpdated(entry, oldNotification.getGroupKey(), oldNotification.isGroup(), + oldNotification.getNotification().isGroupSummary()); + } + + /** + * Updates an entry's group information + * @param entry notification entry to update + * @param oldGroupKey the notification's previous group key before this update + * @param oldIsGroup whether this notification was a group before this update + * @param oldIsGroupSummary whether this notification was a group summary before this update + */ + public void onEntryUpdated(NotificationEntry entry, String oldGroupKey, boolean oldIsGroup, + boolean oldIsGroupSummary) { + String newGroupKey = entry.getSbn().getGroupKey(); + boolean groupKeysChanged = !oldGroupKey.equals(newGroupKey); + boolean wasGroupChild = isGroupChild(entry.getKey(), oldIsGroup, oldIsGroupSummary); boolean isGroupChild = isGroupChild(entry.getSbn()); mIsUpdatingUnchangedGroup = !groupKeysChanged && wasGroupChild == isGroupChild; - if (mGroupMap.get(getGroupKey(oldNotification)) != null) { - onEntryRemovedInternal(entry, oldNotification); + if (mGroupMap.get(getGroupKey(entry.getKey(), oldGroupKey)) != null) { + onEntryRemovedInternal(entry, oldGroupKey, oldIsGroup, oldIsGroupSummary); } onEntryAdded(entry); mIsUpdatingUnchangedGroup = false; - if (isIsolated(entry.getSbn())) { + if (isIsolated(entry.getSbn().getKey())) { mIsolatedEntries.put(entry.getKey(), entry.getSbn()); if (groupKeysChanged) { - updateSuppression(mGroupMap.get(oldKey)); - updateSuppression(mGroupMap.get(newKey)); + updateSuppression(mGroupMap.get(oldGroupKey)); + updateSuppression(mGroupMap.get(newGroupKey)); } } else if (!wasGroupChild && isGroupChild) { onEntryBecomingChild(entry); @@ -418,10 +440,14 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State * @return the key of the notification */ public String getGroupKey(StatusBarNotification sbn) { - if (isIsolated(sbn)) { - return sbn.getKey(); + return getGroupKey(sbn.getKey(), sbn.getGroupKey()); + } + + private String getGroupKey(String key, String groupKey) { + if (isIsolated(key)) { + return key; } - return sbn.getGroupKey(); + return groupKey; } /** @return group expansion state after toggling. */ @@ -434,8 +460,8 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State return group.expanded; } - private boolean isIsolated(StatusBarNotification sbn) { - return mIsolatedEntries.containsKey(sbn.getKey()); + private boolean isIsolated(String sbnKey) { + return mIsolatedEntries.containsKey(sbnKey); } /** @@ -445,7 +471,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State * @return true if it is visually a group summary */ public boolean isGroupSummary(StatusBarNotification sbn) { - if (isIsolated(sbn)) { + if (isIsolated(sbn.getKey())) { return true; } return sbn.getNotification().isGroupSummary(); @@ -458,10 +484,14 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State * @return true if it is visually a group child */ public boolean isGroupChild(StatusBarNotification sbn) { - if (isIsolated(sbn)) { + return isGroupChild(sbn.getKey(), sbn.isGroup(), sbn.getNotification().isGroupSummary()); + } + + private boolean isGroupChild(String key, boolean isGroup, boolean isGroupSummary) { + if (isIsolated(key)) { return false; } - return sbn.isGroup() && !sbn.getNotification().isGroupSummary(); + return isGroup && !isGroupSummary; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index 5916180d75ce..f05b58500d50 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -15,6 +15,9 @@ */ package com.android.systemui.statusbar.policy; +import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN; +import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS; + import android.content.Context; import android.content.Intent; import android.database.ContentObserver; @@ -704,7 +707,11 @@ public class MobileSignalController extends SignalController< } mServiceState = state; if (mServiceState != null) { - updateDataNetType(mServiceState.getDataNetworkType()); + NetworkRegistrationInfo regInfo = mServiceState.getNetworkRegistrationInfo( + DOMAIN_PS, TRANSPORT_TYPE_WWAN); + if (regInfo != null) { + updateDataNetType(regInfo.getAccessNetworkTechnology()); + } } updateTelephony(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index 9cb06a5d85c1..13f3a5fbfff7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java @@ -16,6 +16,9 @@ package com.android.systemui.statusbar.policy; +import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN; +import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; @@ -39,6 +42,7 @@ import android.net.wifi.WifiManager; import android.os.Handler; import android.provider.Settings; import android.provider.Settings.Global; +import android.telephony.NetworkRegistrationInfo; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SignalStrength; @@ -358,7 +362,13 @@ public class NetworkControllerBaseTest extends SysuiTestCase { } public void updateDataConnectionState(int dataState, int dataNetType) { - when(mServiceState.getDataNetworkType()).thenReturn(dataNetType); + NetworkRegistrationInfo fakeRegInfo = new NetworkRegistrationInfo.Builder() + .setTransportType(TRANSPORT_TYPE_WWAN) + .setDomain(DOMAIN_PS) + .setAccessNetworkTechnology(dataNetType) + .build(); + when(mServiceState.getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN)) + .thenReturn(fakeRegInfo); mPhoneStateListener.onDataConnectionStateChanged(dataState, dataNetType); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java index 5a5ef8bd21af..f6c750db56b7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java @@ -1,5 +1,8 @@ package com.android.systemui.statusbar.policy; +import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN; +import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.anyInt; @@ -418,7 +421,13 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { // State from NR_5G to NONE NR_STATE_RESTRICTED, showing corresponding icon doReturn(NetworkRegistrationInfo.NR_STATE_RESTRICTED).when(mServiceState).getNrState(); - doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType(); + NetworkRegistrationInfo fakeRegInfo = new NetworkRegistrationInfo.Builder() + .setTransportType(TRANSPORT_TYPE_WWAN) + .setDomain(DOMAIN_PS) + .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE) + .build(); + doReturn(fakeRegInfo).when(mServiceState) + .getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN); mPhoneStateListener.onDataConnectionStateChanged(TelephonyManager.DATA_CONNECTED, TelephonyManager.NETWORK_TYPE_LTE); @@ -480,8 +489,13 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { verifyDataIndicators(TelephonyIcons.ICON_LTE); - when(mServiceState.getDataNetworkType()) - .thenReturn(TelephonyManager.NETWORK_TYPE_HSPA); + NetworkRegistrationInfo fakeRegInfo = new NetworkRegistrationInfo.Builder() + .setTransportType(TRANSPORT_TYPE_WWAN) + .setDomain(DOMAIN_PS) + .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_HSPA) + .build(); + when(mServiceState.getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN)) + .thenReturn(fakeRegInfo); updateServiceState(); verifyDataIndicators(TelephonyIcons.ICON_H); } diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp index d297f3f84189..5bad24ac5619 100644 --- a/packages/Tethering/Android.bp +++ b/packages/Tethering/Android.bp @@ -29,6 +29,7 @@ java_defaults { "netlink-client", "networkstack-aidl-interfaces-unstable-java", "android.hardware.tetheroffload.control-V1.0-java", + "net-utils-framework-common", ], libs: [ "framework-tethering", diff --git a/packages/Tethering/AndroidManifest.xml b/packages/Tethering/AndroidManifest.xml index 5a71eb23abad..c71d0d7bc543 100644 --- a/packages/Tethering/AndroidManifest.xml +++ b/packages/Tethering/AndroidManifest.xml @@ -36,6 +36,7 @@ <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" /> <uses-permission android:name="android.permission.TETHER_PRIVILEGED" /> <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" /> + <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <application diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml index ca290c637773..19e8d696c39a 100644 --- a/packages/Tethering/res/values/config.xml +++ b/packages/Tethering/res/values/config.xml @@ -38,8 +38,9 @@ <!-- List of regexpressions describing the interface (if any) that represent tetherable Wifi P2P interfaces. If the device doesn't want to support tethering over Wifi P2p this - should be empty. An example would be "p2p-p2p.*" --> + should be empty. An example would be "p2p-p2p\\d-.*" --> <string-array translatable="false" name="config_tether_wifi_p2p_regexs"> + <item>"p2p-p2p\\d-.*"</item> </string-array> <!-- List of regexpressions describing the interface (if any) that represent tetherable @@ -100,7 +101,7 @@ <!-- If the mobile hotspot feature requires provisioning, a package name and class name can be provided to launch a supported application that provisions the devices. - EntitlementManager will send an inent to Settings with the specified package name and + EntitlementManager will send an intent to Settings with the specified package name and class name in extras to launch provision app. TODO: note what extras here. diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java index 6ac467e39a9d..4306cf0bd5f3 100644 --- a/packages/Tethering/src/android/net/ip/IpServer.java +++ b/packages/Tethering/src/android/net/ip/IpServer.java @@ -26,7 +26,6 @@ import static android.net.util.TetheringMessageBase.BASE_IPSERVER; import android.net.INetd; import android.net.INetworkStackStatusCallback; -import android.net.INetworkStatsService; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; @@ -176,7 +175,6 @@ public class IpServer extends StateMachine { private final SharedLog mLog; private final INetd mNetd; - private final INetworkStatsService mStatsService; private final Callback mCallback; private final InterfaceController mInterfaceCtrl; @@ -208,12 +206,10 @@ public class IpServer extends StateMachine { public IpServer( String ifaceName, Looper looper, int interfaceType, SharedLog log, - INetd netd, INetworkStatsService statsService, Callback callback, - boolean usingLegacyDhcp, Dependencies deps) { + INetd netd, Callback callback, boolean usingLegacyDhcp, Dependencies deps) { super(ifaceName, looper); mLog = log.forSubComponent(ifaceName); mNetd = netd; - mStatsService = statsService; mCallback = callback; mInterfaceCtrl = new InterfaceController(ifaceName, mNetd, mLog); mIfaceName = ifaceName; @@ -882,12 +878,6 @@ public class IpServer extends StateMachine { // to remove their rules, which generates errors. // Just do the best we can. try { - // About to tear down NAT; gather remaining statistics. - mStatsService.forceUpdate(); - } catch (Exception e) { - mLog.e("Exception in forceUpdate: " + e.toString()); - } - try { mNetd.ipfwdRemoveInterfaceForward(mIfaceName, upstreamIface); } catch (RemoteException | ServiceSpecificException e) { mLog.e("Exception in ipfwdRemoveInterfaceForward: " + e.toString()); diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java index 4e2a2c1c7af7..1cabc8d0b5b7 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java @@ -27,8 +27,6 @@ import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKONWN; import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; import static android.net.TetheringManager.TETHER_ERROR_PROVISION_FAILED; -import static com.android.internal.R.string.config_wifi_tether_enable; - import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -36,7 +34,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.res.Resources; import android.net.util.SharedLog; import android.os.Bundle; import android.os.Handler; @@ -54,6 +51,7 @@ import android.util.SparseIntArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.StateMachine; +import com.android.networkstack.tethering.R; import java.io.PrintWriter; @@ -75,9 +73,7 @@ public class EntitlementManager { "com.android.server.connectivity.tethering.PROVISIONING_RECHECK_ALARM"; private static final String EXTRA_SUBID = "subId"; - // {@link ComponentName} of the Service used to run tether provisioning. - private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString( - Resources.getSystem().getString(config_wifi_tether_enable)); + private final ComponentName mSilentProvisioningService; private static final int MS_PER_HOUR = 60 * 60 * 1000; private static final int EVENT_START_PROVISIONING = 0; private static final int EVENT_STOP_PROVISIONING = 1; @@ -122,6 +118,8 @@ public class EntitlementManager { mHandler = new EntitlementHandler(masterHandler.getLooper()); mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM), null, mHandler); + mSilentProvisioningService = ComponentName.unflattenFromString( + mContext.getResources().getString(R.string.config_wifi_tether_enable)); } public void setOnUiEntitlementFailedListener(final OnUiEntitlementFailedListener listener) { @@ -377,7 +375,7 @@ public class EntitlementManager { intent.putExtra(EXTRA_RUN_PROVISION, true); intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); intent.putExtra(EXTRA_SUBID, subId); - intent.setComponent(TETHER_SERVICE); + intent.setComponent(mSilentProvisioningService); // Only admin user can change tethering and SilentTetherProvisioning don't need to // show UI, it is fine to always start setting's background service as system user. mContext.startService(intent); diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java index ce7c2a669f0a..cc36f4a9c516 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java @@ -16,35 +16,40 @@ package com.android.server.connectivity.tethering; +import static android.net.NetworkStats.DEFAULT_NETWORK_NO; +import static android.net.NetworkStats.METERED_NO; +import static android.net.NetworkStats.ROAMING_NO; import static android.net.NetworkStats.SET_DEFAULT; -import static android.net.NetworkStats.STATS_PER_UID; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; -import static android.net.TrafficStats.UID_TETHERING; +import static android.net.NetworkStats.UID_TETHERING; import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.usage.NetworkStatsManager; import android.content.ContentResolver; -import android.net.ITetheringStatsProvider; import android.net.InetAddresses; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.NetworkStats; +import android.net.NetworkStats.Entry; import android.net.RouteInfo; import android.net.netlink.ConntrackMessage; import android.net.netlink.NetlinkConstants; import android.net.netlink.NetlinkSocket; +import android.net.netstats.provider.AbstractNetworkStatsProvider; +import android.net.netstats.provider.NetworkStatsProviderCallback; import android.net.util.SharedLog; import android.os.Handler; -import android.os.INetworkManagementService; -import android.os.Looper; -import android.os.RemoteException; -import android.os.SystemClock; import android.provider.Settings; import android.system.ErrnoException; import android.system.OsConstants; import android.text.TextUtils; +import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats; @@ -73,13 +78,19 @@ public class OffloadController { private static final String ANYIP = "0.0.0.0"; private static final ForwardedStats EMPTY_STATS = new ForwardedStats(); + @VisibleForTesting + enum StatsType { + STATS_PER_IFACE, + STATS_PER_UID, + } + private enum UpdateType { IF_NEEDED, FORCE }; private final Handler mHandler; private final OffloadHardwareInterface mHwInterface; private final ContentResolver mContentResolver; - private final INetworkManagementService mNms; - private final ITetheringStatsProvider mStatsProvider; + private final @NonNull OffloadTetheringStatsProvider mStatsProvider; + private final @Nullable NetworkStatsProviderCallback mStatsProviderCb; private final SharedLog mLog; private final HashMap<String, LinkProperties> mDownstreams; private boolean mConfigInitialized; @@ -109,22 +120,23 @@ public class OffloadController { private int mNatUpdateNetlinkErrors; public OffloadController(Handler h, OffloadHardwareInterface hwi, - ContentResolver contentResolver, INetworkManagementService nms, SharedLog log) { + ContentResolver contentResolver, NetworkStatsManager nsm, SharedLog log) { mHandler = h; mHwInterface = hwi; mContentResolver = contentResolver; - mNms = nms; mStatsProvider = new OffloadTetheringStatsProvider(); mLog = log.forSubComponent(TAG); mDownstreams = new HashMap<>(); mExemptPrefixes = new HashSet<>(); mLastLocalPrefixStrs = new HashSet<>(); - + NetworkStatsProviderCallback providerCallback = null; try { - mNms.registerTetheringStatsProvider(mStatsProvider, getClass().getSimpleName()); - } catch (RemoteException e) { - mLog.e("Cannot register offload stats provider: " + e); + providerCallback = nsm.registerNetworkStatsProvider( + getClass().getSimpleName(), mStatsProvider); + } catch (RuntimeException e) { + Log.wtf(TAG, "Cannot register offload stats provider: " + e); } + mStatsProviderCb = providerCallback; } /** Start hardware offload. */ @@ -173,7 +185,7 @@ public class OffloadController { // and we need to synchronize stats and limits between // software and hardware forwarding. updateStatsForAllUpstreams(); - forceTetherStatsPoll(); + mStatsProvider.pushTetherStats(); } @Override @@ -186,7 +198,7 @@ public class OffloadController { // limits set take into account any software tethering // traffic that has been happening in the meantime. updateStatsForAllUpstreams(); - forceTetherStatsPoll(); + mStatsProvider.pushTetherStats(); // [2] (Re)Push all state. computeAndPushLocalPrefixes(UpdateType.FORCE); pushAllDownstreamState(); @@ -204,14 +216,11 @@ public class OffloadController { // the HAL queued the callback. // TODO: rev the HAL so that it provides an interface name. - // Fetch current stats, so that when our notification reaches - // NetworkStatsService and triggers a poll, we will respond with - // current data (which will be above the limit that was reached). - // Note that if we just changed upstream, this is unnecessary but harmless. - // The stats for the previous upstream were already updated on this thread - // just after the upstream was changed, so they are also up-to-date. updateStatsForCurrentUpstream(); - forceTetherStatsPoll(); + mStatsProvider.pushTetherStats(); + // Push stats to service does not cause the service react to it immediately. + // Inform the service about limit reached. + if (mStatsProviderCb != null) mStatsProviderCb.onLimitReached(); } @Override @@ -253,42 +262,37 @@ public class OffloadController { return mConfigInitialized && mControlInitialized; } - private class OffloadTetheringStatsProvider extends ITetheringStatsProvider.Stub { - @Override - public NetworkStats getTetherStats(int how) { - // getTetherStats() is the only function in OffloadController that can be called from - // a different thread. Do not attempt to update stats by querying the offload HAL - // synchronously from a different thread than our Handler thread. http://b/64771555. - Runnable updateStats = () -> { - updateStatsForCurrentUpstream(); - }; - if (Looper.myLooper() == mHandler.getLooper()) { - updateStats.run(); - } else { - mHandler.post(updateStats); - } - - NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0); - NetworkStats.Entry entry = new NetworkStats.Entry(); - entry.set = SET_DEFAULT; - entry.tag = TAG_NONE; - entry.uid = (how == STATS_PER_UID) ? UID_TETHERING : UID_ALL; - - for (Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) { - ForwardedStats value = kv.getValue(); - entry.iface = kv.getKey(); - entry.rxBytes = value.rxBytes; - entry.txBytes = value.txBytes; - stats.addEntry(entry); + @VisibleForTesting + class OffloadTetheringStatsProvider extends AbstractNetworkStatsProvider { + // These stats must only ever be touched on the handler thread. + @NonNull + private NetworkStats mIfaceStats = new NetworkStats(0L, 0); + @NonNull + private NetworkStats mUidStats = new NetworkStats(0L, 0); + + @VisibleForTesting + @NonNull + NetworkStats getTetherStats(@NonNull StatsType how) { + NetworkStats stats = new NetworkStats(0L, 0); + final int uid = (how == StatsType.STATS_PER_UID) ? UID_TETHERING : UID_ALL; + + for (final Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) { + final ForwardedStats value = kv.getValue(); + final Entry entry = new Entry(kv.getKey(), uid, SET_DEFAULT, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, value.rxBytes, 0L, value.txBytes, 0L, 0L); + stats = stats.addValues(entry); } return stats; } @Override - public void setInterfaceQuota(String iface, long quotaBytes) { + public void setLimit(String iface, long quotaBytes) { + mLog.i("setLimit: " + iface + "," + quotaBytes); + // Listen for all iface is necessary since upstream might be changed after limit + // is set. mHandler.post(() -> { - if (quotaBytes == ITetheringStatsProvider.QUOTA_UNLIMITED) { + if (quotaBytes == QUOTA_UNLIMITED) { mInterfaceQuotas.remove(iface); } else { mInterfaceQuotas.put(iface, quotaBytes); @@ -296,6 +300,42 @@ public class OffloadController { maybeUpdateDataLimit(iface); }); } + + /** + * Push stats to service, but does not cause a force polling. Note that this can only be + * called on the handler thread. + */ + public void pushTetherStats() { + // TODO: remove the accumulated stats and report the diff from HAL directly. + if (null == mStatsProviderCb) return; + final NetworkStats ifaceDiff = + getTetherStats(StatsType.STATS_PER_IFACE).subtract(mIfaceStats); + final NetworkStats uidDiff = + getTetherStats(StatsType.STATS_PER_UID).subtract(mUidStats); + try { + mStatsProviderCb.onStatsUpdated(0 /* token */, ifaceDiff, uidDiff); + mIfaceStats = mIfaceStats.add(ifaceDiff); + mUidStats = mUidStats.add(uidDiff); + } catch (RuntimeException e) { + mLog.e("Cannot report network stats: ", e); + } + } + + @Override + public void requestStatsUpdate(int token) { + mLog.i("requestStatsUpdate: " + token); + // Do not attempt to update stats by querying the offload HAL + // synchronously from a different thread than the Handler thread. http://b/64771555. + mHandler.post(() -> { + updateStatsForCurrentUpstream(); + pushTetherStats(); + }); + } + + @Override + public void setAlert(long quotaBytes) { + // TODO: Ask offload HAL to notify alert without stopping traffic. + } } private String currentUpstreamInterface() { @@ -353,14 +393,6 @@ public class OffloadController { } } - private void forceTetherStatsPoll() { - try { - mNms.tetherLimitReached(mStatsProvider); - } catch (RemoteException e) { - mLog.e("Cannot report data limit reached: " + e); - } - } - /** Set current tethering upstream LinkProperties. */ public void setUpstreamLinkProperties(LinkProperties lp) { if (!started() || Objects.equals(mUpstreamLinkProperties, lp)) return; diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java index 4a8ef1f92754..90b9d3f148dc 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java @@ -29,6 +29,8 @@ import android.os.Handler; import android.os.RemoteException; import android.system.OsConstants; +import com.android.internal.annotations.VisibleForTesting; + import java.util.ArrayList; @@ -91,6 +93,12 @@ public class OffloadHardwareInterface { txBytes = 0; } + @VisibleForTesting + public ForwardedStats(long rxBytes, long txBytes) { + this.rxBytes = rxBytes; + this.txBytes = txBytes; + } + /** Add Tx/Rx bytes. */ public void add(ForwardedStats other) { rxBytes += other.rxBytes; diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index 038d7ae72a1a..37e0cc107b58 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.hardware.usb.UsbManager.USB_CONFIGURED; import static android.hardware.usb.UsbManager.USB_CONNECTED; import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS; +import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO; import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED; @@ -53,6 +54,7 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; +import android.app.usage.NetworkStatsManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothPan; import android.bluetooth.BluetoothProfile; @@ -63,9 +65,8 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; import android.hardware.usb.UsbManager; +import android.net.ConnectivityManager; import android.net.INetd; -import android.net.INetworkPolicyManager; -import android.net.INetworkStatsService; import android.net.ITetheringEventCallback; import android.net.IpPrefix; import android.net.LinkAddress; @@ -176,8 +177,6 @@ public class Tethering { private final Context mContext; private final ArrayMap<String, TetherState> mTetherStates; private final BroadcastReceiver mStateReceiver; - private final INetworkStatsService mStatsService; - private final INetworkPolicyManager mPolicyManager; private final Looper mLooper; private final StateMachine mTetherMasterSM; private final OffloadController mOffloadController; @@ -207,13 +206,12 @@ public class Tethering { private boolean mWifiTetherRequested; private Network mTetherUpstream; private TetherStatesParcel mTetherStatesParcel; + private boolean mDataSaverEnabled = false; public Tethering(TetheringDependencies deps) { mLog.mark("Tethering.constructed"); mDeps = deps; mContext = mDeps.getContext(); - mStatsService = mDeps.getINetworkStatsService(); - mPolicyManager = mDeps.getINetworkPolicyManager(); mNetd = mDeps.getINetd(mContext); mLooper = mDeps.getTetheringLooper(); @@ -224,10 +222,12 @@ public class Tethering { mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper, deps); mTetherMasterSM.start(); + final NetworkStatsManager statsManager = + (NetworkStatsManager) mContext.getSystemService(Context.NETWORK_STATS_SERVICE); mHandler = mTetherMasterSM.getHandler(); mOffloadController = new OffloadController(mHandler, mDeps.getOffloadHardwareInterface(mHandler, mLog), mContext.getContentResolver(), - mDeps.getINetworkManagementService(), mLog); + statsManager, mLog); mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK); mForwardedDownstreams = new HashSet<>(); @@ -264,7 +264,7 @@ public class Tethering { } final UserManager userManager = (UserManager) mContext.getSystemService( - Context.USER_SERVICE); + Context.USER_SERVICE); mTetheringRestriction = new UserRestrictionActionListener(userManager, this); final TetheringThreadExecutor executor = new TetheringThreadExecutor(mHandler); mActiveDataSubIdListener = new ActiveDataSubIdListener(executor); @@ -288,6 +288,7 @@ public class Tethering { filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED); + filter.addAction(ACTION_RESTRICT_BACKGROUND_CHANGED); mContext.registerReceiver(mStateReceiver, filter, null, handler); } @@ -484,7 +485,7 @@ public class Tethering { } private void setBluetoothTethering(final boolean enable, final ResultReceiver receiver) { - final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + final BluetoothAdapter adapter = mDeps.getBluetoothAdapter(); if (adapter == null || !adapter.isEnabled()) { Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: " + (adapter == null)); @@ -740,8 +741,7 @@ public class Tethering { .setContentIntent(pi); mLastNotificationId = id; - notificationManager.notify(null, mLastNotificationId, - mTetheredNotificationBuilder.buildInto(new Notification())); + notificationManager.notify(null, mLastNotificationId, mTetheredNotificationBuilder.build()); } @VisibleForTesting @@ -775,6 +775,9 @@ public class Tethering { } else if (action.equals(UserManager.ACTION_USER_RESTRICTIONS_CHANGED)) { mLog.log("OBSERVED user restrictions changed"); handleUserRestrictionAction(); + } else if (action.equals(ACTION_RESTRICT_BACKGROUND_CHANGED)) { + mLog.log("OBSERVED data saver changed"); + handleDataSaverChanged(); } } @@ -885,6 +888,20 @@ public class Tethering { private void handleUserRestrictionAction() { mTetheringRestriction.onUserRestrictionsChanged(); } + + private void handleDataSaverChanged() { + final ConnectivityManager connMgr = (ConnectivityManager) mContext.getSystemService( + Context.CONNECTIVITY_SERVICE); + final boolean isDataSaverEnabled = connMgr.getRestrictBackgroundStatus() + != ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED; + + if (mDataSaverEnabled == isDataSaverEnabled) return; + + mDataSaverEnabled = isDataSaverEnabled; + if (mDataSaverEnabled) { + untetherAll(); + } + } } @VisibleForTesting @@ -1982,15 +1999,6 @@ public class Tethering { mLog.log(String.format("OBSERVED iface=%s state=%s error=%s", iface, state, error)); - try { - // Notify that we're tethering (or not) this interface. - // This is how data saver for instance knows if the user explicitly - // turned on tethering (thus keeping us from being in data saver mode). - mPolicyManager.onTetheringChanged(iface, state == IpServer.STATE_TETHERED); - } catch (RemoteException e) { - // Not really very much we can do here. - } - // If TetherMasterSM is in ErrorState, TetherMasterSM stays there. // Thus we give a chance for TetherMasterSM to recover to InitialState // by sending CMD_CLEAR_ERROR @@ -2054,7 +2062,7 @@ public class Tethering { mLog.log("adding TetheringInterfaceStateMachine for: " + iface); final TetherState tetherState = new TetherState( - new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mStatsService, + new IpServer(iface, mLooper, interfaceType, mLog, mNetd, makeControlCallback(), mConfig.enableLegacyDhcpServer, mDeps.getIpServerDependencies())); mTetherStates.put(iface, tetherState); diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java index dbe789288c6f..068c346fbfc1 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java @@ -23,18 +23,6 @@ import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; -import static com.android.internal.R.array.config_mobile_hotspot_provision_app; -import static com.android.internal.R.array.config_tether_bluetooth_regexs; -import static com.android.internal.R.array.config_tether_dhcp_range; -import static com.android.internal.R.array.config_tether_upstream_types; -import static com.android.internal.R.array.config_tether_usb_regexs; -import static com.android.internal.R.array.config_tether_wifi_p2p_regexs; -import static com.android.internal.R.array.config_tether_wifi_regexs; -import static com.android.internal.R.bool.config_tether_upstream_automatic; -import static com.android.internal.R.integer.config_mobile_hotspot_provision_check_period; -import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui; -import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server; - import android.content.Context; import android.content.res.Resources; import android.net.TetheringConfigurationParcel; @@ -45,6 +33,7 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; +import com.android.networkstack.tethering.R; import java.io.PrintWriter; import java.util.ArrayList; @@ -113,27 +102,30 @@ public class TetheringConfiguration { activeDataSubId = id; Resources res = getResources(ctx, activeDataSubId); - tetherableUsbRegexs = getResourceStringArray(res, config_tether_usb_regexs); + tetherableUsbRegexs = getResourceStringArray(res, R.array.config_tether_usb_regexs); // TODO: Evaluate deleting this altogether now that Wi-Fi always passes // us an interface name. Careful consideration needs to be given to // implications for Settings and for provisioning checks. - tetherableWifiRegexs = getResourceStringArray(res, config_tether_wifi_regexs); - tetherableWifiP2pRegexs = getResourceStringArray(res, config_tether_wifi_p2p_regexs); - tetherableBluetoothRegexs = getResourceStringArray(res, config_tether_bluetooth_regexs); + tetherableWifiRegexs = getResourceStringArray(res, R.array.config_tether_wifi_regexs); + tetherableWifiP2pRegexs = getResourceStringArray( + res, R.array.config_tether_wifi_p2p_regexs); + tetherableBluetoothRegexs = getResourceStringArray( + res, R.array.config_tether_bluetooth_regexs); isDunRequired = checkDunRequired(ctx); - chooseUpstreamAutomatically = getResourceBoolean(res, config_tether_upstream_automatic); + chooseUpstreamAutomatically = getResourceBoolean( + res, R.bool.config_tether_upstream_automatic); preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, isDunRequired); legacyDhcpRanges = getLegacyDhcpRanges(res); defaultIPv4DNS = copy(DEFAULT_IPV4_DNS); enableLegacyDhcpServer = getEnableLegacyDhcpServer(res); - provisioningApp = getResourceStringArray(res, config_mobile_hotspot_provision_app); + provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app); provisioningAppNoUi = getProvisioningAppNoUi(res); provisioningCheckPeriod = getResourceInteger(res, - config_mobile_hotspot_provision_check_period, + R.integer.config_mobile_hotspot_provision_check_period, 0 /* No periodic re-check */); configLog.log(toString()); @@ -248,7 +240,7 @@ public class TetheringConfiguration { } private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) { - final int[] ifaceTypes = res.getIntArray(config_tether_upstream_types); + final int[] ifaceTypes = res.getIntArray(R.array.config_tether_upstream_types); final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length); for (int i : ifaceTypes) { switch (i) { @@ -298,7 +290,7 @@ public class TetheringConfiguration { } private static String[] getLegacyDhcpRanges(Resources res) { - final String[] fromResource = getResourceStringArray(res, config_tether_dhcp_range); + final String[] fromResource = getResourceStringArray(res, R.array.config_tether_dhcp_range); if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) { return fromResource; } @@ -307,7 +299,7 @@ public class TetheringConfiguration { private static String getProvisioningAppNoUi(Resources res) { try { - return res.getString(config_mobile_hotspot_provision_app_no_ui); + return res.getString(R.string.config_mobile_hotspot_provision_app_no_ui); } catch (Resources.NotFoundException e) { return ""; } @@ -339,7 +331,7 @@ public class TetheringConfiguration { } private boolean getEnableLegacyDhcpServer(final Resources res) { - return getResourceBoolean(res, config_tether_enable_legacy_dhcp_server) + return getResourceBoolean(res, R.bool.config_tether_enable_legacy_dhcp_server) || getDeviceConfigBoolean(TETHER_ENABLE_LEGACY_DHCP_SERVER); } diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java index b16b3294a112..e019c3aca26a 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java @@ -16,18 +16,15 @@ package com.android.server.connectivity.tethering; +import android.bluetooth.BluetoothAdapter; import android.content.Context; import android.net.INetd; -import android.net.INetworkPolicyManager; -import android.net.INetworkStatsService; import android.net.NetworkRequest; import android.net.ip.IpServer; import android.net.util.SharedLog; import android.os.Handler; import android.os.IBinder; -import android.os.INetworkManagementService; import android.os.Looper; -import android.os.ServiceManager; import com.android.internal.util.StateMachine; @@ -97,33 +94,6 @@ public abstract class TetheringDependencies { } /** - * Get a reference to INetworkManagementService to registerTetheringStatsProvider from - * OffloadController. Note: This should be removed soon by Usage refactor work in R - * development cycle. - */ - public INetworkManagementService getINetworkManagementService() { - return INetworkManagementService.Stub.asInterface( - ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)); - } - - /** - * Get a reference to INetworkStatsService to force update tethering usage. - * Note: This should be removed in R development cycle. - */ - public INetworkStatsService getINetworkStatsService() { - return INetworkStatsService.Stub.asInterface( - ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); - } - - /** - * Get a reference to INetworkPolicyManager to be used by tethering. - */ - public INetworkPolicyManager getINetworkPolicyManager() { - return INetworkPolicyManager.Stub.asInterface( - ServiceManager.getService(Context.NETWORK_POLICY_SERVICE)); - } - - /** * Get a reference to INetd to be used by tethering. */ public INetd getINetd(Context context) { @@ -140,4 +110,9 @@ public abstract class TetheringDependencies { * Get Context of TetheringSerice. */ public abstract Context getContext(); + + /** + * Get a reference to BluetoothAdapter to be used by tethering. + */ + public abstract BluetoothAdapter getBluetoothAdapter(); } diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java index d5cdd8a004dc..4dd68301f9fa 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java @@ -22,6 +22,8 @@ import android.net.NetworkCapabilities; import android.net.RouteInfo; import android.net.util.InterfaceSet; +import com.android.net.module.util.NetUtils; + import java.net.InetAddress; import java.net.UnknownHostException; @@ -85,7 +87,7 @@ public final class TetheringInterfaceUtils { private static String getInterfaceForDestination(LinkProperties lp, InetAddress dst) { final RouteInfo ri = (lp != null) - ? RouteInfo.selectBestRoute(lp.getAllRoutes(), dst) + ? NetUtils.selectBestRoute(lp.getAllRoutes(), dst) : null; return (ri != null) ? ri.getInterface() : null; } diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java index e4e4a090603d..cb7d3920e693 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java @@ -24,6 +24,7 @@ import static android.net.TetheringManager.TETHER_ERROR_UNSUPPORTED; import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR; import android.app.Service; +import android.bluetooth.BluetoothAdapter; import android.content.Context; import android.content.Intent; import android.net.IIntResultListener; @@ -42,7 +43,6 @@ import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.ResultReceiver; -import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserManager; import android.provider.Settings; @@ -363,7 +363,7 @@ public class TetheringService extends Service { IBinder connector; try { final long before = System.currentTimeMillis(); - while ((connector = ServiceManager.getService( + while ((connector = (IBinder) mContext.getSystemService( Context.NETWORK_STACK_SERVICE)) == null) { if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) { Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector"); @@ -377,6 +377,11 @@ public class TetheringService extends Service { } return INetworkStackConnector.Stub.asInterface(connector); } + + @Override + public BluetoothAdapter getBluetoothAdapter() { + return BluetoothAdapter.getDefaultAdapter(); + } }; } return mDeps; diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index 65a0ac13a84b..1f50b6bf7fb3 100644 --- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -52,7 +52,6 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.net.INetd; -import android.net.INetworkStatsService; import android.net.InterfaceConfigurationParcel; import android.net.IpPrefix; import android.net.LinkAddress; @@ -99,7 +98,6 @@ public class IpServerTest { private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000; @Mock private INetd mNetd; - @Mock private INetworkStatsService mStatsService; @Mock private IpServer.Callback mCallback; @Mock private SharedLog mSharedLog; @Mock private IDhcpServer mDhcpServer; @@ -139,13 +137,13 @@ public class IpServerTest { mInterfaceConfiguration.prefixLength = BLUETOOTH_DHCP_PREFIX_LENGTH; } mIpServer = new IpServer( - IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mStatsService, + IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mCallback, usingLegacyDhcp, mDependencies); mIpServer.start(); // Starting the state machine always puts us in a consistent state and notifies // the rest of the world that we've changed from an unknown to available state. mLooper.dispatchAll(); - reset(mNetd, mStatsService, mCallback); + reset(mNetd, mCallback); when(mRaDaemon.start()).thenReturn(true); } @@ -162,7 +160,7 @@ public class IpServerTest { if (upstreamIface != null) { dispatchTetherConnectionChanged(upstreamIface); } - reset(mNetd, mStatsService, mCallback); + reset(mNetd, mCallback); } @Before public void setUp() throws Exception { @@ -173,13 +171,13 @@ public class IpServerTest { @Test public void startsOutAvailable() { mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog, - mNetd, mStatsService, mCallback, false /* usingLegacyDhcp */, mDependencies); + mNetd, mCallback, false /* usingLegacyDhcp */, mDependencies); mIpServer.start(); mLooper.dispatchAll(); verify(mCallback).updateInterfaceState( mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mCallback, mNetd, mStatsService); + verifyNoMoreInteractions(mCallback, mNetd); } @Test @@ -198,7 +196,7 @@ public class IpServerTest { // None of these commands should trigger us to request action from // the rest of the system. dispatchCommand(command); - verifyNoMoreInteractions(mNetd, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mCallback); } } @@ -210,7 +208,7 @@ public class IpServerTest { verify(mCallback).updateInterfaceState( mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR); verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNetd, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mCallback); } @Test @@ -228,7 +226,7 @@ public class IpServerTest { mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR); inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNetd, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mCallback); } @Test @@ -236,7 +234,7 @@ public class IpServerTest { initTetheredStateMachine(TETHERING_BLUETOOTH, null); dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED); - InOrder inOrder = inOrder(mNetd, mStatsService, mCallback); + InOrder inOrder = inOrder(mNetd, mCallback); inOrder.verify(mNetd).tetherApplyDnsInterfaces(); inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME); inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME); @@ -245,7 +243,7 @@ public class IpServerTest { mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNetd, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mCallback); } @Test @@ -265,7 +263,7 @@ public class IpServerTest { inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), mLinkPropertiesCaptor.capture()); assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue()); - verifyNoMoreInteractions(mNetd, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mCallback); } @Test @@ -285,7 +283,7 @@ public class IpServerTest { inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), mLinkPropertiesCaptor.capture()); assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue()); - verifyNoMoreInteractions(mNetd, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mCallback); } @Test @@ -298,7 +296,7 @@ public class IpServerTest { InOrder inOrder = inOrder(mNetd); inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); - verifyNoMoreInteractions(mNetd, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mCallback); } @Test @@ -306,13 +304,12 @@ public class IpServerTest { initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE); dispatchTetherConnectionChanged(UPSTREAM_IFACE2); - InOrder inOrder = inOrder(mNetd, mStatsService); - inOrder.verify(mStatsService).forceUpdate(); + InOrder inOrder = inOrder(mNetd); inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); - verifyNoMoreInteractions(mNetd, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mCallback); } @Test @@ -322,12 +319,10 @@ public class IpServerTest { doThrow(RemoteException.class).when(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); dispatchTetherConnectionChanged(UPSTREAM_IFACE2); - InOrder inOrder = inOrder(mNetd, mStatsService); - inOrder.verify(mStatsService).forceUpdate(); + InOrder inOrder = inOrder(mNetd); inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mStatsService).forceUpdate(); inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2); } @@ -340,13 +335,11 @@ public class IpServerTest { IFACE_NAME, UPSTREAM_IFACE2); dispatchTetherConnectionChanged(UPSTREAM_IFACE2); - InOrder inOrder = inOrder(mNetd, mStatsService); - inOrder.verify(mStatsService).forceUpdate(); + InOrder inOrder = inOrder(mNetd); inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mStatsService).forceUpdate(); inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2); } @@ -356,8 +349,7 @@ public class IpServerTest { initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE); dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED); - InOrder inOrder = inOrder(mNetd, mStatsService, mCallback); - inOrder.verify(mStatsService).forceUpdate(); + InOrder inOrder = inOrder(mNetd, mCallback); inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNetd).tetherApplyDnsInterfaces(); @@ -368,7 +360,7 @@ public class IpServerTest { mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNetd, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mCallback); } @Test @@ -435,11 +427,11 @@ public class IpServerTest { public void ignoresDuplicateUpstreamNotifications() throws Exception { initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); - verifyNoMoreInteractions(mNetd, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mCallback); for (int i = 0; i < 5; i++) { dispatchTetherConnectionChanged(UPSTREAM_IFACE); - verifyNoMoreInteractions(mNetd, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mCallback); } } diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java index 79bba7f6e663..4f0746199786 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java @@ -27,7 +27,6 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; -import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -56,10 +55,10 @@ import android.telephony.CarrierConfigManager; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.R; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.internal.util.test.BroadcastInterceptingContext; +import com.android.networkstack.tethering.R; import org.junit.After; import org.junit.Before; @@ -157,16 +156,18 @@ public final class EntitlementManagerTest { eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); when(mResources.getStringArray(R.array.config_tether_dhcp_range)) - .thenReturn(new String[0]); + .thenReturn(new String[0]); when(mResources.getStringArray(R.array.config_tether_usb_regexs)) - .thenReturn(new String[0]); + .thenReturn(new String[0]); when(mResources.getStringArray(R.array.config_tether_wifi_regexs)) - .thenReturn(new String[0]); + .thenReturn(new String[0]); when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)) - .thenReturn(new String[0]); + .thenReturn(new String[0]); when(mResources.getIntArray(R.array.config_tether_upstream_types)) - .thenReturn(new int[0]); - when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false); + .thenReturn(new int[0]); + when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( + false); + when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn(""); when(mLog.forSubComponent(anyString())).thenReturn(mLog); mMockContext = new MockContext(mContext); diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java index 7886ca6c132d..7e62e5aca993 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java @@ -16,21 +16,26 @@ package com.android.server.connectivity.tethering; +import static android.net.NetworkStats.DEFAULT_NETWORK_NO; +import static android.net.NetworkStats.METERED_NO; +import static android.net.NetworkStats.ROAMING_NO; import static android.net.NetworkStats.SET_DEFAULT; -import static android.net.NetworkStats.STATS_PER_IFACE; -import static android.net.NetworkStats.STATS_PER_UID; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; +import static android.net.NetworkStats.UID_TETHERING; import static android.net.RouteInfo.RTN_UNICAST; -import static android.net.TrafficStats.UID_TETHERING; import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; +import static com.android.server.connectivity.tethering.OffloadController.StatsType.STATS_PER_IFACE; +import static com.android.server.connectivity.tethering.OffloadController.StatsType.STATS_PER_UID; import static com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats; import static com.android.testutils.MiscAssertsKt.assertContainsAll; import static com.android.testutils.MiscAssertsKt.assertThrows; +import static com.android.testutils.NetworkStatsUtilsKt.orderInsensitiveEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyObject; @@ -39,11 +44,14 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import android.annotation.NonNull; +import android.app.usage.NetworkStatsManager; import android.content.Context; import android.content.pm.ApplicationInfo; import android.net.ITetheringStatsProvider; @@ -51,10 +59,12 @@ import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.NetworkStats; +import android.net.NetworkStats.Entry; import android.net.RouteInfo; +import android.net.netstats.provider.AbstractNetworkStatsProvider; +import android.net.netstats.provider.NetworkStatsProviderCallback; import android.net.util.SharedLog; import android.os.Handler; -import android.os.INetworkManagementService; import android.os.Looper; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; @@ -97,11 +107,13 @@ public class OffloadControllerTest { @Mock private OffloadHardwareInterface mHardware; @Mock private ApplicationInfo mApplicationInfo; @Mock private Context mContext; - @Mock private INetworkManagementService mNMService; + @Mock private NetworkStatsManager mStatsManager; + @Mock private NetworkStatsProviderCallback mTetherStatsProviderCb; private final ArgumentCaptor<ArrayList> mStringArrayCaptor = ArgumentCaptor.forClass(ArrayList.class); - private final ArgumentCaptor<ITetheringStatsProvider.Stub> mTetherStatsProviderCaptor = - ArgumentCaptor.forClass(ITetheringStatsProvider.Stub.class); + private final ArgumentCaptor<OffloadController.OffloadTetheringStatsProvider> + mTetherStatsProviderCaptor = + ArgumentCaptor.forClass(OffloadController.OffloadTetheringStatsProvider.class); private final ArgumentCaptor<OffloadHardwareInterface.ControlCallback> mControlCallbackCaptor = ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class); private MockContentResolver mContentResolver; @@ -114,6 +126,8 @@ public class OffloadControllerTest { mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); when(mContext.getContentResolver()).thenReturn(mContentResolver); FakeSettingsProvider.clearSettingsProvider(); + when(mStatsManager.registerNetworkStatsProvider(anyString(), any())) + .thenReturn(mTetherStatsProviderCb); } @After public void tearDown() throws Exception { @@ -139,9 +153,9 @@ public class OffloadControllerTest { private OffloadController makeOffloadController() throws Exception { OffloadController offload = new OffloadController(new Handler(Looper.getMainLooper()), - mHardware, mContentResolver, mNMService, new SharedLog("test")); - verify(mNMService).registerTetheringStatsProvider( - mTetherStatsProviderCaptor.capture(), anyString()); + mHardware, mContentResolver, mStatsManager, new SharedLog("test")); + verify(mStatsManager).registerNetworkStatsProvider(anyString(), + mTetherStatsProviderCaptor.capture()); return offload; } @@ -384,12 +398,11 @@ public class OffloadControllerTest { inOrder.verifyNoMoreInteractions(); } - private void assertNetworkStats(String iface, ForwardedStats stats, NetworkStats.Entry entry) { - assertEquals(iface, entry.iface); - assertEquals(stats.rxBytes, entry.rxBytes); - assertEquals(stats.txBytes, entry.txBytes); - assertEquals(SET_DEFAULT, entry.set); - assertEquals(TAG_NONE, entry.tag); + private static @NonNull Entry buildTestEntry(@NonNull OffloadController.StatsType how, + @NonNull String iface, long rxBytes, long txBytes) { + return new Entry(iface, how == STATS_PER_IFACE ? UID_ALL : UID_TETHERING, SET_DEFAULT, + TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes, 0L, + txBytes, 0L, 0L); } @Test @@ -400,19 +413,16 @@ public class OffloadControllerTest { final OffloadController offload = makeOffloadController(); offload.start(); + final OffloadController.OffloadTetheringStatsProvider provider = + mTetherStatsProviderCaptor.getValue(); + final String ethernetIface = "eth1"; final String mobileIface = "rmnet_data0"; - ForwardedStats ethernetStats = new ForwardedStats(); - ethernetStats.rxBytes = 12345; - ethernetStats.txBytes = 54321; - - ForwardedStats mobileStats = new ForwardedStats(); - mobileStats.rxBytes = 999; - mobileStats.txBytes = 99999; - - when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(ethernetStats); - when(mHardware.getForwardedStats(eq(mobileIface))).thenReturn(mobileStats); + when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( + new ForwardedStats(12345, 54321)); + when(mHardware.getForwardedStats(eq(mobileIface))).thenReturn( + new ForwardedStats(999, 99999)); InOrder inOrder = inOrder(mHardware); @@ -432,10 +442,35 @@ public class OffloadControllerTest { // Expect that we fetch stats from the previous upstream. inOrder.verify(mHardware, times(1)).getForwardedStats(eq(mobileIface)); - ethernetStats = new ForwardedStats(); - ethernetStats.rxBytes = 100000; - ethernetStats.txBytes = 100000; - when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(ethernetStats); + // Verify that the fetched stats are stored. + final NetworkStats ifaceStats = provider.getTetherStats(STATS_PER_IFACE); + final NetworkStats uidStats = provider.getTetherStats(STATS_PER_UID); + final NetworkStats expectedIfaceStats = new NetworkStats(0L, 2) + .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999)) + .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 12345, 54321)); + + final NetworkStats expectedUidStats = new NetworkStats(0L, 2) + .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999)) + .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321)); + + assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStats)); + assertTrue(orderInsensitiveEquals(expectedUidStats, uidStats)); + + final ArgumentCaptor<NetworkStats> ifaceStatsCaptor = ArgumentCaptor.forClass( + NetworkStats.class); + final ArgumentCaptor<NetworkStats> uidStatsCaptor = ArgumentCaptor.forClass( + NetworkStats.class); + + // Force pushing stats update to verify the stats reported. + provider.pushTetherStats(); + verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), + ifaceStatsCaptor.capture(), uidStatsCaptor.capture()); + assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStatsCaptor.getValue())); + assertTrue(orderInsensitiveEquals(expectedUidStats, uidStatsCaptor.getValue())); + + + when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( + new ForwardedStats(100000, 100000)); offload.setUpstreamLinkProperties(null); // Expect that we first clear the HAL's upstream parameters. inOrder.verify(mHardware, times(1)).setUpstreamParameters( @@ -443,37 +478,38 @@ public class OffloadControllerTest { // Expect that we fetch stats from the previous upstream. inOrder.verify(mHardware, times(1)).getForwardedStats(eq(ethernetIface)); - ITetheringStatsProvider provider = mTetherStatsProviderCaptor.getValue(); - NetworkStats stats = provider.getTetherStats(STATS_PER_IFACE); - NetworkStats perUidStats = provider.getTetherStats(STATS_PER_UID); - waitForIdle(); // There is no current upstream, so no stats are fetched. inOrder.verify(mHardware, never()).getForwardedStats(any()); inOrder.verifyNoMoreInteractions(); - assertEquals(2, stats.size()); - assertEquals(2, perUidStats.size()); - - NetworkStats.Entry entry = null; - for (int i = 0; i < stats.size(); i++) { - assertEquals(UID_ALL, stats.getValues(i, entry).uid); - assertEquals(UID_TETHERING, perUidStats.getValues(i, entry).uid); - } - - int ethernetPosition = ethernetIface.equals(stats.getValues(0, entry).iface) ? 0 : 1; - int mobilePosition = 1 - ethernetPosition; - - entry = stats.getValues(mobilePosition, entry); - assertNetworkStats(mobileIface, mobileStats, entry); - entry = perUidStats.getValues(mobilePosition, entry); - assertNetworkStats(mobileIface, mobileStats, entry); - - ethernetStats.rxBytes = 12345 + 100000; - ethernetStats.txBytes = 54321 + 100000; - entry = stats.getValues(ethernetPosition, entry); - assertNetworkStats(ethernetIface, ethernetStats, entry); - entry = perUidStats.getValues(ethernetPosition, entry); - assertNetworkStats(ethernetIface, ethernetStats, entry); + // Verify that the stored stats is accumulated. + final NetworkStats ifaceStatsAccu = provider.getTetherStats(STATS_PER_IFACE); + final NetworkStats uidStatsAccu = provider.getTetherStats(STATS_PER_UID); + final NetworkStats expectedIfaceStatsAccu = new NetworkStats(0L, 2) + .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999)) + .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 112345, 154321)); + + final NetworkStats expectedUidStatsAccu = new NetworkStats(0L, 2) + .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999)) + .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321)); + + assertTrue(orderInsensitiveEquals(expectedIfaceStatsAccu, ifaceStatsAccu)); + assertTrue(orderInsensitiveEquals(expectedUidStatsAccu, uidStatsAccu)); + + // Verify that only diff of stats is reported. + reset(mTetherStatsProviderCb); + provider.pushTetherStats(); + final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2) + .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0)) + .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 100000, 100000)); + + final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2) + .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0)) + .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000)); + verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), + ifaceStatsCaptor.capture(), uidStatsCaptor.capture()); + assertTrue(orderInsensitiveEquals(expectedIfaceStatsDiff, ifaceStatsCaptor.getValue())); + assertTrue(orderInsensitiveEquals(expectedUidStatsDiff, uidStatsCaptor.getValue())); } @Test @@ -493,19 +529,19 @@ public class OffloadControllerTest { lp.setInterfaceName(ethernetIface); offload.setUpstreamLinkProperties(lp); - ITetheringStatsProvider provider = mTetherStatsProviderCaptor.getValue(); + AbstractNetworkStatsProvider provider = mTetherStatsProviderCaptor.getValue(); final InOrder inOrder = inOrder(mHardware); when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true); when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true); // Applying an interface quota to the current upstream immediately sends it to the hardware. - provider.setInterfaceQuota(ethernetIface, ethernetLimit); + provider.setLimit(ethernetIface, ethernetLimit); waitForIdle(); inOrder.verify(mHardware).setDataLimit(ethernetIface, ethernetLimit); inOrder.verifyNoMoreInteractions(); // Applying an interface quota to another upstream does not take any immediate action. - provider.setInterfaceQuota(mobileIface, mobileLimit); + provider.setLimit(mobileIface, mobileLimit); waitForIdle(); inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong()); @@ -518,7 +554,7 @@ public class OffloadControllerTest { // Setting a limit of ITetheringStatsProvider.QUOTA_UNLIMITED causes the limit to be set // to Long.MAX_VALUE. - provider.setInterfaceQuota(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED); + provider.setLimit(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED); waitForIdle(); inOrder.verify(mHardware).setDataLimit(mobileIface, Long.MAX_VALUE); @@ -526,7 +562,7 @@ public class OffloadControllerTest { when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(false); lp.setInterfaceName(ethernetIface); offload.setUpstreamLinkProperties(lp); - provider.setInterfaceQuota(mobileIface, mobileLimit); + provider.setLimit(mobileIface, mobileLimit); waitForIdle(); inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong()); @@ -535,7 +571,7 @@ public class OffloadControllerTest { when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(false); lp.setInterfaceName(mobileIface); offload.setUpstreamLinkProperties(lp); - provider.setInterfaceQuota(mobileIface, mobileLimit); + provider.setLimit(mobileIface, mobileLimit); waitForIdle(); inOrder.verify(mHardware).getForwardedStats(ethernetIface); inOrder.verify(mHardware).stopOffloadControl(); @@ -551,7 +587,7 @@ public class OffloadControllerTest { OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue(); callback.onStoppedLimitReached(); - verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue()); + verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any()); } @Test @@ -654,9 +690,10 @@ public class OffloadControllerTest { // Verify forwarded stats behaviour. verify(mHardware, times(1)).getForwardedStats(eq(RMNET0)); verify(mHardware, times(1)).getForwardedStats(eq(WLAN0)); + // TODO: verify the exact stats reported. + verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any()); + verifyNoMoreInteractions(mTetherStatsProviderCb); verifyNoMoreInteractions(mHardware); - verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue()); - verifyNoMoreInteractions(mNMService); } @Test @@ -719,8 +756,8 @@ public class OffloadControllerTest { // Verify forwarded stats behaviour. verify(mHardware, times(1)).getForwardedStats(eq(RMNET0)); verify(mHardware, times(1)).getForwardedStats(eq(WLAN0)); - verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue()); - verifyNoMoreInteractions(mNMService); + verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any()); + verifyNoMoreInteractions(mTetherStatsProviderCb); // TODO: verify local prefixes and downstreams are also pushed to the HAL. verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture()); diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java index ef97ad418245..3635964dd6a6 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java @@ -26,13 +26,6 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; -import static com.android.internal.R.array.config_mobile_hotspot_provision_app; -import static com.android.internal.R.array.config_tether_bluetooth_regexs; -import static com.android.internal.R.array.config_tether_dhcp_range; -import static com.android.internal.R.array.config_tether_upstream_types; -import static com.android.internal.R.array.config_tether_usb_regexs; -import static com.android.internal.R.array.config_tether_wifi_regexs; -import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -51,6 +44,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.test.BroadcastInterceptingContext; +import com.android.networkstack.tethering.R; import org.junit.After; import org.junit.Before; @@ -120,15 +114,18 @@ public class TetheringConfigurationTest { () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY), eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); - when(mResources.getStringArray(config_tether_dhcp_range)).thenReturn(new String[0]); - when(mResources.getStringArray(config_tether_usb_regexs)).thenReturn(new String[0]); - when(mResources.getStringArray(config_tether_wifi_regexs)) + when(mResources.getStringArray(R.array.config_tether_dhcp_range)).thenReturn( + new String[0]); + when(mResources.getStringArray(R.array.config_tether_usb_regexs)).thenReturn(new String[0]); + when(mResources.getStringArray(R.array.config_tether_wifi_regexs)) .thenReturn(new String[]{ "test_wlan\\d" }); - when(mResources.getStringArray(config_tether_bluetooth_regexs)).thenReturn(new String[0]); - when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(new int[0]); - when(mResources.getStringArray(config_mobile_hotspot_provision_app)) + when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)).thenReturn( + new String[0]); + when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]); + when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) .thenReturn(new String[0]); - when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false); + when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( + false); mHasTelephonyManager = true; mMockContext = new MockContext(mContext); mEnableLegacyDhcpServer = false; @@ -140,7 +137,7 @@ public class TetheringConfigurationTest { } private TetheringConfiguration getTetheringConfiguration(int... legacyTetherUpstreamTypes) { - when(mResources.getIntArray(config_tether_upstream_types)).thenReturn( + when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn( legacyTetherUpstreamTypes); return new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); } @@ -224,7 +221,7 @@ public class TetheringConfigurationTest { @Test public void testNoDefinedUpstreamTypesAddsEthernet() { - when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(new int[]{}); + when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[]{}); when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false); final TetheringConfiguration cfg = new TetheringConfiguration( @@ -246,7 +243,7 @@ public class TetheringConfigurationTest { @Test public void testDefinedUpstreamTypesSansEthernetAddsEthernet() { - when(mResources.getIntArray(config_tether_upstream_types)).thenReturn( + when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn( new int[]{TYPE_WIFI, TYPE_MOBILE_HIPRI}); when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false); @@ -264,7 +261,7 @@ public class TetheringConfigurationTest { @Test public void testDefinedUpstreamTypesWithEthernetDoesNotAddEthernet() { - when(mResources.getIntArray(config_tether_upstream_types)) + when(mResources.getIntArray(R.array.config_tether_upstream_types)) .thenReturn(new int[]{TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_HIPRI}); when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false); @@ -282,7 +279,8 @@ public class TetheringConfigurationTest { @Test public void testNewDhcpServerDisabled() { - when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(true); + when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( + true); doReturn(false).when( () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY), eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); @@ -291,7 +289,8 @@ public class TetheringConfigurationTest { new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); assertTrue(enableByRes.enableLegacyDhcpServer); - when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false); + when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( + false); doReturn(true).when( () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY), eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); @@ -303,7 +302,8 @@ public class TetheringConfigurationTest { @Test public void testNewDhcpServerEnabled() { - when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false); + when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( + false); doReturn(false).when( () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY), eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); @@ -329,16 +329,17 @@ public class TetheringConfigurationTest { private void setUpResourceForSubId() { when(mResourcesForSubId.getStringArray( - config_tether_dhcp_range)).thenReturn(new String[0]); + R.array.config_tether_dhcp_range)).thenReturn(new String[0]); when(mResourcesForSubId.getStringArray( - config_tether_usb_regexs)).thenReturn(new String[0]); + R.array.config_tether_usb_regexs)).thenReturn(new String[0]); when(mResourcesForSubId.getStringArray( - config_tether_wifi_regexs)).thenReturn(new String[]{ "test_wlan\\d" }); + R.array.config_tether_wifi_regexs)).thenReturn(new String[]{ "test_wlan\\d" }); when(mResourcesForSubId.getStringArray( - config_tether_bluetooth_regexs)).thenReturn(new String[0]); - when(mResourcesForSubId.getIntArray(config_tether_upstream_types)).thenReturn(new int[0]); + R.array.config_tether_bluetooth_regexs)).thenReturn(new String[0]); + when(mResourcesForSubId.getIntArray(R.array.config_tether_upstream_types)).thenReturn( + new int[0]); when(mResourcesForSubId.getStringArray( - config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME); + R.array.config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME); } } diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index 900c67198677..59106240a08b 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -19,6 +19,9 @@ package com.android.server.connectivity.tethering; import static android.hardware.usb.UsbManager.USB_CONFIGURED; import static android.hardware.usb.UsbManager.USB_CONNECTED; import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS; +import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED; +import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED; +import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED; import static android.net.RouteInfo.RTN_UNICAST; import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED; import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY; @@ -37,8 +40,6 @@ import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; -import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server; - import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -53,6 +54,7 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; @@ -60,6 +62,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import android.app.usage.NetworkStatsManager; +import android.bluetooth.BluetoothAdapter; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -68,9 +72,8 @@ import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.res.Resources; import android.hardware.usb.UsbManager; +import android.net.ConnectivityManager; import android.net.INetd; -import android.net.INetworkPolicyManager; -import android.net.INetworkStatsService; import android.net.ITetheringEventCallback; import android.net.InetAddresses; import android.net.InterfaceConfigurationParcel; @@ -99,7 +102,6 @@ import android.net.wifi.p2p.WifiP2pInfo; import android.net.wifi.p2p.WifiP2pManager; import android.os.Bundle; import android.os.Handler; -import android.os.INetworkManagementService; import android.os.Looper; import android.os.PersistableBundle; import android.os.RemoteException; @@ -120,6 +122,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.StateMachine; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.FakeSettingsProvider; +import com.android.networkstack.tethering.R; import org.junit.After; import org.junit.Before; @@ -133,6 +136,7 @@ import java.net.Inet4Address; import java.net.Inet6Address; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Vector; @RunWith(AndroidJUnit4.class) @@ -151,9 +155,7 @@ public class TetheringTest { @Mock private ApplicationInfo mApplicationInfo; @Mock private Context mContext; - @Mock private INetworkManagementService mNMService; - @Mock private INetworkStatsService mStatsService; - @Mock private INetworkPolicyManager mPolicyManager; + @Mock private NetworkStatsManager mStatsManager; @Mock private OffloadHardwareInterface mOffloadHardwareInterface; @Mock private Resources mResources; @Mock private TelephonyManager mTelephonyManager; @@ -167,6 +169,7 @@ public class TetheringTest { @Mock private INetd mNetd; @Mock private UserManager mUserManager; @Mock private NetworkRequest mNetworkRequest; + @Mock private ConnectivityManager mCm; private final MockIpServerDependencies mIpServerDependencies = spy(new MockIpServerDependencies()); @@ -217,6 +220,8 @@ public class TetheringTest { if (Context.USB_SERVICE.equals(name)) return mUsbManager; if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager; if (Context.USER_SERVICE.equals(name)) return mUserManager; + if (Context.NETWORK_STATS_SERVICE.equals(name)) return mStatsManager; + if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm; return super.getSystemService(name); } @@ -334,21 +339,6 @@ public class TetheringTest { } @Override - public INetworkManagementService getINetworkManagementService() { - return mNMService; - } - - @Override - public INetworkStatsService getINetworkStatsService() { - return mStatsService; - } - - @Override - public INetworkPolicyManager getINetworkPolicyManager() { - return mPolicyManager; - } - - @Override public INetd getINetd(Context context) { return mNetd; } @@ -362,6 +352,12 @@ public class TetheringTest { public Context getContext() { return mServiceContext; } + + @Override + public BluetoothAdapter getBluetoothAdapter() { + // TODO: add test for bluetooth tethering. + return null; + } } private static UpstreamNetworkState buildMobileUpstreamState(boolean withIPv4, @@ -420,24 +416,24 @@ public class TetheringTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - when(mResources.getStringArray(com.android.internal.R.array.config_tether_dhcp_range)) + when(mResources.getStringArray(R.array.config_tether_dhcp_range)) .thenReturn(new String[0]); - when(mResources.getStringArray(com.android.internal.R.array.config_tether_usb_regexs)) + when(mResources.getStringArray(R.array.config_tether_usb_regexs)) .thenReturn(new String[] { "test_rndis\\d" }); - when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs)) + when(mResources.getStringArray(R.array.config_tether_wifi_regexs)) .thenReturn(new String[]{ "test_wlan\\d" }); - when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_p2p_regexs)) + when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs)) .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" }); - when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs)) + when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)) .thenReturn(new String[0]); - when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types)) - .thenReturn(new int[0]); - when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic)) - .thenReturn(false); - when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false); + when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]); + when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(false); + when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( + false); when(mNetd.interfaceGetList()) .thenReturn(new String[] { TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME}); + when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn(""); mInterfaceConfiguration = new InterfaceConfigurationParcel(); mInterfaceConfiguration.flags = new String[0]; when(mRouterAdvertisementDaemon.start()) @@ -457,7 +453,7 @@ public class TetheringTest { mServiceContext.registerReceiver(mBroadcastReceiver, new IntentFilter(ACTION_TETHER_STATE_CHANGED)); mTethering = makeTethering(); - verify(mNMService).registerTetheringStatsProvider(any(), anyString()); + verify(mStatsManager, times(1)).registerNetworkStatsProvider(anyString(), any()); verify(mNetd).registerUnsolicitedEventListener(any()); final ArgumentCaptor<PhoneStateListener> phoneListenerCaptor = ArgumentCaptor.forClass(PhoneStateListener.class); @@ -700,7 +696,8 @@ public class TetheringTest { @Test public void workingMobileUsbTethering_IPv4LegacyDhcp() { - when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(true); + when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( + true); sendConfigurationChanged(); final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); runUsbTethering(upstreamState); @@ -788,8 +785,7 @@ public class TetheringTest { @Test public void configTetherUpstreamAutomaticIgnoresConfigTetherUpstreamTypes() throws Exception { - when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic)) - .thenReturn(true); + when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(true); sendConfigurationChanged(); // Setup IPv6 @@ -1317,7 +1313,7 @@ public class TetheringTest { private void workingWifiP2pGroupOwnerLegacyMode( boolean emulateInterfaceStatusChanged) throws Exception { // change to legacy mode and update tethering information by chaning SIM - when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_p2p_regexs)) + when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs)) .thenReturn(new String[]{}); final int fakeSubId = 1234; mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId); @@ -1355,6 +1351,50 @@ public class TetheringTest { workingWifiP2pGroupClient(false); } + private void setDataSaverEnabled(boolean enabled) { + final Intent intent = new Intent(ACTION_RESTRICT_BACKGROUND_CHANGED); + mServiceContext.sendBroadcastAsUser(intent, UserHandle.ALL); + + final int status = enabled ? RESTRICT_BACKGROUND_STATUS_ENABLED + : RESTRICT_BACKGROUND_STATUS_DISABLED; + when(mCm.getRestrictBackgroundStatus()).thenReturn(status); + mLooper.dispatchAll(); + } + + @Test + public void testDataSaverChanged() { + // Start Tethering. + final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); + runUsbTethering(upstreamState); + assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME); + // Data saver is ON. + setDataSaverEnabled(true); + // Verify that tethering should be disabled. + verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE); + mTethering.interfaceRemoved(TEST_USB_IFNAME); + mLooper.dispatchAll(); + assertEquals(mTethering.getTetheredIfaces(), new String[0]); + reset(mUsbManager); + + runUsbTethering(upstreamState); + // Verify that user can start tethering again without turning OFF data saver. + assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME); + + // If data saver is keep ON with change event, tethering should not be OFF this time. + setDataSaverEnabled(true); + verify(mUsbManager, times(0)).setCurrentFunctions(UsbManager.FUNCTION_NONE); + assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME); + + // If data saver is turned OFF, it should not change tethering. + setDataSaverEnabled(false); + verify(mUsbManager, times(0)).setCurrentFunctions(UsbManager.FUNCTION_NONE); + assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME); + } + + private static <T> void assertContains(Collection<T> collection, T element) { + assertTrue(element + " not found in " + collection, collection.contains(element)); + } + // TODO: Test that a request for hotspot mode doesn't interfere with an // already operating tethering mode interface. } diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 5eaa80a5143b..c3965c44d4c0 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -36,8 +36,12 @@ import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; +import android.graphics.Bitmap; +import android.graphics.Point; +import android.graphics.Rect; import android.graphics.Region; import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManagerGlobal; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -54,6 +58,7 @@ import android.util.SparseArray; import android.view.Display; import android.view.KeyEvent; import android.view.MagnificationSpec; +import android.view.SurfaceControl; import android.view.View; import android.view.WindowInfo; import android.view.accessibility.AccessibilityCache; @@ -90,6 +95,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ private static final String LOG_TAG = "AbstractAccessibilityServiceConnection"; private static final int WAIT_WINDOWS_TIMEOUT_MILLIS = 5000; + protected static final String TAKE_SCREENSHOT = "takeScreenshot"; protected final Context mContext; protected final SystemSupport mSystemSupport; protected final WindowManagerInternal mWindowManagerService; @@ -934,6 +940,54 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ mInvocationHandler.setSoftKeyboardCallbackEnabled(enabled); } + @Nullable + @Override + public Bitmap takeScreenshot(int displayId) { + synchronized (mLock) { + if (!hasRightsToCurrentUserLocked()) { + return null; + } + + if (!mSecurityPolicy.canTakeScreenshotLocked(this)) { + return null; + } + } + + if (!mSecurityPolicy.checkAccessibilityAccess(this)) { + return null; + } + + final Display display = DisplayManagerGlobal.getInstance() + .getRealDisplay(displayId); + if (display == null) { + return null; + } + final Point displaySize = new Point(); + display.getRealSize(displaySize); + + final int rotation = display.getRotation(); + Bitmap screenShot = null; + + final long identity = Binder.clearCallingIdentity(); + try { + final Rect crop = new Rect(0, 0, displaySize.x, displaySize.y); + // TODO (b/145893483): calling new API with the display as a parameter + // when surface control supported. + screenShot = SurfaceControl.screenshot(crop, displaySize.x, displaySize.y, + rotation); + if (screenShot != null) { + // Optimization for telling the bitmap that all of the pixels are known to be + // opaque (false). This is meant as a drawing hint, as in some cases a bitmap + // that is known to be opaque can take a faster drawing case than one that may + // have non-opaque per-pixel alpha values. + screenShot.setHasAlpha(false); + } + } finally { + Binder.restoreCallingIdentity(identity); + } + return screenShot; + } + @Override public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return; @@ -1018,6 +1072,20 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } } + /** + * Gets windowId of given token. + * + * @param token The token + * @return window id + */ + @Override + public int getWindowIdForLeashToken(@NonNull IBinder token) { + synchronized (mLock) { + // TODO: Add a method to lookup window ID by given leash token. + return -1; + } + } + public void resetLocked() { mSystemSupport.getKeyEventDispatcher().flush(this); try { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 58d3489a8cc8..36a9e0ebbf5d 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -411,6 +411,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (reboundAService) { onUserStateChangedLocked(userState); } + migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, packageName); } } @@ -811,9 +812,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub userState.setTouchExplorationEnabledLocked(touchExplorationEnabled); userState.setDisplayMagnificationEnabledLocked(false); - userState.setNavBarMagnificationEnabledLocked(false); userState.disableShortcutMagnificationLocked(); - userState.setAutoclickEnabledLocked(false); userState.mEnabledServices.clear(); userState.mEnabledServices.add(service); @@ -853,17 +852,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * navigation area has been clicked. * * @param displayId The logical display id. + * @param targetName The flattened {@link ComponentName} string or the class name of a system + * class implementing a supported accessibility feature, or {@code null} if there's no + * specified target. */ @Override - public void notifyAccessibilityButtonClicked(int displayId) { + public void notifyAccessibilityButtonClicked(int displayId, String targetName) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Caller does not hold permission " + android.Manifest.permission.STATUS_BAR_SERVICE); } - synchronized (mLock) { - notifyAccessibilityButtonClickedLocked(displayId); - } + mMainHandler.sendMessage(obtainMessage( + AccessibilityManagerService::performAccessibilityShortcutInternal, this, + displayId, ACCESSIBILITY_BUTTON, targetName)); } /** @@ -1023,6 +1025,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // the state since the context in which the current user // state was used has changed since it was inactive. onUserStateChangedLocked(userState); + migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, null); if (announceNewUser) { // Schedule announcement of the current user if needed. @@ -1132,66 +1135,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - // TODO(a11y shortcut): Remove this function and Use #performAccessibilityShortcutInternal( - // ACCESSIBILITY_BUTTON) instead, after the new Settings shortcut Ui merged. - private void notifyAccessibilityButtonClickedLocked(int displayId) { - final AccessibilityUserState state = getCurrentUserStateLocked(); - - int potentialTargets = state.isNavBarMagnificationEnabledLocked() ? 1 : 0; - for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { - final AccessibilityServiceConnection service = state.mBoundServices.get(i); - if (service.mRequestAccessibilityButton) { - potentialTargets++; - } - } - - if (potentialTargets == 0) { - return; - } - if (potentialTargets == 1) { - if (state.isNavBarMagnificationEnabledLocked()) { - mMainHandler.sendMessage(obtainMessage( - AccessibilityManagerService::sendAccessibilityButtonToInputFilter, this, - displayId)); - return; - } else { - for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { - final AccessibilityServiceConnection service = state.mBoundServices.get(i); - if (service.mRequestAccessibilityButton) { - service.notifyAccessibilityButtonClickedLocked(displayId); - return; - } - } - } - } else { - if (state.getServiceAssignedToAccessibilityButtonLocked() == null - && !state.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) { - mMainHandler.sendMessage(obtainMessage( - AccessibilityManagerService::showAccessibilityTargetsSelection, this, - displayId, ACCESSIBILITY_BUTTON)); - } else if (state.isNavBarMagnificationEnabledLocked() - && state.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) { - mMainHandler.sendMessage(obtainMessage( - AccessibilityManagerService::sendAccessibilityButtonToInputFilter, this, - displayId)); - return; - } else { - for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { - final AccessibilityServiceConnection service = state.mBoundServices.get(i); - if (service.mRequestAccessibilityButton && (service.mComponentName.equals( - state.getServiceAssignedToAccessibilityButtonLocked()))) { - service.notifyAccessibilityButtonClickedLocked(displayId); - return; - } - } - } - // The user may have turned off the assigned service or feature - mMainHandler.sendMessage(obtainMessage( - AccessibilityManagerService::showAccessibilityTargetsSelection, this, - displayId, ACCESSIBILITY_BUTTON)); - } - } - private void sendAccessibilityButtonToInputFilter(int displayId) { synchronized (mLock) { if (mHasInputFilter && mInputFilter != null) { @@ -1635,8 +1578,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (userState.isDisplayMagnificationEnabledLocked()) { flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER; } - if (userState.isNavBarMagnificationEnabledLocked() - || userState.isShortcutKeyMagnificationEnabledLocked()) { + if (userState.isShortcutMagnificationEnabledLocked()) { flags |= AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER; } if (userHasMagnificationServicesLocked(userState)) { @@ -1886,14 +1828,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0, userState.mUserId) == 1; - final boolean navBarMagnificationEnabled = Settings.Secure.getIntForUser( - mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, - 0, userState.mUserId) == 1; - if ((displayMagnificationEnabled != userState.isDisplayMagnificationEnabledLocked()) - || (navBarMagnificationEnabled != userState.isNavBarMagnificationEnabledLocked())) { + if ((displayMagnificationEnabled != userState.isDisplayMagnificationEnabledLocked())) { userState.setDisplayMagnificationEnabledLocked(displayMagnificationEnabled); - userState.setNavBarMagnificationEnabledLocked(navBarMagnificationEnabled); return true; } return false; @@ -2088,8 +2024,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // displays in one display. It's not a real display and there's no input events for it. final ArrayList<Display> displays = getValidDisplayList(); if (userState.isDisplayMagnificationEnabledLocked() - || userState.isNavBarMagnificationEnabledLocked() - || userState.isShortcutKeyMagnificationEnabledLocked()) { + || userState.isShortcutMagnificationEnabledLocked()) { for (int i = 0; i < displays.size(); i++) { final Display display = displays.get(i); getMagnificationController().register(display.getDisplayId()); @@ -2208,6 +2143,85 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub scheduleNotifyClientsOfServicesStateChangeLocked(userState); } + /** + * 1) Check if the service assigned to accessibility button target sdk version > Q. + * If it isn't, remove it from the list and associated setting. + * (It happens when an accessibility service package is downgraded.) + * 2) Check if an enabled service targeting sdk version > Q and requesting a11y button is + * assigned to a shortcut. If it isn't, assigns it to the accessibility button. + * (It happens when an enabled accessibility service package is upgraded.) + * + * @param packageName The package name to check, or {@code null} to check all services. + */ + private void migrateAccessibilityButtonSettingsIfNecessaryLocked( + AccessibilityUserState userState, @Nullable String packageName) { + final Set<String> buttonTargets = + userState.getShortcutTargetsLocked(ACCESSIBILITY_BUTTON); + int lastSize = buttonTargets.size(); + buttonTargets.removeIf(name -> { + if (packageName != null && name != null && !name.contains(packageName)) { + return false; + } + final ComponentName componentName = ComponentName.unflattenFromString(name); + if (componentName == null) { + return false; + } + final AccessibilityServiceInfo serviceInfo = + userState.getInstalledServiceInfoLocked(componentName); + if (serviceInfo == null) { + return false; + } + if (serviceInfo.getResolveInfo().serviceInfo.applicationInfo + .targetSdkVersion > Build.VERSION_CODES.Q) { + return false; + } + // A11y services targeting sdk version <= Q should not be in the list. + return true; + }); + boolean changed = (lastSize != buttonTargets.size()); + lastSize = buttonTargets.size(); + + final Set<String> shortcutKeyTargets = + userState.getShortcutTargetsLocked(ACCESSIBILITY_SHORTCUT_KEY); + userState.mEnabledServices.forEach(componentName -> { + if (packageName != null && componentName != null + && !packageName.equals(componentName.getPackageName())) { + return; + } + final AccessibilityServiceInfo serviceInfo = + userState.getInstalledServiceInfoLocked(componentName); + if (serviceInfo == null) { + return; + } + final boolean requestA11yButton = (serviceInfo.flags + & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0; + if (!(serviceInfo.getResolveInfo().serviceInfo.applicationInfo + .targetSdkVersion > Build.VERSION_CODES.Q && requestA11yButton)) { + return; + } + final String serviceName = serviceInfo.getComponentName().flattenToString(); + if (TextUtils.isEmpty(serviceName)) { + return; + } + if (shortcutKeyTargets.contains(serviceName) || buttonTargets.contains(serviceName)) { + return; + } + // For enabled a11y services targeting sdk version > Q and requesting a11y button should + // be assigned to a shortcut. + buttonTargets.add(serviceName); + }); + changed |= (lastSize != buttonTargets.size()); + if (!changed) { + return; + } + + // Update setting key with new value. + persistColonDelimitedSetToSettingLocked( + Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, + userState.mUserId, buttonTargets, str -> str); + scheduleNotifyClientsOfServicesStateChangeLocked(userState); + } + private void updateRecommendedUiTimeoutLocked(AccessibilityUserState userState) { int newNonInteractiveUiTimeout = userState.getUserNonInteractiveUiTimeoutLocked(); int newInteractiveUiTimeout = userState.getUserInteractiveUiTimeoutLocked(); @@ -2269,9 +2283,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * AIDL-exposed method to be called when the accessibility shortcut key is enabled. Requires * permission to write secure settings, since someone with that permission can enable * accessibility services themselves. + * + * @param targetName The flattened {@link ComponentName} string or the class name of a system + * class implementing a supported accessibility feature, or {@code null} if there's no + * specified target. */ @Override - public void performAccessibilityShortcut() { + public void performAccessibilityShortcut(String targetName) { if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) && (mContext.checkCallingPermission(Manifest.permission.MANAGE_ACCESSIBILITY) != PackageManager.PERMISSION_GRANTED)) { @@ -2280,7 +2298,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } mMainHandler.sendMessage(obtainMessage( AccessibilityManagerService::performAccessibilityShortcutInternal, this, - Display.DEFAULT_DISPLAY, ACCESSIBILITY_SHORTCUT_KEY)); + Display.DEFAULT_DISPLAY, ACCESSIBILITY_SHORTCUT_KEY, targetName)); } /** @@ -2288,20 +2306,31 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * * @param shortcutType The shortcut type. * @param displayId The display id of the accessibility button. + * @param targetName The flattened {@link ComponentName} string or the class name of a system + * class implementing a supported accessibility feature, or {@code null} if there's no + * specified target. */ private void performAccessibilityShortcutInternal(int displayId, - @ShortcutType int shortcutType) { + @ShortcutType int shortcutType, @Nullable String targetName) { final List<String> shortcutTargets = getAccessibilityShortcutTargetsInternal(shortcutType); if (shortcutTargets.isEmpty()) { Slog.d(LOG_TAG, "No target to perform shortcut, shortcutType=" + shortcutType); return; } - // In case there are many targets assigned to the given shortcut. - if (shortcutTargets.size() > 1) { - showAccessibilityTargetsSelection(displayId, shortcutType); - return; + // In case the caller specified a target name + if (targetName != null) { + if (!shortcutTargets.contains(targetName)) { + Slog.d(LOG_TAG, "Perform shortcut failed, invalid target name:" + targetName); + return; + } + } else { + // In case there are many targets assigned to the given shortcut. + if (shortcutTargets.size() > 1) { + showAccessibilityTargetsSelection(displayId, shortcutType); + return; + } + targetName = shortcutTargets.get(0); } - final String targetName = shortcutTargets.get(0); // In case user assigned magnification to the given shortcut. if (targetName.equals(MAGNIFICATION_CONTROLLER_NAME)) { sendAccessibilityButtonToInputFilter(displayId); @@ -2844,11 +2873,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final Uri mDisplayMagnificationEnabledUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED); - // TODO(a11y shortcut): Remove this setting key, and have a migrate function in - // Setting provider after new shortcut UI merged. - private final Uri mNavBarMagnificationEnabledUri = Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED); - private final Uri mAutoclickEnabledUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED); @@ -2888,8 +2912,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver(mDisplayMagnificationEnabledUri, false, this, UserHandle.USER_ALL); - contentResolver.registerContentObserver(mNavBarMagnificationEnabledUri, - false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver(mAutoclickEnabledUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver(mEnabledAccessibilityServicesUri, @@ -2924,8 +2946,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (readTouchExplorationEnabledSettingLocked(userState)) { onUserStateChangedLocked(userState); } - } else if (mDisplayMagnificationEnabledUri.equals(uri) - || mNavBarMagnificationEnabledUri.equals(uri)) { + } else if (mDisplayMagnificationEnabledUri.equals(uri)) { if (readMagnificationEnabledSettingsLocked(userState)) { onUserStateChangedLocked(userState); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java index 203210998c48..7dbec7c8c0c5 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java @@ -326,6 +326,19 @@ public class AccessibilitySecurityPolicy { } /** + * Checks if a service can take screenshot. + * + * @param service The service requesting access + * + * @return Whether ot not the service may take screenshot + */ + public boolean canTakeScreenshotLocked( + @NonNull AbstractAccessibilityServiceConnection service) { + return (service.getCapabilities() + & AccessibilityServiceInfo.CAPABILITY_CAN_TAKE_SCREENSHOT) != 0; + } + + /** * Returns the parent userId of the profile according to the specified userId. * * @param userId The userId to check @@ -426,6 +439,7 @@ public class AccessibilitySecurityPolicy { return false; } } + // TODO: Check parent windowId if the giving windowId is from embedded view hierarchy. if (windowId == mAccessibilityWindowManager.getActiveWindowId(userId)) { return true; } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java index 37ac3ec6f105..25911a7ed0ea 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java @@ -16,6 +16,8 @@ package com.android.server.accessibility; +import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT; + import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import android.Manifest; @@ -25,16 +27,20 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ParceledListSlice; +import android.graphics.Bitmap; import android.os.Binder; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Process; +import android.os.RemoteCallback; import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; import android.util.Slog; import android.view.Display; +import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; @@ -313,48 +319,16 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect } } - // TODO(a11y shortcut): Refactoring the logic here, after the new Settings shortcut Ui merged. public boolean isAccessibilityButtonAvailableLocked(AccessibilityUserState userState) { // If the service does not request the accessibility button, it isn't available if (!mRequestAccessibilityButton) { return false; } - // If the accessibility button isn't currently shown, it cannot be available to services if (!mSystemSupport.isAccessibilityButtonShown()) { return false; } - - // If magnification is on and assigned to the accessibility button, services cannot be - if (userState.isNavBarMagnificationEnabledLocked() - && userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) { - return false; - } - - int requestingServices = 0; - for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) { - final AccessibilityServiceConnection service = userState.mBoundServices.get(i); - if (service.mRequestAccessibilityButton) { - requestingServices++; - } - } - - if (requestingServices == 1) { - // If only a single service is requesting, it must be this service, and the - // accessibility button is available to it - return true; - } else { - // With more than one active service, we derive the target from the user's settings - if (userState.getServiceAssignedToAccessibilityButtonLocked() == null) { - // If the user has not made an assignment, we treat the button as available to - // all services until the user interacts with the button to make an assignment - return true; - } else { - // If an assignment was made, it defines availability - return mComponentName.equals( - userState.getServiceAssignedToAccessibilityButtonLocked()); - } - } + return true; } @Override @@ -419,4 +393,15 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect } } } + + @Override + public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) { + mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> { + final Bitmap screenshot = super.takeScreenshot(displayId); + // Send back the result. + final Bundle payload = new Bundle(); + payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT, screenshot); + callback.sendResult(payload); + }, null).recycleOnUse()); + } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java index a163f7434e1f..ebe2af62b5db 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java @@ -100,7 +100,6 @@ class AccessibilityUserState { private boolean mIsAutoclickEnabled; private boolean mIsDisplayMagnificationEnabled; private boolean mIsFilterKeyEventsEnabled; - private boolean mIsNavBarMagnificationEnabled; private boolean mIsPerformGesturesEnabled; private boolean mIsTextHighContrastEnabled; private boolean mIsTouchExplorationEnabled; @@ -153,7 +152,6 @@ class AccessibilityUserState { mAccessibilityButtonTargets.clear(); mIsTouchExplorationEnabled = false; mIsDisplayMagnificationEnabled = false; - mIsNavBarMagnificationEnabled = false; mIsAutoclickEnabled = false; mUserNonInteractiveUiTimeout = 0; mUserInteractiveUiTimeout = 0; @@ -435,8 +433,6 @@ class AccessibilityUserState { pw.append(", touchExplorationEnabled=").append(String.valueOf(mIsTouchExplorationEnabled)); pw.append(", displayMagnificationEnabled=").append(String.valueOf( mIsDisplayMagnificationEnabled)); - pw.append(", navBarMagnificationEnabled=").append(String.valueOf( - mIsNavBarMagnificationEnabled)); pw.append(", autoclickEnabled=").append(String.valueOf(mIsAutoclickEnabled)); pw.append(", nonInteractiveUiTimeout=").append(String.valueOf(mNonInteractiveUiTimeout)); pw.append(", interactiveUiTimeout=").append(String.valueOf(mInteractiveUiTimeout)); @@ -553,8 +549,12 @@ class AccessibilityUserState { mLastSentClientState = state; } - public boolean isShortcutKeyMagnificationEnabledLocked() { - return mAccessibilityShortcutKeyTargets.contains(MAGNIFICATION_CONTROLLER_NAME); + /** + * Returns true if navibar magnification or shortcut key magnification is enabled. + */ + public boolean isShortcutMagnificationEnabledLocked() { + return mAccessibilityShortcutKeyTargets.contains(MAGNIFICATION_CONTROLLER_NAME) + || mAccessibilityButtonTargets.contains(MAGNIFICATION_CONTROLLER_NAME); } /** @@ -690,28 +690,4 @@ class AccessibilityUserState { public void setUserNonInteractiveUiTimeoutLocked(int timeout) { mUserNonInteractiveUiTimeout = timeout; } - - // TODO(a11y shortcut): These functions aren't necessary, after the new Settings shortcut Ui - // is merged. - boolean isNavBarMagnificationEnabledLocked() { - return mIsNavBarMagnificationEnabled; - } - - void setNavBarMagnificationEnabledLocked(boolean enabled) { - mIsNavBarMagnificationEnabled = enabled; - } - - boolean isNavBarMagnificationAssignedToAccessibilityButtonLocked() { - return mAccessibilityButtonTargets.contains(MAGNIFICATION_CONTROLLER_NAME); - } - - ComponentName getServiceAssignedToAccessibilityButtonLocked() { - final String targetName = mAccessibilityButtonTargets.isEmpty() ? null - : mAccessibilityButtonTargets.valueAt(0); - if (targetName == null) { - return null; - } - return ComponentName.unflattenFromString(targetName); - } - // TODO(a11y shortcut): End } diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java index 7a8a112fae40..5d9af26a8339 100644 --- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java @@ -25,6 +25,7 @@ import android.content.Context; import android.os.Handler; import android.os.IBinder; import android.os.IBinder.DeathRecipient; +import android.os.RemoteCallback; import android.os.RemoteException; import android.util.Slog; import android.view.Display; @@ -325,5 +326,8 @@ class UiAutomationManager { @Override public void onFingerprintGesture(int gesture) {} + + @Override + public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) {} } } diff --git a/services/art-profile b/services/art-profile index a0338d55c55f..4e113c818c6c 100644 --- a/services/art-profile +++ b/services/art-profile @@ -3066,18 +3066,18 @@ HSPLcom/android/server/am/ActivityManagerShellCommand;->runSendBroadcast(Ljava/i HSPLcom/android/server/am/AppBindRecord;->dumpInIntentBind(Ljava/io/PrintWriter;Ljava/lang/String;)V PLcom/android/server/am/AppBindRecord;->toString()Ljava/lang/String; PLcom/android/server/am/AppBindRecord;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V -PLcom/android/server/am/AppCompactor$1;->onPropertyChanged(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V -HSPLcom/android/server/am/AppCompactor$MemCompactionHandler;->handleMessage(Landroid/os/Message;)V -HSPLcom/android/server/am/AppCompactor;-><init>(Lcom/android/server/am/ActivityManagerService;)V -HSPLcom/android/server/am/AppCompactor;->access$1000(Lcom/android/server/am/AppCompactor;)V -HSPLcom/android/server/am/AppCompactor;->access$700(Lcom/android/server/am/AppCompactor;)Lcom/android/server/am/ActivityManagerService; -HSPLcom/android/server/am/AppCompactor;->access$800(Lcom/android/server/am/AppCompactor;)Ljava/util/ArrayList; -HSPLcom/android/server/am/AppCompactor;->access$900(Lcom/android/server/am/AppCompactor;)Ljava/util/Random; -PLcom/android/server/am/AppCompactor;->dump(Ljava/io/PrintWriter;)V -HSPLcom/android/server/am/AppCompactor;->init()V -HSPLcom/android/server/am/AppCompactor;->updateCompactionThrottles()V -HSPLcom/android/server/am/AppCompactor;->updateUseCompaction()V -HSPLcom/android/server/am/AppCompactor;->useCompaction()Z +PLcom/android/server/am/CachedAppOptimizer$1;->onPropertyChanged(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V +HSPLcom/android/server/am/CachedAppOptimizer$MemCompactionHandler;->handleMessage(Landroid/os/Message;)V +HSPLcom/android/server/am/CachedAppOptimizer;-><init>(Lcom/android/server/am/ActivityManagerService;)V +HSPLcom/android/server/am/CachedAppOptimizer;->access$1000(Lcom/android/server/am/CachedAppOptimizer;)V +HSPLcom/android/server/am/CachedAppOptimizer;->access$700(Lcom/android/server/am/CachedAppOptimizer;)Lcom/android/server/am/ActivityManagerService; +HSPLcom/android/server/am/CachedAppOptimizer;->access$800(Lcom/android/server/am/CachedAppOptimizer;)Ljava/util/ArrayList; +HSPLcom/android/server/am/CachedAppOptimizer;->access$900(Lcom/android/server/am/CachedAppOptimizer;)Ljava/util/Random; +PLcom/android/server/am/CachedAppOptimizer;->dump(Ljava/io/PrintWriter;)V +HSPLcom/android/server/am/CachedAppOptimizer;->init()V +HSPLcom/android/server/am/CachedAppOptimizer;->updateCompactionThrottles()V +HSPLcom/android/server/am/CachedAppOptimizer;->updateUseCompaction()V +HSPLcom/android/server/am/CachedAppOptimizer;->useCompaction()Z PLcom/android/server/am/AppErrorDialog$1;->handleMessage(Landroid/os/Message;)V PLcom/android/server/am/AppErrorDialog;-><init>(Landroid/content/Context;Lcom/android/server/am/ActivityManagerService;Lcom/android/server/am/AppErrorDialog$Data;)V PLcom/android/server/am/AppErrorDialog;->onClick(Landroid/view/View;)V @@ -18632,9 +18632,9 @@ Lcom/android/server/am/ActivityManagerService$UiHandler; Lcom/android/server/am/ActivityManagerService$UidObserverRegistration; Lcom/android/server/am/ActivityManagerService; Lcom/android/server/am/AppBindRecord; -Lcom/android/server/am/AppCompactor$1; -Lcom/android/server/am/AppCompactor$MemCompactionHandler; -Lcom/android/server/am/AppCompactor; +Lcom/android/server/am/CachedAppOptimizer$1; +Lcom/android/server/am/CachedAppOptimizer$MemCompactionHandler; +Lcom/android/server/am/CachedAppOptimizer; Lcom/android/server/am/AppErrorDialog$Data; Lcom/android/server/am/AppErrorResult; Lcom/android/server/am/AppErrors$BadProcessInfo; diff --git a/services/art-profile-boot b/services/art-profile-boot index e09424bc261c..fe4178ac4a50 100644 --- a/services/art-profile-boot +++ b/services/art-profile-boot @@ -538,7 +538,7 @@ Lcom/android/server/wm/WindowProcessController;->setCurrentProcState(I)V Lcom/android/server/am/ActivityManagerService;->updateLowMemStateLocked(III)Z Lcom/android/server/wm/ConfigurationContainer;->getWindowConfiguration()Landroid/app/WindowConfiguration; Lcom/android/server/am/OomAdjuster;->applyOomAdjLocked(Lcom/android/server/am/ProcessRecord;ZJJ)Z -Lcom/android/server/am/AppCompactor;->useCompaction()Z +Lcom/android/server/am/CachedAppOptimizer;->useCompaction()Z Lcom/android/server/am/ProcessList;->procStatesDifferForMem(II)Z Lcom/android/server/am/ActivityManagerService;->dispatchUidsChanged()V Lcom/android/server/audio/AudioService$VolumeStreamState;->setIndex(IILjava/lang/String;)Z diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index 7b95ab526b41..2c229b443f91 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -2538,7 +2538,6 @@ public class UserBackupManagerService { KeyValueBackupJob.schedule(mUserId, mContext, mConstants); } else { if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass"); - synchronized (mQueueLock) { // Fire the intent that kicks off the whole shebang... try { mRunBackupIntent.send(); @@ -2546,10 +2545,8 @@ public class UserBackupManagerService { // should never happen Slog.e(TAG, "run-backup intent cancelled!"); } - // ...and cancel any pending scheduled job, because we've just superseded it KeyValueBackupJob.cancel(mUserId, mContext); - } } } finally { Binder.restoreCallingIdentity(oldId); diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 5435cbad870f..c1e23e42c14f 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -79,7 +79,6 @@ import android.net.InetAddresses; import android.net.IpMemoryStore; import android.net.IpPrefix; import android.net.LinkProperties; -import android.net.LinkProperties.CompareResult; import android.net.MatchAllNetworkSpecifier; import android.net.NattSocketKeepalive; import android.net.Network; @@ -87,7 +86,6 @@ import android.net.NetworkAgent; import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; import android.net.NetworkConfig; -import android.net.NetworkFactory; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkMonitorManager; @@ -114,6 +112,7 @@ import android.net.metrics.IpConnectivityLog; import android.net.metrics.NetworkEvent; import android.net.netlink.InetDiagMessage; import android.net.shared.PrivateDnsConfig; +import android.net.util.LinkPropertiesUtils.CompareResult; import android.net.util.MultinetworkPolicyTracker; import android.net.util.NetdService; import android.os.Binder; @@ -2843,26 +2842,11 @@ public class ConnectivityService extends IConnectivityManager.Stub return true; } - // TODO: delete when direct use of registerNetworkFactory is no longer supported. - private boolean maybeHandleNetworkFactoryMessage(Message msg) { - switch (msg.what) { - default: - return false; - case NetworkFactory.EVENT_UNFULFILLABLE_REQUEST: { - handleReleaseNetworkRequest((NetworkRequest) msg.obj, msg.sendingUid, - /* callOnUnavailable */ true); - break; - } - } - return true; - } - @Override public void handleMessage(Message msg) { if (!maybeHandleAsyncChannelMessage(msg) && !maybeHandleNetworkMonitorMessage(msg) - && !maybeHandleNetworkAgentInfoMessage(msg) - && !maybeHandleNetworkFactoryMessage(msg)) { + && !maybeHandleNetworkAgentInfoMessage(msg)) { maybeHandleNetworkAgentMessage(msg); } } diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java index c0f43a81eca4..e7f78462e6ee 100644 --- a/services/core/java/com/android/server/SystemServiceManager.java +++ b/services/core/java/com/android/server/SystemServiceManager.java @@ -25,11 +25,14 @@ import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; import android.os.UserManagerInternal; +import android.util.ArrayMap; import android.util.Slog; import com.android.server.SystemService.TargetUser; import com.android.server.utils.TimingsTraceAndSlog; +import dalvik.system.PathClassLoader; + import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -63,6 +66,9 @@ public class SystemServiceManager { // Services that should receive lifecycle events. private final ArrayList<SystemService> mServices = new ArrayList<SystemService>(); + // Map of paths to PathClassLoader, so we don't load the same path multiple times. + private final ArrayMap<String, PathClassLoader> mLoadedPaths = new ArrayMap<>(); + private int mCurrentPhase = -1; private UserManagerInternal mUserManagerInternal; @@ -76,20 +82,46 @@ public class SystemServiceManager { * * @return The service instance. */ - @SuppressWarnings("unchecked") public SystemService startService(String className) { - final Class<SystemService> serviceClass; + final Class<SystemService> serviceClass = loadClassFromLoader(className, + this.getClass().getClassLoader()); + return startService(serviceClass); + } + + /** + * Starts a service by class name and a path that specifies the jar where the service lives. + * + * @return The service instance. + */ + public SystemService startServiceFromJar(String className, String path) { + PathClassLoader pathClassLoader = mLoadedPaths.get(path); + if (pathClassLoader == null) { + // NB: the parent class loader should always be the system server class loader. + // Changing it has implications that require discussion with the mainline team. + pathClassLoader = new PathClassLoader(path, this.getClass().getClassLoader()); + mLoadedPaths.put(path, pathClassLoader); + } + final Class<SystemService> serviceClass = loadClassFromLoader(className, pathClassLoader); + return startService(serviceClass); + } + + /* + * Loads and initializes a class from the given classLoader. Returns the class. + */ + @SuppressWarnings("unchecked") + private static Class<SystemService> loadClassFromLoader(String className, + ClassLoader classLoader) { try { - serviceClass = (Class<SystemService>)Class.forName(className); + return (Class<SystemService>) Class.forName(className, true, classLoader); } catch (ClassNotFoundException ex) { - Slog.i(TAG, "Starting " + className); throw new RuntimeException("Failed to create service " + className - + ": service class not found, usually indicates that the caller should " + + " from class loader " + classLoader.toString() + ": service class not " + + "found, usually indicates that the caller should " + "have called PackageManager.hasSystemFeature() to check whether the " + "feature is available on this device before trying to start the " - + "services that implement it", ex); + + "services that implement it. Also ensure that the correct path for the " + + "classloader is supplied, if applicable.", ex); } - return startService(serviceClass); } /** diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 27e0d52d78ee..5a56a9fa5367 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -61,7 +61,6 @@ import android.provider.DeviceConfig; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.DebugUtils; -import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.StatsLog; @@ -164,8 +163,7 @@ public class VibratorService extends IVibratorService.Stub private int mHapticFeedbackIntensity; private int mNotificationIntensity; private int mRingIntensity; - private SparseArray<Pair<VibrationEffect, VibrationAttributes>> mAlwaysOnEffects = - new SparseArray<>(); + private SparseArray<Vibration> mAlwaysOnEffects = new SparseArray<>(); static native boolean vibratorExists(); static native void vibratorInit(); @@ -461,6 +459,10 @@ public class VibratorService extends IVibratorService.Stub Settings.System.getUriFor(Settings.System.RING_VIBRATION_INTENSITY), true, mSettingObserver, UserHandle.USER_ALL); + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.ZEN_MODE), + true, mSettingObserver, UserHandle.USER_ALL); + mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -508,7 +510,8 @@ public class VibratorService extends IVibratorService.Stub } @Override // Binder call - public boolean setAlwaysOnEffect(int id, VibrationEffect effect, VibrationAttributes attrs) { + public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, VibrationEffect effect, + VibrationAttributes attrs) { if (!hasPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON)) { throw new SecurityException("Requires VIBRATE_ALWAYS_ON permission"); } @@ -518,8 +521,8 @@ public class VibratorService extends IVibratorService.Stub } if (effect == null) { synchronized (mLock) { - mAlwaysOnEffects.delete(id); - vibratorAlwaysOnDisable(id); + mAlwaysOnEffects.delete(alwaysOnId); + vibratorAlwaysOnDisable(alwaysOnId); } } else { if (!verifyVibrationEffect(effect)) { @@ -529,13 +532,11 @@ public class VibratorService extends IVibratorService.Stub Slog.e(TAG, "Only prebaked effects supported for always-on."); return false; } - if (attrs == null) { - attrs = new VibrationAttributes.Builder() - .build(); - } + attrs = fixupVibrationAttributes(attrs); synchronized (mLock) { - mAlwaysOnEffects.put(id, Pair.create(effect, attrs)); - updateAlwaysOnLocked(id, effect, attrs); + Vibration vib = new Vibration(null, effect, attrs, uid, opPkg, null); + mAlwaysOnEffects.put(alwaysOnId, vib); + updateAlwaysOnLocked(alwaysOnId, vib); } } return true; @@ -575,6 +576,23 @@ public class VibratorService extends IVibratorService.Stub return true; } + private VibrationAttributes fixupVibrationAttributes(VibrationAttributes attrs) { + if (attrs == null) { + attrs = DEFAULT_ATTRIBUTES; + } + if (shouldBypassDnd(attrs)) { + if (!(hasPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + || hasPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + || hasPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING))) { + final int flags = attrs.getFlags() + & ~VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY; + attrs = new VibrationAttributes.Builder(attrs).replaceFlags(flags).build(); + } + } + + return attrs; + } + private static long[] getLongIntArray(Resources r, int resid) { int[] ar = r.getIntArray(resid); if (ar == null) { @@ -604,19 +622,7 @@ public class VibratorService extends IVibratorService.Stub return; } - if (attrs == null) { - attrs = DEFAULT_ATTRIBUTES; - } - - if (shouldBypassDnd(attrs)) { - if (!(hasPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - || hasPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - || hasPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING))) { - final int flags = attrs.getFlags() - & ~VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY; - attrs = new VibrationAttributes.Builder(attrs).replaceFlags(flags).build(); - } - } + attrs = fixupVibrationAttributes(attrs); // If our current vibration is longer than the new vibration and is the same amplitude, // then just let the current one finish. @@ -777,29 +783,8 @@ public class VibratorService extends IVibratorService.Stub private void startVibrationLocked(final Vibration vib) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked"); try { - if (!isAllowedToVibrateLocked(vib)) { - return; - } - final int intensity = getCurrentIntensityLocked(vib); - if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) { - return; - } - - if (vib.isRingtone() && !shouldVibrateForRingtone()) { - if (DEBUG) { - Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones"); - } - return; - } - - final int mode = getAppOpMode(vib); - if (mode != AppOpsManager.MODE_ALLOWED) { - if (mode == AppOpsManager.MODE_ERRORED) { - // We might be getting calls from within system_server, so we don't actually - // want to throw a SecurityException here. - Slog.w(TAG, "Would be an error: vibrate from uid " + vib.uid); - } + if (!shouldVibrate(vib, intensity)) { return; } applyVibrationIntensityScalingLocked(vib, intensity); @@ -958,6 +943,35 @@ public class VibratorService extends IVibratorService.Stub return mode; } + private boolean shouldVibrate(Vibration vib, int intensity) { + if (!isAllowedToVibrateLocked(vib)) { + return false; + } + + if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) { + return false; + } + + if (vib.isRingtone() && !shouldVibrateForRingtone()) { + if (DEBUG) { + Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones"); + } + return false; + } + + final int mode = getAppOpMode(vib); + if (mode != AppOpsManager.MODE_ALLOWED) { + if (mode == AppOpsManager.MODE_ERRORED) { + // We might be getting calls from within system_server, so we don't actually + // want to throw a SecurityException here. + Slog.w(TAG, "Would be an error: vibrate from uid " + vib.uid); + } + return false; + } + + return true; + } + @GuardedBy("mLock") private void reportFinishVibrationLocked() { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked"); @@ -1069,14 +1083,12 @@ public class VibratorService extends IVibratorService.Stub mVibrator.getDefaultRingVibrationIntensity(), UserHandle.USER_CURRENT); } - private void updateAlwaysOnLocked(int id, VibrationEffect effect, VibrationAttributes attrs) { - // TODO: Check DND and LowPower settings - final Vibration vib = new Vibration(null, effect, attrs, 0, null, null); + private void updateAlwaysOnLocked(int id, Vibration vib) { final int intensity = getCurrentIntensityLocked(vib); - if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) { + if (!shouldVibrate(vib, intensity)) { vibratorAlwaysOnDisable(id); } else { - final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect; + final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect; final int strength = intensityToEffectStrength(intensity); vibratorAlwaysOnEnable(id, prebaked.getId(), strength); } @@ -1085,8 +1097,8 @@ public class VibratorService extends IVibratorService.Stub private void updateAlwaysOnLocked() { for (int i = 0; i < mAlwaysOnEffects.size(); i++) { int id = mAlwaysOnEffects.keyAt(i); - Pair<VibrationEffect, VibrationAttributes> pair = mAlwaysOnEffects.valueAt(i); - updateAlwaysOnLocked(id, pair.first, pair.second); + Vibration vib = mAlwaysOnEffects.valueAt(i); + updateAlwaysOnLocked(id, vib); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 8f6d981064de..883e7c6799dc 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2555,7 +2555,7 @@ public class ActivityManagerService extends IActivityManager.Stub Process.setThreadGroupAndCpuset(BackgroundThread.get().getThreadId(), Process.THREAD_GROUP_SYSTEM); Process.setThreadGroupAndCpuset( - mOomAdjuster.mAppCompact.mCompactionThread.getThreadId(), + mOomAdjuster.mCachedAppOptimizer.mCachedAppOptimizerThread.getThreadId(), Process.THREAD_GROUP_SYSTEM); } catch (Exception e) { Slog.w(TAG, "Setting background thread cpuset failed"); @@ -5304,7 +5304,7 @@ public class ActivityManagerService extends IActivityManager.Stub String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) { synchronized (ActivityManagerService.this) { - mOomAdjuster.mAppCompact.compactAllSystem(); + mOomAdjuster.mCachedAppOptimizer.compactAllSystem(); requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false); } } @@ -9000,7 +9000,7 @@ public class ActivityManagerService extends IActivityManager.Stub final long timeSinceLastIdle = now - mLastIdleTime; // Compact all non-zygote processes to freshen up the page cache. - mOomAdjuster.mAppCompact.compactAllSystem(); + mOomAdjuster.mCachedAppOptimizer.compactAllSystem(); final long lowRamSinceLastIdle = getLowRamTimeSinceIdle(now); mLastIdleTime = now; @@ -9108,13 +9108,14 @@ public class ActivityManagerService extends IActivityManager.Stub final Resources res = mContext.getResources(); mAppErrors.loadAppsNotReportingCrashesFromConfigLocked(res.getString( com.android.internal.R.string.config_appsNotReportingCrashes)); - mUserController.mUserSwitchUiEnabled = !res.getBoolean( + final boolean userSwitchUiEnabled = !res.getBoolean( com.android.internal.R.bool.config_customUserSwitchUi); - mUserController.mMaxRunningUsers = res.getInteger( + final int maxRunningUsers = res.getInteger( com.android.internal.R.integer.config_multiuserMaxRunningUsers); - mUserController.mDelayUserDataLocking = res.getBoolean( + final boolean delayUserDataLocking = res.getBoolean( com.android.internal.R.bool.config_multiuserDelayUserDataLocking); - + mUserController.setInitialConfig(userSwitchUiEnabled, maxRunningUsers, + delayUserDataLocking); mWaitForNetworkTimeoutMs = waitForNetworkTimeoutMs; mPssDeferralTime = pssDeferralMs; } @@ -10020,7 +10021,7 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized(this) { mConstants.dump(pw); - mOomAdjuster.dumpAppCompactorSettings(pw); + mOomAdjuster.dumpCachedAppOptimizerSettings(pw); pw.println(); if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); @@ -10425,7 +10426,7 @@ public class ActivityManagerService extends IActivityManager.Stub } else if ("settings".equals(cmd)) { synchronized (this) { mConstants.dump(pw); - mOomAdjuster.dumpAppCompactorSettings(pw); + mOomAdjuster.dumpCachedAppOptimizerSettings(pw); } } else if ("services".equals(cmd) || "s".equals(cmd)) { if (dumpClient) { @@ -17942,7 +17943,31 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public int stopUser(final int userId, boolean force, final IStopUserCallback callback) { - return mUserController.stopUser(userId, force, callback, null /* keyEvictedCallback */); + return mUserController.stopUser(userId, force, /* allowDelayedLocking= */ false, + /* callback= */ callback, /* keyEvictedCallback= */ null); + } + + /** + * Stops user but allow delayed locking. Delayed locking keeps user unlocked even after + * stopping only if {@code config_multiuserDelayUserDataLocking} overlay is set true. + * + * <p>When delayed locking is not enabled through the overlay, this call becomes the same + * with {@link #stopUser(int, boolean, IStopUserCallback)} call. + * + * @param userId User id to stop. + * @param force Force stop the user even if the user is related with system user or current + * user. + * @param callback Callback called when user has stopped. + * + * @return {@link ActivityManager#USER_OP_SUCCESS} when user is stopped successfully. Returns + * other {@code ActivityManager#USER_OP_*} codes for failure. + * + */ + @Override + public int stopUserWithDelayedLocking(final int userId, boolean force, + final IStopUserCallback callback) { + return mUserController.stopUser(userId, force, /* allowDelayedLocking= */ true, + /* callback= */ callback, /* keyEvictedCallback= */ null); } @Override @@ -18348,7 +18373,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public int getMaxRunningUsers() { - return mUserController.mMaxRunningUsers; + return mUserController.getMaxRunningUsers(); } @Override diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index b7e206516640..3ca5ebce93f2 100644 --- a/services/core/java/com/android/server/am/AppCompactor.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -51,7 +51,7 @@ import java.util.Map; import java.util.Random; import java.util.Set; -public final class AppCompactor { +public final class CachedAppOptimizer { // Flags stored in the DeviceConfig API. @VisibleForTesting static final String KEY_USE_COMPACTION = "use_compaction"; @@ -122,7 +122,7 @@ public final class AppCompactor { * that will wipe out the cpuset assignment for system_server threads. * Accordingly, this is in the AMS constructor. */ - final ServiceThread mCompactionThread; + final ServiceThread mCachedAppOptimizerThread; private final ArrayList<ProcessRecord> mPendingCompactionProcesses = new ArrayList<ProcessRecord>(); @@ -214,15 +214,15 @@ public final class AppCompactor { private int mPersistentCompactionCount; private int mBfgsCompactionCount; - public AppCompactor(ActivityManagerService am) { + public CachedAppOptimizer(ActivityManagerService am) { mAm = am; - mCompactionThread = new ServiceThread("CompactionThread", + mCachedAppOptimizerThread = new ServiceThread("CachedAppOptimizerThread", THREAD_PRIORITY_FOREGROUND, true); mProcStateThrottle = new HashSet<>(); } @VisibleForTesting - AppCompactor(ActivityManagerService am, PropertyChangedCallbackForTest callback) { + CachedAppOptimizer(ActivityManagerService am, PropertyChangedCallbackForTest callback) { this(am); mTestCallback = callback; } @@ -243,7 +243,7 @@ public final class AppCompactor { updateFullDeltaRssThrottle(); updateProcStateThrottle(); } - Process.setThreadGroupAndCpuset(mCompactionThread.getThreadId(), + Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(), Process.THREAD_GROUP_SYSTEM); } @@ -258,7 +258,7 @@ public final class AppCompactor { @GuardedBy("mAm") void dump(PrintWriter pw) { - pw.println("AppCompactor settings"); + pw.println("CachedAppOptimizer settings"); synchronized (mPhenotypeFlagLock) { pw.println(" " + KEY_USE_COMPACTION + "=" + mUseCompaction); pw.println(" " + KEY_COMPACT_ACTION_1 + "=" + mCompactActionSome); @@ -300,7 +300,7 @@ public final class AppCompactor { app.reqCompactAction = COMPACT_PROCESS_SOME; mPendingCompactionProcesses.add(app); mCompactionHandler.sendMessage( - mCompactionHandler.obtainMessage( + mCompactionHandler.obtainMessage( COMPACT_PROCESS_MSG, app.setAdj, app.setProcState)); } @@ -309,7 +309,7 @@ public final class AppCompactor { app.reqCompactAction = COMPACT_PROCESS_FULL; mPendingCompactionProcesses.add(app); mCompactionHandler.sendMessage( - mCompactionHandler.obtainMessage( + mCompactionHandler.obtainMessage( COMPACT_PROCESS_MSG, app.setAdj, app.setProcState)); } @@ -362,8 +362,8 @@ public final class AppCompactor { private void updateUseCompaction() { mUseCompaction = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_USE_COMPACTION, DEFAULT_USE_COMPACTION); - if (mUseCompaction && !mCompactionThread.isAlive()) { - mCompactionThread.start(); + if (mUseCompaction && !mCachedAppOptimizerThread.isAlive()) { + mCachedAppOptimizerThread.start(); mCompactionHandler = new MemCompactionHandler(); } } @@ -521,7 +521,7 @@ public final class AppCompactor { private final class MemCompactionHandler extends Handler { private MemCompactionHandler() { - super(mCompactionThread.getLooper()); + super(mCachedAppOptimizerThread.getLooper()); } @Override diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 0fc885a2e61f..f86d6a70a076 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -122,9 +122,9 @@ public final class OomAdjuster { PowerManagerInternal mLocalPowerManager; /** - * Service for compacting background apps. + * Service for optimizing resource usage from background apps. */ - AppCompactor mAppCompact; + CachedAppOptimizer mCachedAppOptimizer; ActivityManagerConstants mConstants; @@ -197,7 +197,7 @@ public final class OomAdjuster { mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class); mConstants = mService.mConstants; - mAppCompact = new AppCompactor(mService); + mCachedAppOptimizer = new CachedAppOptimizer(mService); mProcessGroupHandler = new Handler(adjusterThread.getLooper(), msg -> { final int pid = msg.arg1; @@ -224,7 +224,7 @@ public final class OomAdjuster { } void initSettings() { - mAppCompact.init(); + mCachedAppOptimizer.init(); } /** @@ -1978,7 +1978,7 @@ public final class OomAdjuster { int changes = 0; // don't compact during bootup - if (mAppCompact.useCompaction() && mService.mBooted) { + if (mCachedAppOptimizer.useCompaction() && mService.mBooted) { // Cached and prev/home compaction if (app.curAdj != app.setAdj) { // Perform a minor compaction when a perceptible app becomes the prev/home app @@ -1987,26 +1987,26 @@ public final class OomAdjuster { if (app.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ && (app.curAdj == ProcessList.PREVIOUS_APP_ADJ || app.curAdj == ProcessList.HOME_APP_ADJ)) { - mAppCompact.compactAppSome(app); + mCachedAppOptimizer.compactAppSome(app); } else if ((app.setAdj < ProcessList.CACHED_APP_MIN_ADJ || app.setAdj > ProcessList.CACHED_APP_MAX_ADJ) && app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ && app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ) { - mAppCompact.compactAppFull(app); + mCachedAppOptimizer.compactAppFull(app); } } else if (mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE && app.setAdj < ProcessList.FOREGROUND_APP_ADJ // Because these can fire independent of oom_adj/procstate changes, we need // to throttle the actual dispatch of these requests in addition to the // processing of the requests. As a result, there is throttling both here - // and in AppCompactor. - && mAppCompact.shouldCompactPersistent(app, now)) { - mAppCompact.compactAppPersistent(app); + // and in CachedAppOptimizer. + && mCachedAppOptimizer.shouldCompactPersistent(app, now)) { + mCachedAppOptimizer.compactAppPersistent(app); } else if (mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE && app.getCurProcState() == ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE - && mAppCompact.shouldCompactBFGS(app, now)) { - mAppCompact.compactAppBfgs(app); + && mCachedAppOptimizer.shouldCompactBFGS(app, now)) { + mCachedAppOptimizer.compactAppBfgs(app); } } @@ -2439,7 +2439,7 @@ public final class OomAdjuster { } @GuardedBy("mService") - void dumpAppCompactorSettings(PrintWriter pw) { - mAppCompact.dump(pw); + void dumpCachedAppOptimizerSettings(PrintWriter pw) { + mCachedAppOptimizer.dump(pw); } } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index e11008c246dd..b7f867df04c2 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -647,11 +647,10 @@ public final class ProcessList { // Get this after boot, and won't be changed until it's rebooted, as we don't // want some apps enabled while some apps disabled mAppDataIsolationEnabled = - SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, false); + SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true); mAppDataIsolationWhitelistedApps = new ArrayList<>( SystemConfig.getInstance().getAppDataIsolationWhitelistedApps()); - if (sKillHandler == null) { sKillThread = new ServiceThread(TAG + ":kill", THREAD_PRIORITY_BACKGROUND, true /* allowIo */); diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 8ae18ff68b66..f3a2e70f9b89 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -93,7 +93,6 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.util.ArrayUtils; -import com.android.internal.util.Preconditions; import com.android.internal.widget.LockPatternUtils; import com.android.server.FgThread; import com.android.server.LocalServices; @@ -161,7 +160,8 @@ class UserController implements Handler.Callback { * <p>Note: Current and system user (and their related profiles) are never stopped when * switching users. Due to that, the actual number of running users can exceed mMaxRunningUsers */ - int mMaxRunningUsers; + @GuardedBy("mLock") + private int mMaxRunningUsers; // Lock for internal state. private final Object mLock = new Object(); @@ -213,7 +213,8 @@ class UserController implements Handler.Callback { private final RemoteCallbackList<IUserSwitchObserver> mUserSwitchObservers = new RemoteCallbackList<>(); - boolean mUserSwitchUiEnabled = true; + @GuardedBy("mLock") + private boolean mUserSwitchUiEnabled = true; /** * Currently active user switch callbacks. @@ -246,10 +247,11 @@ class UserController implements Handler.Callback { /** * In this mode, user is always stopped when switched out but locking of user data is * postponed until total number of unlocked users in the system reaches mMaxRunningUsers. - * Once total number of unlocked users reach mMaxRunningUsers, least recentely used user + * Once total number of unlocked users reach mMaxRunningUsers, least recently used user * will be locked. */ - boolean mDelayUserDataLocking; + @GuardedBy("mLock") + private boolean mDelayUserDataLocking; /** * Keep track of last active users for mDelayUserDataLocking. * The latest stopped user is placed in front while the least recently stopped user in back. @@ -275,6 +277,33 @@ class UserController implements Handler.Callback { updateStartedUserArrayLU(); } + void setInitialConfig(boolean userSwitchUiEnabled, int maxRunningUsers, + boolean delayUserDataLocking) { + synchronized (mLock) { + mUserSwitchUiEnabled = userSwitchUiEnabled; + mMaxRunningUsers = maxRunningUsers; + mDelayUserDataLocking = delayUserDataLocking; + } + } + + private boolean isUserSwitchUiEnabled() { + synchronized (mLock) { + return mUserSwitchUiEnabled; + } + } + + int getMaxRunningUsers() { + synchronized (mLock) { + return mMaxRunningUsers; + } + } + + private boolean isDelayUserDataLockingEnabled() { + synchronized (mLock) { + return mDelayUserDataLocking; + } + } + void finishUserSwitch(UserState uss) { // This call holds the AM lock so we post to the handler. mHandler.post(() -> { @@ -321,7 +350,11 @@ class UserController implements Handler.Callback { // Owner/System user and current user can't be stopped continue; } - if (stopUsersLU(userId, false, null, null) == USER_OP_SUCCESS) { + // allowDelayedLocking set here as stopping user is done without any explicit request + // from outside. + if (stopUsersLU(userId, /* force= */ false, /* allowDelayedLocking= */ true, + /* stopUserCallback= */ null, /* keyEvictedCallback= */ null) + == USER_OP_SUCCESS) { iterator.remove(); } } @@ -567,8 +600,8 @@ class UserController implements Handler.Callback { // intialize it; it should be stopped right away as it's not really a "real" user. // TODO(b/143092698): in the long-term, it might be better to add a onCreateUser() // callback on SystemService instead. - stopUser(userInfo.id, /* force= */ true, /* stopUserCallback= */ null, - /* keyEvictedCallback= */ null); + stopUser(userInfo.id, /* force= */ true, /* allowDelayedLocking= */ false, + /* stopUserCallback= */ null, /* keyEvictedCallback= */ null); return; } @@ -611,7 +644,8 @@ class UserController implements Handler.Callback { } int restartUser(final int userId, final boolean foreground) { - return stopUser(userId, /* force */ true, null, new KeyEvictedCallback() { + return stopUser(userId, /* force= */ true, /* allowDelayedLocking= */ false, + /* stopUserCallback= */ null, new KeyEvictedCallback() { @Override public void keyEvicted(@UserIdInt int userId) { // Post to the same handler that this callback is called from to ensure the user @@ -621,15 +655,16 @@ class UserController implements Handler.Callback { }); } - int stopUser(final int userId, final boolean force, final IStopUserCallback stopUserCallback, - KeyEvictedCallback keyEvictedCallback) { + int stopUser(final int userId, final boolean force, boolean allowDelayedLocking, + final IStopUserCallback stopUserCallback, KeyEvictedCallback keyEvictedCallback) { checkCallingPermission(INTERACT_ACROSS_USERS_FULL, "stopUser"); if (userId < 0 || userId == UserHandle.USER_SYSTEM) { throw new IllegalArgumentException("Can't stop system user " + userId); } enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId); synchronized (mLock) { - return stopUsersLU(userId, force, stopUserCallback, keyEvictedCallback); + return stopUsersLU(userId, force, allowDelayedLocking, stopUserCallback, + keyEvictedCallback); } } @@ -638,7 +673,7 @@ class UserController implements Handler.Callback { * {@link #getUsersToStopLU(int)} to determine the list of users that should be stopped. */ @GuardedBy("mLock") - private int stopUsersLU(final int userId, boolean force, + private int stopUsersLU(final int userId, boolean force, boolean allowDelayedLocking, final IStopUserCallback stopUserCallback, KeyEvictedCallback keyEvictedCallback) { if (userId == UserHandle.USER_SYSTEM) { return USER_OP_ERROR_IS_SYSTEM; @@ -657,7 +692,8 @@ class UserController implements Handler.Callback { if (force) { Slog.i(TAG, "Force stop user " + userId + ". Related users will not be stopped"); - stopSingleUserLU(userId, stopUserCallback, keyEvictedCallback); + stopSingleUserLU(userId, allowDelayedLocking, stopUserCallback, + keyEvictedCallback); return USER_OP_SUCCESS; } return USER_OP_ERROR_RELATED_USERS_CANNOT_STOP; @@ -665,21 +701,64 @@ class UserController implements Handler.Callback { } if (DEBUG_MU) Slog.i(TAG, "stopUsersLocked usersToStop=" + Arrays.toString(usersToStop)); for (int userIdToStop : usersToStop) { - stopSingleUserLU(userIdToStop, + stopSingleUserLU(userIdToStop, allowDelayedLocking, userIdToStop == userId ? stopUserCallback : null, userIdToStop == userId ? keyEvictedCallback : null); } return USER_OP_SUCCESS; } + /** + * Stops a single User. This can also trigger locking user data out depending on device's + * config ({@code mDelayUserDataLocking}) and arguments. + * User will be unlocked when + * - {@code mDelayUserDataLocking} is not set. + * - {@code mDelayUserDataLocking} is set and {@code keyEvictedCallback} is non-null. + * - + * + * @param userId User Id to stop and lock the data. + * @param allowDelayedLocking When set, do not lock user after stopping. Locking can happen + * later when number of unlocked users reaches + * {@code mMaxRunnngUsers}. Note that this is respected only when + * {@code mDelayUserDataLocking} is set and {@keyEvictedCallback} is + * null. Otherwise the user will be locked. + * @param stopUserCallback Callback to notify that user has stopped. + * @param keyEvictedCallback Callback to notify that user has been unlocked. + */ @GuardedBy("mLock") - private void stopSingleUserLU(final int userId, final IStopUserCallback stopUserCallback, + private void stopSingleUserLU(final int userId, boolean allowDelayedLocking, + final IStopUserCallback stopUserCallback, KeyEvictedCallback keyEvictedCallback) { if (DEBUG_MU) Slog.i(TAG, "stopSingleUserLocked userId=" + userId); final UserState uss = mStartedUsers.get(userId); - if (uss == null) { - // User is not started, nothing to do... but we do need to - // callback if requested. + if (uss == null) { // User is not started + // If mDelayUserDataLocking is set and allowDelayedLocking is not set, we need to lock + // the requested user as the client wants to stop and lock the user. On the other hand, + // having keyEvictedCallback set will lead into locking user if mDelayUserDataLocking + // is set as that means client wants to lock the user immediately. + // If mDelayUserDataLocking is not set, the user was already locked when it was stopped + // and no further action is necessary. + if (mDelayUserDataLocking) { + if (allowDelayedLocking && keyEvictedCallback != null) { + Slog.wtf(TAG, "allowDelayedLocking set with KeyEvictedCallback, ignore it" + + " and lock user:" + userId, new RuntimeException()); + allowDelayedLocking = false; + } + if (!allowDelayedLocking) { + if (mLastActiveUsers.remove(Integer.valueOf(userId))) { + // should lock the user, user is already gone + final ArrayList<KeyEvictedCallback> keyEvictedCallbacks; + if (keyEvictedCallback != null) { + keyEvictedCallbacks = new ArrayList<>(1); + keyEvictedCallbacks.add(keyEvictedCallback); + } else { + keyEvictedCallbacks = null; + } + dispatchUserLocking(userId, keyEvictedCallbacks); + } + } + } + // We do need to post the stopped callback even though user is already stopped. if (stopUserCallback != null) { mHandler.post(() -> { try { @@ -704,6 +783,7 @@ class UserController implements Handler.Callback { mInjector.getUserManagerInternal().setUserState(userId, uss.state); updateStartedUserArrayLU(); + final boolean allowDelayyLockingCopied = allowDelayedLocking; // Post to handler to obtain amLock mHandler.post(() -> { // We are going to broadcast ACTION_USER_STOPPING and then @@ -718,7 +798,8 @@ class UserController implements Handler.Callback { @Override public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) { - mHandler.post(() -> finishUserStopping(userId, uss)); + mHandler.post(() -> finishUserStopping(userId, uss, + allowDelayyLockingCopied)); } }; @@ -734,7 +815,8 @@ class UserController implements Handler.Callback { } } - void finishUserStopping(final int userId, final UserState uss) { + void finishUserStopping(final int userId, final UserState uss, + final boolean allowDelayedLocking) { Slog.d(TAG, "UserController event: finishUserStopping(" + userId + ")"); // On to the next. final Intent shutdownIntent = new Intent(Intent.ACTION_SHUTDOWN); @@ -746,7 +828,7 @@ class UserController implements Handler.Callback { mHandler.post(new Runnable() { @Override public void run() { - finishUserStopped(uss); + finishUserStopped(uss, allowDelayedLocking); } }); } @@ -773,7 +855,7 @@ class UserController implements Handler.Callback { Binder.getCallingPid(), userId); } - void finishUserStopped(UserState uss) { + void finishUserStopped(UserState uss, boolean allowDelayedLocking) { final int userId = uss.mHandle.getIdentifier(); Slog.d(TAG, "UserController event: finishUserStopped(" + userId + ")"); final boolean stopped; @@ -792,7 +874,13 @@ class UserController implements Handler.Callback { mStartedUsers.remove(userId); mUserLru.remove(Integer.valueOf(userId)); updateStartedUserArrayLU(); - userIdToLock = updateUserToLockLU(userId); + if (allowDelayedLocking && !keyEvictedCallbacks.isEmpty()) { + Slog.wtf(TAG, + "Delayed locking enabled while KeyEvictedCallbacks not empty, userId:" + + userId + " callbacks:" + keyEvictedCallbacks); + allowDelayedLocking = false; + } + userIdToLock = updateUserToLockLU(userId, allowDelayedLocking); if (userIdToLock == UserHandle.USER_NULL) { lockUser = false; } @@ -826,31 +914,36 @@ class UserController implements Handler.Callback { if (!lockUser) { return; } - final int userIdToLockF = userIdToLock; - // Evict the user's credential encryption key. Performed on FgThread to make it - // serialized with call to UserManagerService.onBeforeUnlockUser in finishUserUnlocking - // to prevent data corruption. - FgThread.getHandler().post(() -> { - synchronized (mLock) { - if (mStartedUsers.get(userIdToLockF) != null) { - Slog.w(TAG, "User was restarted, skipping key eviction"); - return; - } - } - try { - mInjector.getStorageManager().lockUserKey(userIdToLockF); - } catch (RemoteException re) { - throw re.rethrowAsRuntimeException(); - } - if (userIdToLockF == userId) { - for (final KeyEvictedCallback callback : keyEvictedCallbacks) { - callback.keyEvicted(userId); - } - } - }); + dispatchUserLocking(userIdToLock, keyEvictedCallbacks); } } + private void dispatchUserLocking(@UserIdInt int userId, + @Nullable List<KeyEvictedCallback> keyEvictedCallbacks) { + // Evict the user's credential encryption key. Performed on FgThread to make it + // serialized with call to UserManagerService.onBeforeUnlockUser in finishUserUnlocking + // to prevent data corruption. + FgThread.getHandler().post(() -> { + synchronized (mLock) { + if (mStartedUsers.get(userId) != null) { + Slog.w(TAG, "User was restarted, skipping key eviction"); + return; + } + } + try { + mInjector.getStorageManager().lockUserKey(userId); + } catch (RemoteException re) { + throw re.rethrowAsRuntimeException(); + } + if (keyEvictedCallbacks == null) { + return; + } + for (int i = 0; i < keyEvictedCallbacks.size(); i++) { + keyEvictedCallbacks.get(i).keyEvicted(userId); + } + }); + } + /** * For mDelayUserDataLocking mode, storage once unlocked is kept unlocked. * Total number of unlocked user storage is limited by mMaxRunningUsers. @@ -861,9 +954,9 @@ class UserController implements Handler.Callback { * @return user id to lock. UserHandler.USER_NULL will be returned if no user should be locked. */ @GuardedBy("mLock") - private int updateUserToLockLU(@UserIdInt int userId) { + private int updateUserToLockLU(@UserIdInt int userId, boolean allowDelayedLocking) { int userIdToLock = userId; - if (mDelayUserDataLocking && !getUserInfo(userId).isEphemeral() + if (mDelayUserDataLocking && allowDelayedLocking && !getUserInfo(userId).isEphemeral() && !hasUserRestriction(UserManager.DISALLOW_RUN_IN_BACKGROUND, userId)) { mLastActiveUsers.remove((Integer) userId); // arg should be object, not index mLastActiveUsers.add(0, userId); @@ -945,7 +1038,8 @@ class UserController implements Handler.Callback { if (userInfo.isGuest() || userInfo.isEphemeral()) { // This is a user to be stopped. synchronized (mLock) { - stopUsersLU(oldUserId, true, null, null); + stopUsersLU(oldUserId, /* force= */ true, /* allowDelayedLocking= */ false, + null, null); } } } @@ -977,7 +1071,7 @@ class UserController implements Handler.Callback { } final int profilesToStartSize = profilesToStart.size(); int i = 0; - for (; i < profilesToStartSize && i < (mMaxRunningUsers - 1); ++i) { + for (; i < profilesToStartSize && i < (getMaxRunningUsers() - 1); ++i) { startUser(profilesToStart.get(i).id, /* foreground= */ false); } if (i < profilesToStartSize) { @@ -1094,7 +1188,7 @@ class UserController implements Handler.Callback { return false; } - if (foreground && mUserSwitchUiEnabled) { + if (foreground && isUserSwitchUiEnabled()) { t.traceBegin("startFreezingScreen"); mInjector.getWindowManager().startFreezingScreen( R.anim.screen_user_exit, R.anim.screen_user_enter); @@ -1142,9 +1236,11 @@ class UserController implements Handler.Callback { if (foreground) { // Make sure the old user is no longer considering the display to be on. mInjector.reportGlobalUsageEventLocked(UsageEvents.Event.SCREEN_NON_INTERACTIVE); + boolean userSwitchUiEnabled; synchronized (mLock) { mCurrentUserId = userId; mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up + userSwitchUiEnabled = mUserSwitchUiEnabled; } mInjector.updateUserConfiguration(); updateCurrentProfileIds(); @@ -1152,7 +1248,7 @@ class UserController implements Handler.Callback { mInjector.reportCurWakefulnessUsageEvent(); // Once the internal notion of the active user has switched, we lock the device // with the option to show the user switcher on the keyguard. - if (mUserSwitchUiEnabled) { + if (userSwitchUiEnabled) { mInjector.getWindowManager().setSwitchingUser(true); mInjector.getWindowManager().lockNow(null); } @@ -1391,10 +1487,12 @@ class UserController implements Handler.Callback { Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not a full user"); return false; } + boolean userSwitchUiEnabled; synchronized (mLock) { mTargetUserId = targetUserId; + userSwitchUiEnabled = mUserSwitchUiEnabled; } - if (mUserSwitchUiEnabled) { + if (userSwitchUiEnabled) { UserInfo currentUserInfo = getUserInfo(currentUserId); Pair<UserInfo, UserInfo> userNames = new Pair<>(currentUserInfo, targetUserInfo); mUiHandler.removeMessages(START_USER_SWITCH_UI_MSG); @@ -1458,14 +1556,15 @@ class UserController implements Handler.Callback { } // If running in background is disabled or mDelayUserDataLocking mode, stop the user. boolean disallowRunInBg = hasUserRestriction(UserManager.DISALLOW_RUN_IN_BACKGROUND, - oldUserId) || mDelayUserDataLocking; + oldUserId) || isDelayUserDataLockingEnabled(); if (!disallowRunInBg) { return; } synchronized (mLock) { if (DEBUG_MU) Slog.i(TAG, "stopBackgroundUsersIfEnforced stopping " + oldUserId + " and related users"); - stopUsersLU(oldUserId, false, null, null); + stopUsersLU(oldUserId, /* force= */ false, /* allowDelayedLocking= */ true, + null, null); } } @@ -1551,7 +1650,7 @@ class UserController implements Handler.Callback { void continueUserSwitch(UserState uss, int oldUserId, int newUserId) { Slog.d(TAG, "Continue user switch oldUser #" + oldUserId + ", newUser #" + newUserId); - if (mUserSwitchUiEnabled) { + if (isUserSwitchUiEnabled()) { mInjector.getWindowManager().stopFreezingScreen(); } uss.switching = false; diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index cd2272aa421c..eedeeea5cdb3 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -2193,8 +2193,8 @@ public class AudioService extends IAudioService.Stub } private void enforceModifyAudioRoutingPermission() { - if (mContext.checkCallingPermission( - android.Manifest.permission.MODIFY_AUDIO_ROUTING) + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.MODIFY_AUDIO_ROUTING) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Missing MODIFY_AUDIO_ROUTING permission"); } @@ -6427,7 +6427,7 @@ public class AudioService extends IAudioService.Stub return false; } boolean suppress = false; - if (resolvedStream == DEFAULT_VOL_STREAM_NO_PLAYBACK && mController != null) { + if (resolvedStream != AudioSystem.STREAM_MUSIC && mController != null) { final long now = SystemClock.uptimeMillis(); if ((flags & AudioManager.FLAG_SHOW_UI) != 0 && !mVisible) { // ui will become visible diff --git a/services/core/java/com/android/server/connectivity/DataConnectionStats.java b/services/core/java/com/android/server/connectivity/DataConnectionStats.java index 1b1c54682255..5010e46a74eb 100644 --- a/services/core/java/com/android/server/connectivity/DataConnectionStats.java +++ b/services/core/java/com/android/server/connectivity/DataConnectionStats.java @@ -16,6 +16,9 @@ package com.android.server.connectivity; +import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN; +import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -24,6 +27,7 @@ import android.net.ConnectivityManager; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; +import android.telephony.NetworkRegistrationInfo; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SignalStrength; @@ -91,7 +95,10 @@ public class DataConnectionStats extends BroadcastReceiver { boolean visible = (simReadyOrUnknown || isCdma()) // we only check the sim state for GSM && hasService() && mDataState == TelephonyManager.DATA_CONNECTED; - int networkType = mServiceState.getDataNetworkType(); + NetworkRegistrationInfo regInfo = + mServiceState.getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN); + int networkType = regInfo == null ? TelephonyManager.NETWORK_TYPE_UNKNOWN + : regInfo.getAccessNetworkTechnology(); if (DEBUG) Log.d(TAG, String.format("Noting data connection for network type %s: %svisible", networkType, visible ? "" : "not ")); try { diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 8498dcb32eb1..decb1f9bbcf7 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -73,7 +73,7 @@ public class DisplayModeDirector { private static final int GLOBAL_ID = -1; // The tolerance within which we consider something approximately equals. - private static final float EPSILON = 0.001f; + private static final float EPSILON = 0.01f; private final Object mLock = new Object(); private final Context mContext; diff --git a/services/core/java/com/android/server/integrity/model/BitOutputStream.java b/services/core/java/com/android/server/integrity/model/BitOutputStream.java index b8ea041c4196..da778aa696a4 100644 --- a/services/core/java/com/android/server/integrity/model/BitOutputStream.java +++ b/services/core/java/com/android/server/integrity/model/BitOutputStream.java @@ -16,17 +16,25 @@ package com.android.server.integrity.model; -import java.util.BitSet; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; /** A wrapper class for writing a stream of bits. */ public class BitOutputStream { - private BitSet mBitSet; - private int mIndex; + private static final int BUFFER_SIZE = 4 * 1024; + private static final int BYTE_BITS = 8; - public BitOutputStream() { - mBitSet = new BitSet(); - mIndex = 0; + private int mNextBitIndex; + + private final OutputStream mOutputStream; + private final byte[] mBuffer; + + public BitOutputStream(OutputStream outputStream) { + mBuffer = new byte[BUFFER_SIZE]; + mNextBitIndex = 0; + mOutputStream = outputStream; } /** @@ -35,15 +43,17 @@ public class BitOutputStream { * @param numOfBits The number of bits used to represent the value. * @param value The value to convert to bits. */ - public void setNext(int numOfBits, int value) { + public void setNext(int numOfBits, int value) throws IOException { if (numOfBits <= 0) { return; } - int offset = 1 << (numOfBits - 1); + + // optional: we can do some clever size checking to "OR" an entire segment of bits instead + // of setting bits one by one, but it is probably not worth it. + int nextBitMask = 1 << (numOfBits - 1); while (numOfBits-- > 0) { - mBitSet.set(mIndex, (value & offset) != 0); - offset >>>= 1; - mIndex++; + setNext((value & nextBitMask) != 0); + nextBitMask >>>= 1; } } @@ -52,35 +62,43 @@ public class BitOutputStream { * * @param value The value to set the bit to. */ - public void setNext(boolean value) { - mBitSet.set(mIndex, value); - mIndex++; + public void setNext(boolean value) throws IOException { + int byteToWrite = mNextBitIndex / BYTE_BITS; + if (byteToWrite == BUFFER_SIZE) { + mOutputStream.write(mBuffer); + reset(); + byteToWrite = 0; + } + if (value) { + mBuffer[byteToWrite] |= 1 << (BYTE_BITS - 1 - (mNextBitIndex % BYTE_BITS)); + } + mNextBitIndex++; } /** Set the next bit in the stream to true. */ - public void setNext() { + public void setNext() throws IOException { setNext(/* value= */ true); } - /** Convert BitSet in big-endian to ByteArray in big-endian. */ - public byte[] toByteArray() { - int bitSetSize = mBitSet.length(); - int numOfBytes = bitSetSize / 8; - if (bitSetSize % 8 != 0) { - numOfBytes++; - } - byte[] bytes = new byte[numOfBytes]; - for (int i = 0; i < mBitSet.length(); i++) { - if (mBitSet.get(i)) { - bytes[i / 8] |= 1 << (7 - (i % 8)); - } + /** + * Flush the data written to the underlying {@link java.io.OutputStream}. Any unfinished bytes + * will be padded with 0. + */ + public void flush() throws IOException { + int endByte = mNextBitIndex / BYTE_BITS; + if (mNextBitIndex % BYTE_BITS != 0) { + // If next bit is not the first bit of a byte, then mNextBitIndex / BYTE_BITS would be + // the byte that includes already written bits. We need to increment it so this byte + // gets written. + endByte++; } - return bytes; + mOutputStream.write(mBuffer, 0, endByte); + reset(); } - /** Clear the stream. */ - public void clear() { - mBitSet.clear(); - mIndex = 0; + /** Reset this output stream to start state. */ + private void reset() { + mNextBitIndex = 0; + Arrays.fill(mBuffer, (byte) 0); } } diff --git a/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java b/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java index f575599e1c49..0d6807a2fd8b 100644 --- a/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java +++ b/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java @@ -23,31 +23,44 @@ import java.io.OutputStream; * An output stream that tracks the total number written bytes since construction and allows * querying this value any time during the execution. * - * This class is used for constructing the rule indexing. + * <p>This class is used for constructing the rule indexing. */ -public class ByteTrackedOutputStream { +public class ByteTrackedOutputStream extends OutputStream { - private static int sWrittenBytesCount; - private static OutputStream sOutputStream; + private static final int INT_BYTES = 4; + + private int mWrittenBytesCount; + private final OutputStream mOutputStream; public ByteTrackedOutputStream(OutputStream outputStream) { - sWrittenBytesCount = 0; - sOutputStream = outputStream; + mWrittenBytesCount = 0; + mOutputStream = outputStream; + } + + @Override + public void write(int b) throws IOException { + mWrittenBytesCount += INT_BYTES; + mOutputStream.write(b); } /** - * Writes the given bytes into the output stream provided in constructor and updates the - * total number of written bytes. + * Writes the given bytes into the output stream provided in constructor and updates the total + * number of written bytes. */ + @Override public void write(byte[] bytes) throws IOException { - sWrittenBytesCount += bytes.length; - sOutputStream.write(bytes); + mWrittenBytesCount += bytes.length; + mOutputStream.write(bytes); } - /** - * Returns the total number of bytes written into the output stream at the requested time. - */ + @Override + public void write(byte[] b, int off, int len) throws IOException { + mWrittenBytesCount += len; + mOutputStream.write(b, off, len); + } + + /** Returns the total number of bytes written into the output stream at the requested time. */ public int getWrittenBytesCount() { - return sWrittenBytesCount; + return mWrittenBytesCount; } } diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java index 8f53be7d87af..6afadbad2054 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java @@ -97,42 +97,38 @@ public class RuleBinarySerializer implements RuleSerializer { ruleFileByteTrackedOutputStream); LinkedHashMap<String, Integer> unindexedRulesIndexes = serializeRuleList( - indexedRules.get(NOT_INDEXED), - ruleFileByteTrackedOutputStream); + indexedRules.get(NOT_INDEXED), ruleFileByteTrackedOutputStream); // Serialize their indexes. - BitOutputStream indexingBitOutputStream = new BitOutputStream(); - serializeIndexGroup(packageNameIndexes, indexingBitOutputStream, /* isIndexed= */true); - serializeIndexGroup(appCertificateIndexes, indexingBitOutputStream, /* isIndexed= */ - true); - serializeIndexGroup(unindexedRulesIndexes, indexingBitOutputStream, /* isIndexed= */ - false); - // TODO(b/147609625): This dummy bit is set for fixing the padding issue. Remove when - // the issue is fixed and correct the tests that does this padding too. - indexingBitOutputStream.setNext(); - indexingFileOutputStream.write(indexingBitOutputStream.toByteArray()); + BitOutputStream indexingBitOutputStream = new BitOutputStream(indexingFileOutputStream); + serializeIndexGroup(packageNameIndexes, indexingBitOutputStream, /* isIndexed= */ true); + serializeIndexGroup( + appCertificateIndexes, indexingBitOutputStream, /* isIndexed= */ true); + serializeIndexGroup( + unindexedRulesIndexes, indexingBitOutputStream, /* isIndexed= */ false); + indexingBitOutputStream.flush(); } catch (Exception e) { throw new RuleSerializeException(e.getMessage(), e); } } - private void serializeRuleFileMetadata(Optional<Integer> formatVersion, - ByteTrackedOutputStream outputStream) + private void serializeRuleFileMetadata( + Optional<Integer> formatVersion, ByteTrackedOutputStream outputStream) throws IOException { int formatVersionValue = formatVersion.orElse(DEFAULT_FORMAT_VERSION); - BitOutputStream bitOutputStream = new BitOutputStream(); + BitOutputStream bitOutputStream = new BitOutputStream(outputStream); bitOutputStream.setNext(FORMAT_VERSION_BITS, formatVersionValue); - outputStream.write(bitOutputStream.toByteArray()); + bitOutputStream.flush(); } private LinkedHashMap<String, Integer> serializeRuleList( Map<String, List<Rule>> rulesMap, ByteTrackedOutputStream outputStream) throws IOException { - Preconditions.checkArgument(rulesMap != null, - "serializeRuleList should never be called with null rule list."); + Preconditions.checkArgument( + rulesMap != null, "serializeRuleList should never be called with null rule list."); - BitOutputStream bitOutputStream = new BitOutputStream(); + BitOutputStream bitOutputStream = new BitOutputStream(outputStream); LinkedHashMap<String, Integer> indexMapping = new LinkedHashMap(); indexMapping.put(START_INDEXING_KEY, outputStream.getWrittenBytesCount()); @@ -145,9 +141,8 @@ public class RuleBinarySerializer implements RuleSerializer { } for (Rule rule : rulesMap.get(key)) { - bitOutputStream.clear(); serializeRule(rule, bitOutputStream); - outputStream.write(bitOutputStream.toByteArray()); + bitOutputStream.flush(); indexTracker++; } } @@ -156,7 +151,7 @@ public class RuleBinarySerializer implements RuleSerializer { return indexMapping; } - private void serializeRule(Rule rule, BitOutputStream bitOutputStream) { + private void serializeRule(Rule rule, BitOutputStream bitOutputStream) throws IOException { if (rule == null) { throw new IllegalArgumentException("Null rule can not be serialized"); } @@ -171,7 +166,8 @@ public class RuleBinarySerializer implements RuleSerializer { bitOutputStream.setNext(); } - private void serializeFormula(Formula formula, BitOutputStream bitOutputStream) { + private void serializeFormula(Formula formula, BitOutputStream bitOutputStream) + throws IOException { if (formula instanceof AtomicFormula) { serializeAtomicFormula((AtomicFormula) formula, bitOutputStream); } else if (formula instanceof CompoundFormula) { @@ -183,7 +179,7 @@ public class RuleBinarySerializer implements RuleSerializer { } private void serializeCompoundFormula( - CompoundFormula compoundFormula, BitOutputStream bitOutputStream) { + CompoundFormula compoundFormula, BitOutputStream bitOutputStream) throws IOException { if (compoundFormula == null) { throw new IllegalArgumentException("Null compound formula can not be serialized"); } @@ -197,7 +193,7 @@ public class RuleBinarySerializer implements RuleSerializer { } private void serializeAtomicFormula( - AtomicFormula atomicFormula, BitOutputStream bitOutputStream) { + AtomicFormula atomicFormula, BitOutputStream bitOutputStream) throws IOException { if (atomicFormula == null) { throw new IllegalArgumentException("Null atomic formula can not be serialized"); } @@ -231,11 +227,10 @@ public class RuleBinarySerializer implements RuleSerializer { private void serializeIndexGroup( LinkedHashMap<String, Integer> indexes, BitOutputStream bitOutputStream, - boolean isIndexed) { - + boolean isIndexed) + throws IOException { // Output the starting location of this indexing group. - serializeStringValue( - START_INDEXING_KEY, /* isHashedValue= */false, bitOutputStream); + serializeStringValue(START_INDEXING_KEY, /* isHashedValue= */ false, bitOutputStream); serializeIntValue(indexes.get(START_INDEXING_KEY), bitOutputStream); // If the group is indexed, output the locations of the indexes. @@ -243,8 +238,8 @@ public class RuleBinarySerializer implements RuleSerializer { for (Map.Entry<String, Integer> entry : indexes.entrySet()) { if (!entry.getKey().equals(START_INDEXING_KEY) && !entry.getKey().equals(END_INDEXING_KEY)) { - serializeStringValue(entry.getKey(), /* isHashedValue= */false, - bitOutputStream); + serializeStringValue( + entry.getKey(), /* isHashedValue= */ false, bitOutputStream); serializeIntValue(entry.getValue(), bitOutputStream); } } @@ -256,7 +251,8 @@ public class RuleBinarySerializer implements RuleSerializer { } private void serializeStringValue( - String value, boolean isHashedValue, BitOutputStream bitOutputStream) { + String value, boolean isHashedValue, BitOutputStream bitOutputStream) + throws IOException { if (value == null) { throw new IllegalArgumentException("String value can not be null."); } @@ -269,11 +265,12 @@ public class RuleBinarySerializer implements RuleSerializer { } } - private void serializeIntValue(int value, BitOutputStream bitOutputStream) { + private void serializeIntValue(int value, BitOutputStream bitOutputStream) throws IOException { bitOutputStream.setNext(/* numOfBits= */ 32, value); } - private void serializeBooleanValue(boolean value, BitOutputStream bitOutputStream) { + private void serializeBooleanValue(boolean value, BitOutputStream bitOutputStream) + throws IOException { bitOutputStream.setNext(value); } diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java index 5191833f367e..b67335ab82bc 100644 --- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java +++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java @@ -306,14 +306,17 @@ class BluetoothRouteProvider { } } else if (state == BluetoothProfile.STATE_DISCONNECTING || state == BluetoothProfile.STATE_DISCONNECTED) { - btRoute.connectedProfiles.delete(profile); - if (btRoute.connectedProfiles.size() == 0) { - mBluetoothRoutes.remove(device.getAddress()); - if (mActiveDevice != null - && TextUtils.equals(mActiveDevice.getAddress(), device.getAddress())) { - mActiveDevice = null; + if (btRoute != null) { + btRoute.connectedProfiles.delete(profile); + if (btRoute.connectedProfiles.size() == 0) { + mBluetoothRoutes.remove(device.getAddress()); + if (mActiveDevice != null + && TextUtils.equals(mActiveDevice.getAddress(), + device.getAddress())) { + mActiveDevice = null; + } + notifyBluetoothRoutesUpdated(); } - notifyBluetoothRoutesUpdated(); } } } diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index df115d0f2773..190557122aa4 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -135,6 +135,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR private int mMaxVolume = 0; private int mCurrentVolume = 0; private int mOptimisticVolume = -1; + private String mVolumeControlId; // End volume handling fields private boolean mIsActive = false; @@ -714,7 +715,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_REMOTE) { int current = mOptimisticVolume != -1 ? mOptimisticVolume : mCurrentVolume; return new PlaybackInfo(mVolumeType, mVolumeControlType, mMaxVolume, current, - mAudioAttrs); + mAudioAttrs, mVolumeControlId); } volumeType = mVolumeType; attributes = mAudioAttrs; @@ -723,7 +724,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR int max = mAudioManager.getStreamMaxVolume(stream); int current = mAudioManager.getStreamVolume(stream); return new PlaybackInfo(volumeType, VolumeProvider.VOLUME_CONTROL_ABSOLUTE, max, - current, attributes); + current, attributes, null); } private final Runnable mClearOptimisticVolumeRunnable = new Runnable() { @@ -886,6 +887,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR synchronized (mLock) { typeChanged = mVolumeType == PlaybackInfo.PLAYBACK_TYPE_REMOTE; mVolumeType = PlaybackInfo.PLAYBACK_TYPE_LOCAL; + mVolumeControlId = null; if (attributes != null) { mAudioAttrs = attributes; } else { @@ -904,13 +906,15 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR } @Override - public void setPlaybackToRemote(int control, int max) throws RemoteException { + public void setPlaybackToRemote(int control, int max, String controlId) + throws RemoteException { boolean typeChanged; synchronized (mLock) { typeChanged = mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL; mVolumeType = PlaybackInfo.PLAYBACK_TYPE_REMOTE; mVolumeControlType = control; mMaxVolume = max; + mVolumeControlId = controlId; } if (typeChanged) { final long token = Binder.clearCallingIdentity(); diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index d8a4655815ad..b291a2efda8b 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -2877,17 +2877,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @Override - public void onTetheringChanged(String iface, boolean tethering) { - // No need to enforce permission because setRestrictBackground() will do it. - synchronized (mUidRulesFirstLock) { - if (mRestrictBackground && tethering) { - Log.d(TAG, "Tethering on (" + iface +"); disable Data Saver"); - setRestrictBackground(false); - } - } - } - - @Override public void setRestrictBackground(boolean restrictBackground) { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "setRestrictBackground"); try { @@ -4522,7 +4511,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } case MSG_STATS_PROVIDER_LIMIT_REACHED: { mNetworkStats.forceUpdate(); + synchronized (mNetworkPoliciesSecondLock) { + // Some providers might hit the limit reached event prior to others. Thus, + // re-calculate and update interface quota for every provider is needed. + updateNetworkRulesNL(); updateNetworkEnabledNL(); updateNotificationsNL(); } @@ -4530,17 +4523,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } case MSG_LIMIT_REACHED: { final String iface = (String) msg.obj; - synchronized (mNetworkPoliciesSecondLock) { - if (mMeteredIfaces.contains(iface)) { - // force stats update to make sure we have - // numbers that caused alert to trigger. - mNetworkStats.forceUpdate(); - - updateNetworkEnabledNL(); - updateNotificationsNL(); + // fast return if not needed. + if (!mMeteredIfaces.contains(iface)) { + return true; } } + + // force stats update to make sure the service have the numbers that caused + // alert to trigger. + mNetworkStats.forceUpdate(); + + synchronized (mNetworkPoliciesSecondLock) { + updateNetworkRulesNL(); + updateNetworkEnabledNL(); + updateNotificationsNL(); + } return true; } case MSG_RESTRICT_BACKGROUND_CHANGED: { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 159b4e49bfea..410017a99182 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -190,6 +190,7 @@ import android.content.pm.SELinuxUtil; import android.content.pm.ServiceInfo; import android.content.pm.SharedLibraryInfo; import android.content.pm.Signature; +import android.content.pm.SigningInfo; import android.content.pm.SuspendDialogInfo; import android.content.pm.UserInfo; import android.content.pm.VerifierDeviceIdentity; @@ -20021,8 +20022,9 @@ public class PackageManagerService extends IPackageManager.Stub String initiatingPackageName; String originatingPackageName; + final InstallSource installSource; synchronized (mLock) { - final InstallSource installSource = getInstallSourceLocked(packageName, callingUid); + installSource = getInstallSourceLocked(packageName, callingUid); if (installSource == null) { return null; } @@ -20036,9 +20038,16 @@ public class PackageManagerService extends IPackageManager.Stub } if (installSource.isInitiatingPackageUninstalled) { - // TODO(b/146555198) Allow the app itself to see the info - // (at least for non-instant apps) - initiatingPackageName = null; + // We can't check visibility in the usual way, since the initiating package is no + // longer present. So we apply simpler rules to whether to expose the info: + // 1. Instant apps can't see it. + // 2. Otherwise only the installed app itself can see it. + final boolean isInstantApp = getInstantAppPackageName(callingUid) != null; + if (!isInstantApp && isCallerSameApp(packageName, callingUid)) { + initiatingPackageName = installSource.initiatingPackageName; + } else { + initiatingPackageName = null; + } } else { // All installSource strings are interned, so == is ok here if (installSource.initiatingPackageName == installSource.installerPackageName) { @@ -20063,13 +20072,27 @@ public class PackageManagerService extends IPackageManager.Stub } } + // Remaining work can safely be done outside the lock. (Note that installSource is + // immutable so it's ok to carry on reading from it.) + if (originatingPackageName != null && mContext.checkCallingOrSelfPermission( Manifest.permission.INSTALL_PACKAGES) != PackageManager.PERMISSION_GRANTED) { originatingPackageName = null; } - return new InstallSourceInfo(initiatingPackageName, originatingPackageName, - installerPackageName); + // If you can see the initiatingPackageName, and we have valid signing info for it, + // then we let you see that too. + final SigningInfo initiatingPackageSigningInfo; + final PackageSignatures signatures = installSource.initiatingPackageSignatures; + if (initiatingPackageName != null && signatures != null + && signatures.mSigningDetails != SigningDetails.UNKNOWN) { + initiatingPackageSigningInfo = new SigningInfo(signatures.mSigningDetails); + } else { + initiatingPackageSigningInfo = null; + } + + return new InstallSourceInfo(initiatingPackageName, initiatingPackageSigningInfo, + originatingPackageName, installerPackageName); } @GuardedBy("mLock") @@ -22267,6 +22290,13 @@ public class PackageManagerService extends IPackageManager.Stub mPermissionManager.onNewUserCreated(userId); } + boolean readPermissionStateForUser(@UserIdInt int userId) { + synchronized (mPackages) { + mSettings.readPermissionStateForUserSyncLPr(userId); + return mSettings.areDefaultRuntimePermissionsGrantedLPr(userId); + } + } + @Override public VerifierDeviceIdentity getVerifierDeviceIdentity() throws RemoteException { mContext.enforceCallingOrSelfPermission( diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 3e665c324f0d..4f18cb43cc94 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -3125,6 +3125,10 @@ public final class Settings { return true; } + void readPermissionStateForUserSyncLPr(@UserIdInt int userId) { + mRuntimePermissionsPersistence.readStateForUserSyncLPr(userId); + } + void applyDefaultPreferredAppsLPw(int userId) { // First pull data from any pre-installed apps. final PackageManagerInternal pmInternal = diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 2265d010216e..7888d1f9612f 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -49,6 +49,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; +import android.os.ParcelableException; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; @@ -570,18 +571,17 @@ public class StagingManager { } else { params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION; } - int apkSessionId = mPi.createSession( - params, originalSession.getInstallerPackageName(), - 0 /* UserHandle.SYSTEM */); - PackageInstallerSession apkSession = mPi.getSession(apkSessionId); - try { + int apkSessionId = mPi.createSession( + params, originalSession.getInstallerPackageName(), + 0 /* UserHandle.SYSTEM */); + PackageInstallerSession apkSession = mPi.getSession(apkSessionId); apkSession.open(); for (String apkFilePath : apkFilePaths) { File apkFile = new File(apkFilePath); ParcelFileDescriptor pfd = ParcelFileDescriptor.open(apkFile, ParcelFileDescriptor.MODE_READ_ONLY); - long sizeBytes = pfd.getStatSize(); + long sizeBytes = (pfd == null) ? -1 : pfd.getStatSize(); if (sizeBytes < 0) { Slog.e(TAG, "Unable to get size of: " + apkFilePath); throw new PackageManagerException(errorCode, @@ -589,11 +589,11 @@ public class StagingManager { } apkSession.write(apkFile.getName(), 0, sizeBytes, pfd); } - } catch (IOException e) { + return apkSession; + } catch (IOException | ParcelableException e) { Slog.e(TAG, "Failure to install APK staged session " + originalSession.sessionId, e); - throw new PackageManagerException(errorCode, "Failed to write APK session", e); + throw new PackageManagerException(errorCode, "Failed to create/write APK session", e); } - return apkSession; } /** diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index e5d5b57113c0..6ee864880716 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -492,6 +492,10 @@ public class UserManagerService extends IUserManager.Stub { public void onBootPhase(int phase) { if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { mUms.cleanupPartialUsers(); + + if (mUms.mPm.isDeviceUpgrading()) { + mUms.cleanupPreCreatedUsers(); + } } } @@ -617,6 +621,33 @@ public class UserManagerService extends IUserManager.Stub { } } + /** + * Removes any pre-created users from the system. Should be invoked after OTAs, to ensure + * pre-created users are not stale. New pre-created pool can be re-created after the update. + */ + void cleanupPreCreatedUsers() { + final ArrayList<UserInfo> preCreatedUsers; + synchronized (mUsersLock) { + final int userSize = mUsers.size(); + preCreatedUsers = new ArrayList<>(userSize); + for (int i = 0; i < userSize; i++) { + UserInfo ui = mUsers.valueAt(i).info; + if (ui.preCreated) { + preCreatedUsers.add(ui); + addRemovingUserIdLocked(ui.id); + ui.flags |= UserInfo.FLAG_DISABLED; + ui.partial = true; + } + } + } + final int preCreatedSize = preCreatedUsers.size(); + for (int i = 0; i < preCreatedSize; i++) { + UserInfo ui = preCreatedUsers.get(i); + Slog.i(LOG_TAG, "Removing pre-created user " + ui.id); + removeUserState(ui.id); + } + } + @Override public String getUserAccount(@UserIdInt int userId) { checkManageUserAndAcrossUsersFullPermission("get user account"); @@ -3078,7 +3109,6 @@ public class UserManagerService extends IUserManager.Stub { @NonNull String userType, @UserInfoFlag int flags, @UserIdInt int parentId, boolean preCreate, @Nullable String[] disallowedPackages, @NonNull TimingsTraceAndSlog t) { - final UserTypeDetails userTypeDetails = mUserTypes.get(userType); if (userTypeDetails == null) { Slog.e(LOG_TAG, "Cannot create user of invalid user type: " + userType); @@ -3254,9 +3284,9 @@ public class UserManagerService extends IUserManager.Stub { mBaseUserRestrictions.append(userId, restrictions); } - t.traceBegin("PM.onNewUserCreated"); + t.traceBegin("PM.onNewUserCreated-" + userId); mPm.onNewUserCreated(userId); - + t.traceEnd(); if (preCreate) { // Must start user (which will be stopped right away, through // UserController.finishUserUnlockedCompleted) so services can properly @@ -3323,11 +3353,16 @@ public class UserManagerService extends IUserManager.Stub { preCreatedUser.preCreated = false; preCreatedUser.creationTime = getCreationTime(); - dispatchUserAddedIntent(preCreatedUser); synchronized (mPackagesLock) { writeUserLP(preCreatedUserData); writeUserListLP(); } + updateUserIds(); + if (!mPm.readPermissionStateForUser(preCreatedUser.id)) { + // Could not read the existing permissions, re-grant them. + mPm.onNewUserCreated(preCreatedUser.id); + } + dispatchUserAddedIntent(preCreatedUser); return preCreatedUser; } @@ -4027,14 +4062,16 @@ public class UserManagerService extends IUserManager.Stub { synchronized (mUsersLock) { final int userSize = mUsers.size(); for (int i = 0; i < userSize; i++) { - if (!mUsers.valueAt(i).info.partial) { + UserInfo userInfo = mUsers.valueAt(i).info; + if (!userInfo.partial && !userInfo.preCreated) { num++; } } final int[] newUsers = new int[num]; int n = 0; for (int i = 0; i < userSize; i++) { - if (!mUsers.valueAt(i).info.partial) { + UserInfo userInfo = mUsers.valueAt(i).info; + if (!userInfo.partial && !userInfo.preCreated) { newUsers[n++] = mUsers.keyAt(i); } } @@ -4095,7 +4132,10 @@ public class UserManagerService extends IUserManager.Stub { * recycled. */ void reconcileUsers(String volumeUuid) { - mUserDataPreparer.reconcileUsers(volumeUuid, getUsers(true /* excludeDying */)); + mUserDataPreparer.reconcileUsers(volumeUuid, getUsers( + /* excludePartial= */ true, + /* excludeDying= */ true, + /* excludePreCreated= */ false)); } /** diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index 815f7b4357bf..89030ed7c075 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -199,13 +199,31 @@ public class UserRestrictionsUtils { ); /** - * Special user restrictions that are applied globally when set by the profile owner of a - * managed profile that was created during the device provisioning flow. + * Special user restrictions that profile owner of an organization-owned managed profile can + * set on the parent profile instance to apply them globally. */ private static final Set<String> PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS = Sets.newArraySet( UserManager.DISALLOW_CONFIG_DATE_TIME, - UserManager.DISALLOW_CAMERA + UserManager.DISALLOW_CAMERA, + UserManager.DISALLOW_ADD_USER, + UserManager.DISALLOW_BLUETOOTH, + UserManager.DISALLOW_BLUETOOTH_SHARING, + UserManager.DISALLOW_CONFIG_BLUETOOTH, + UserManager.DISALLOW_CONFIG_CELL_BROADCASTS, + UserManager.DISALLOW_CONFIG_LOCATION, + UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, + UserManager.DISALLOW_CONFIG_PRIVATE_DNS, + UserManager.DISALLOW_CONFIG_TETHERING, + UserManager.DISALLOW_CONFIG_WIFI, + UserManager.DISALLOW_CONTENT_CAPTURE, + UserManager.DISALLOW_CONTENT_SUGGESTIONS, + UserManager.DISALLOW_DATA_ROAMING, + UserManager.DISALLOW_DEBUGGING_FEATURES, + UserManager.DISALLOW_SAFE_BOOT, + UserManager.DISALLOW_SHARE_LOCATION, + UserManager.DISALLOW_SMS, + UserManager.DISALLOW_USB_FILE_TRANSFER ); /** diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java index 9f592b85a5e6..7d0072abe1c9 100644 --- a/services/core/java/com/android/server/rollback/Rollback.java +++ b/services/core/java/com/android/server/rollback/Rollback.java @@ -172,6 +172,13 @@ class Rollback { @Nullable public final String mInstallerPackageName; /** + * This array holds all of the rollback tokens associated with package sessions included in + * this rollback. + */ + @GuardedBy("mLock") + private final IntArray mTokens = new IntArray(); + + /** * Constructs a new, empty Rollback instance. * * @param rollbackId the id of the rollback. @@ -766,6 +773,26 @@ class Rollback { } } + /** + * Adds a rollback token to be associated with this rollback. This may be used to + * identify which rollback should be removed in case {@link PackageManager} sends an + * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} intent. + */ + void addToken(int token) { + synchronized (mLock) { + mTokens.add(token); + } + } + + /** + * Returns true if this rollback is associated with the provided {@code token}. + */ + boolean hasToken(int token) { + synchronized (mLock) { + return mTokens.indexOf(token) != -1; + } + } + static String rollbackStateToString(@RollbackState int state) { switch (state) { case Rollback.ROLLBACK_STATE_ENABLING: return "enabling"; diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index 8f8a5c4b14e9..eefcde6bd355 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -235,12 +235,17 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { Slog.v(TAG, "broadcast=ACTION_CANCEL_ENABLE_ROLLBACK token=" + token); } synchronized (mLock) { - for (NewRollback rollback : mNewRollbacks) { - if (rollback.hasToken(token)) { - rollback.setCancelled(); - return; + NewRollback found = null; + for (NewRollback newRollback : mNewRollbacks) { + if (newRollback.rollback.hasToken(token)) { + found = newRollback; + break; } } + if (found != null) { + mNewRollbacks.remove(found); + found.rollback.delete(mAppDataRollbackHelper); + } } } } @@ -433,10 +438,14 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { rollback.delete(mAppDataRollbackHelper); } } - for (NewRollback newRollback : mNewRollbacks) { + Iterator<NewRollback> iter2 = mNewRollbacks.iterator(); + while (iter2.hasNext()) { + NewRollback newRollback = iter2.next(); if (newRollback.rollback.includesPackage(packageName)) { - newRollback.setCancelled(); + iter2.remove(); + newRollback.rollback.delete(mAppDataRollbackHelper); } + } } } @@ -798,7 +807,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { mNewRollbacks.add(newRollback); } } - newRollback.addToken(token); + newRollback.rollback.addToken(token); return enableRollbackForPackageSession(newRollback.rollback, packageSession); } @@ -1220,12 +1229,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { Slog.v(TAG, "completeEnableRollback id=" + rollback.info.getRollbackId()); } - if (newRollback.isCancelled()) { - Slog.e(TAG, "Rollback has been cancelled by PackageManager"); - rollback.delete(mAppDataRollbackHelper); - return null; - } - // We are checking if number of packages (excluding apk-in-apex) we enabled for rollback is // equal to the number of sessions we are installing, to ensure we didn't skip enabling // of any sessions. If we successfully enable an apex, then we can assume we enabled @@ -1335,22 +1338,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { public final Rollback rollback; /** - * This array holds all of the rollback tokens associated with package sessions included in - * this rollback. - */ - @GuardedBy("mNewRollbackLock") - private final IntArray mTokens = new IntArray(); - - /** * Session ids for all packages in the install. For multi-package sessions, this is the list * of child session ids. For normal sessions, this list is a single element with the normal * session id. */ private final int[] mPackageSessionIds; - @GuardedBy("mNewRollbackLock") - private boolean mIsCancelled = false; - /** * The number of sessions in the install which are notified with success by * {@link PackageInstaller.SessionCallback#onFinished(int, boolean)}. @@ -1367,52 +1360,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } /** - * Adds a rollback token to be associated with this NewRollback. This may be used to - * identify which rollback should be cancelled in case {@link PackageManager} sends an - * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} intent. - */ - void addToken(int token) { - synchronized (mNewRollbackLock) { - mTokens.add(token); - } - } - - /** - * Returns true if this NewRollback is associated with the provided {@code token}. - */ - boolean hasToken(int token) { - synchronized (mNewRollbackLock) { - return mTokens.indexOf(token) != -1; - } - } - - /** - * Returns true if this NewRollback has been cancelled. - * - * <p>Rollback could be invalidated and cancelled if RollbackManager receives - * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} from {@link PackageManager}. - * - * <p>The main underlying assumption here is that if enabling the rollback times out, then - * {@link PackageManager} will NOT send - * {@link PackageInstaller.SessionCallback#onFinished(int, boolean)} before it broadcasts - * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK}. - */ - boolean isCancelled() { - synchronized (mNewRollbackLock) { - return mIsCancelled; - } - } - - /** - * Sets this NewRollback to be marked as cancelled. - */ - void setCancelled() { - synchronized (mNewRollbackLock) { - mIsCancelled = true; - } - } - - /** * Returns true if this NewRollback contains the provided {@code packageSessionId}. */ boolean containsSessionId(int packageSessionId) { diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java index a0ef8cfec80f..b9ef7b39c451 100644 --- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -280,7 +280,6 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED, WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, ""); - mContext.getSystemService(PowerManager.class).reboot("Rollback staged install"); } else if (sessionInfo.isStagedSessionFailed() && markStagedSessionHandled(rollbackId)) { logEvent(moduleMetadataPackage, @@ -291,6 +290,11 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } } } + + // Wait for all pending staged sessions to get handled before rebooting. + if (isPendingStagedSessionsEmpty()) { + mContext.getSystemService(PowerManager.class).reboot("Rollback staged install"); + } } /** @@ -303,6 +307,16 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } } + /** + * Returns {@code true} if all pending staged rollback sessions were marked as handled, + * {@code false} if there is any left. + */ + private boolean isPendingStagedSessionsEmpty() { + synchronized (mPendingStagedRollbackIds) { + return mPendingStagedRollbackIds.isEmpty(); + } + } + private void saveLastStagedRollbackId(int stagedRollbackId) { try { FileOutputStream fos = new FileOutputStream(mLastStagedRollbackIdFile); @@ -414,6 +428,9 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve reasonToLog, failedPackageToLog); } } else { + if (rollback.isStaged()) { + markStagedSessionHandled(rollback.getRollbackId()); + } logEvent(logPackage, StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, reasonToLog, failedPackageToLog); @@ -431,6 +448,16 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); List<RollbackInfo> rollbacks = rollbackManager.getAvailableRollbacks(); + // Add all rollback ids to mPendingStagedRollbackIds, so that we do not reboot before all + // pending staged rollbacks are handled. + synchronized (mPendingStagedRollbackIds) { + for (RollbackInfo rollback : rollbacks) { + if (rollback.isStaged()) { + mPendingStagedRollbackIds.add(rollback.getRollbackId()); + } + } + } + for (RollbackInfo rollback : rollbacks) { VersionedPackage sample = rollback.getPackages().get(0).getVersionRolledBackFrom(); rollbackPackage(rollback, sample, PackageWatchdog.FAILURE_REASON_NATIVE_CRASH); diff --git a/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java b/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java new file mode 100644 index 000000000000..b19e2ed1a91b --- /dev/null +++ b/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java @@ -0,0 +1,31 @@ +/* + * 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.soundtrigger_middleware; + +import android.hardware.soundtrigger.V2_0.ISoundTriggerHw; + +/** + * A factory for creating instances of {@link ISoundTriggerHw}. + * + * @hide + */ +public interface HalFactory { + /** + * @return An instance of {@link ISoundTriggerHw}. + */ + ISoundTriggerHw create(); +} diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java index 9d51b65ea152..9f4b09a62aff 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java @@ -78,18 +78,17 @@ public class SoundTriggerMiddlewareImpl implements ISoundTriggerMiddlewareServic } /** - * Most generic constructor - gets an array of HAL driver instances. + * Constructor - gets an array of HAL driver factories. */ - public SoundTriggerMiddlewareImpl(@NonNull ISoundTriggerHw[] halServices, + public SoundTriggerMiddlewareImpl(@NonNull HalFactory[] halFactories, @NonNull AudioSessionProvider audioSessionProvider) { - List<SoundTriggerModule> modules = new ArrayList<>(halServices.length); + List<SoundTriggerModule> modules = new ArrayList<>(halFactories.length); - for (int i = 0; i < halServices.length; ++i) { - ISoundTriggerHw service = halServices[i]; + for (int i = 0; i < halFactories.length; ++i) { try { - modules.add(new SoundTriggerModule(service, audioSessionProvider)); + modules.add(new SoundTriggerModule(halFactories[i], audioSessionProvider)); } catch (Exception e) { - Log.e(TAG, "Failed to a SoundTriggerModule instance", e); + Log.e(TAG, "Failed to add a SoundTriggerModule instance", e); } } @@ -97,11 +96,11 @@ public class SoundTriggerMiddlewareImpl implements ISoundTriggerMiddlewareServic } /** - * Convenience constructor - gets a single HAL driver instance. + * Convenience constructor - gets a single HAL factory. */ - public SoundTriggerMiddlewareImpl(@NonNull ISoundTriggerHw halService, + public SoundTriggerMiddlewareImpl(@NonNull HalFactory factory, @NonNull AudioSessionProvider audioSessionProvider) { - this(new ISoundTriggerHw[]{halService}, audioSessionProvider); + this(new HalFactory[]{factory}, audioSessionProvider); } @Override diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java index 5a06a2c4b388..12f9fd936fba 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java @@ -113,9 +113,9 @@ import java.util.Set; public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareService.Stub { static private final String TAG = "SoundTriggerMiddlewareService"; - final ISoundTriggerMiddlewareService mDelegate; - final Context mContext; - Set<Integer> mModuleHandles; + private final ISoundTriggerMiddlewareService mDelegate; + private final Context mContext; + private Set<Integer> mModuleHandles; /** * Constructor for internal use only. Could be exposed for testing purposes in the future. @@ -280,7 +280,7 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic } /** Activity state. */ - public Activity activityState = Activity.LOADED; + Activity activityState = Activity.LOADED; /** * A map of known parameter support. A missing key means we don't know yet whether the @@ -294,7 +294,7 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic * * @param modelParam The parameter key. */ - public void checkSupported(int modelParam) { + void checkSupported(int modelParam) { if (!parameterSupport.containsKey(modelParam)) { throw new IllegalStateException("Parameter has not been checked for support."); } @@ -311,7 +311,7 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic * @param modelParam The parameter key. * @param value The value. */ - public void checkSupported(int modelParam, int value) { + void checkSupported(int modelParam, int value) { if (!parameterSupport.containsKey(modelParam)) { throw new IllegalStateException("Parameter has not been checked for support."); } @@ -329,7 +329,7 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic * @param modelParam The parameter key. * @param range The parameter value range, or null if not supported. */ - public void updateParameterSupport(int modelParam, @Nullable ModelParameterRange range) { + void updateParameterSupport(int modelParam, @Nullable ModelParameterRange range) { parameterSupport.put(modelParam, range); } } @@ -338,27 +338,26 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic * Entry-point to this module: exposes the module as a {@link SystemService}. */ public static final class Lifecycle extends SystemService { - private SoundTriggerMiddlewareService mService; - public Lifecycle(Context context) { super(context); } @Override public void onStart() { - ISoundTriggerHw[] services; - try { - services = new ISoundTriggerHw[]{ISoundTriggerHw.getService(true)}; - Log.d(TAG, "Connected to default ISoundTriggerHw"); - } catch (Exception e) { - Log.e(TAG, "Failed to connect to default ISoundTriggerHw", e); - services = new ISoundTriggerHw[0]; - } + HalFactory[] factories = new HalFactory[]{() -> { + try { + Log.d(TAG, "Connecting to default ISoundTriggerHw"); + return ISoundTriggerHw.getService(true); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + }}; - mService = new SoundTriggerMiddlewareService( - new SoundTriggerMiddlewareImpl(services, new AudioSessionProviderImpl()), - getContext()); - publishBinderService(Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE, mService); + publishBinderService(Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE, + new SoundTriggerMiddlewareService( + new SoundTriggerMiddlewareImpl(factories, + new AudioSessionProviderImpl()), + getContext())); } } @@ -370,7 +369,7 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic DeathRecipient { private final ISoundTriggerCallback mCallback; private ISoundTriggerModule mDelegate; - private Map<Integer, ModelState> mLoadedModels = new HashMap<>(); + private @NonNull Map<Integer, ModelState> mLoadedModels = new HashMap<>(); ModuleService(@NonNull ISoundTriggerCallback callback) { mCallback = callback; @@ -680,7 +679,7 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic } catch (RemoteException e) { // Dead client will be handled by binderDied() - no need to handle here. // In any case, client callbacks are considered best effort. - Log.e(TAG, "Client callback execption.", e); + Log.e(TAG, "Client callback exception.", e); } } } @@ -696,20 +695,33 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic } catch (RemoteException e) { // Dead client will be handled by binderDied() - no need to handle here. // In any case, client callbacks are considered best effort. - Log.e(TAG, "Client callback execption.", e); + Log.e(TAG, "Client callback exception.", e); } } } @Override - public void onRecognitionAvailabilityChange(boolean available) throws RemoteException { + public void onRecognitionAvailabilityChange(boolean available) { synchronized (this) { try { mCallback.onRecognitionAvailabilityChange(available); } catch (RemoteException e) { // Dead client will be handled by binderDied() - no need to handle here. // In any case, client callbacks are considered best effort. - Log.e(TAG, "Client callback execption.", e); + Log.e(TAG, "Client callback exception.", e); + } + } + } + + @Override + public void onModuleDied() { + synchronized (this) { + try { + mCallback.onModuleDied(); + } catch (RemoteException e) { + // Dead client will be handled by binderDied() - no need to handle here. + // In any case, client callbacks are considered best effort. + Log.e(TAG, "Client callback exception.", e); } } } diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java index f024edecab3b..adf16fafc000 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java @@ -30,7 +30,9 @@ import android.media.soundtrigger_middleware.SoundModelType; import android.media.soundtrigger_middleware.SoundTriggerModuleProperties; import android.media.soundtrigger_middleware.Status; import android.os.IBinder; +import android.os.IHwBinder; import android.os.RemoteException; +import android.os.ServiceSpecificException; import android.util.Log; import java.util.HashMap; @@ -57,6 +59,7 @@ import java.util.Set; * gracefully handle driver malfunction and such behavior will result in undefined behavior. If this * service is to used with an untrusted driver, the driver must be wrapped with validation / error * recovery code. + * <li>Recovery from driver death is supported.</li> * <li>RemoteExceptions thrown by the driver are treated as RuntimeExceptions - they are not * considered recoverable faults and should not occur in a properly functioning system. * <li>There is no binder instance associated with this implementation. Do not call asBinder(). @@ -79,27 +82,29 @@ import java.util.Set; * * @hide */ -class SoundTriggerModule { +class SoundTriggerModule implements IHwBinder.DeathRecipient { static private final String TAG = "SoundTriggerModule"; - @NonNull private final ISoundTriggerHw2 mHalService; + @NonNull private HalFactory mHalFactory; + @NonNull private ISoundTriggerHw2 mHalService; @NonNull private final SoundTriggerMiddlewareImpl.AudioSessionProvider mAudioSessionProvider; private final Set<Session> mActiveSessions = new HashSet<>(); private int mNumLoadedModels = 0; - private SoundTriggerModuleProperties mProperties = null; + private SoundTriggerModuleProperties mProperties; private boolean mRecognitionAvailable; /** * Ctor. * - * @param halService The underlying HAL driver. + * @param halFactory A factory for the underlying HAL driver. */ - SoundTriggerModule(@NonNull android.hardware.soundtrigger.V2_0.ISoundTriggerHw halService, + SoundTriggerModule(@NonNull HalFactory halFactory, @NonNull SoundTriggerMiddlewareImpl.AudioSessionProvider audioSessionProvider) { - assert halService != null; - mHalService = new SoundTriggerHw2Compat(halService); + assert halFactory != null; + mHalFactory = halFactory; mAudioSessionProvider = audioSessionProvider; - mProperties = ConversionUtil.hidl2aidlProperties(mHalService.getProperties()); + attachToHal(); + mProperties = ConversionUtil.hidl2aidlProperties(mHalService.getProperties()); // We conservatively assume that external capture is active until explicitly told otherwise. mRecognitionAvailable = mProperties.concurrentCapture; } @@ -117,7 +122,7 @@ class SoundTriggerModule { * @return The interface through which this module can be controlled. */ synchronized @NonNull - Session attach(@NonNull ISoundTriggerCallback callback) { + ISoundTriggerModule attach(@NonNull ISoundTriggerCallback callback) { Log.d(TAG, "attach()"); Session session = new Session(callback); mActiveSessions.add(session); @@ -145,6 +150,7 @@ class SoundTriggerModule { */ synchronized void setExternalCaptureState(boolean active) { Log.d(TAG, String.format("setExternalCaptureState(active=%b)", active)); + if (mProperties.concurrentCapture) { // If we support concurrent capture, we don't care about any of this. return; @@ -162,6 +168,23 @@ class SoundTriggerModule { } } + @Override + public synchronized void serviceDied(long cookie) { + Log.w(TAG, String.format("Underlying HAL driver died.")); + for (Session session : mActiveSessions) { + session.moduleDied(); + } + attachToHal(); + } + + /** + * Attached to the HAL service via factory. + */ + private void attachToHal() { + mHalService = new SoundTriggerHw2Compat(mHalFactory.create()); + mHalService.linkToDeath(this, 0); + } + /** * Remove session from the list of active sessions. * @@ -204,7 +227,11 @@ class SoundTriggerModule { public void detach() { Log.d(TAG, "detach()"); synchronized (SoundTriggerModule.this) { + if (mCallback == null) { + return; + } removeSession(this); + mCallback = null; } } @@ -212,6 +239,7 @@ class SoundTriggerModule { public int loadModel(@NonNull SoundModel model) { Log.d(TAG, String.format("loadModel(model=%s)", model)); synchronized (SoundTriggerModule.this) { + checkValid(); if (mNumLoadedModels == mProperties.maxSoundModels) { throw new RecoverableException(Status.RESOURCE_CONTENTION, "Maximum number of models loaded."); @@ -227,6 +255,7 @@ class SoundTriggerModule { public int loadPhraseModel(@NonNull PhraseSoundModel model) { Log.d(TAG, String.format("loadPhraseModel(model=%s)", model)); synchronized (SoundTriggerModule.this) { + checkValid(); if (mNumLoadedModels == mProperties.maxSoundModels) { throw new RecoverableException(Status.RESOURCE_CONTENTION, "Maximum number of models loaded."); @@ -243,6 +272,7 @@ class SoundTriggerModule { public void unloadModel(int modelHandle) { Log.d(TAG, String.format("unloadModel(handle=%d)", modelHandle)); synchronized (SoundTriggerModule.this) { + checkValid(); mLoadedModels.get(modelHandle).unload(); --mNumLoadedModels; } @@ -253,6 +283,7 @@ class SoundTriggerModule { Log.d(TAG, String.format("startRecognition(handle=%d, config=%s)", modelHandle, config)); synchronized (SoundTriggerModule.this) { + checkValid(); mLoadedModels.get(modelHandle).startRecognition(config); } } @@ -269,26 +300,28 @@ class SoundTriggerModule { public void forceRecognitionEvent(int modelHandle) { Log.d(TAG, String.format("forceRecognitionEvent(handle=%d)", modelHandle)); synchronized (SoundTriggerModule.this) { + checkValid(); mLoadedModels.get(modelHandle).forceRecognitionEvent(); } } @Override - public void setModelParameter(int modelHandle, int modelParam, int value) - throws RemoteException { + public void setModelParameter(int modelHandle, int modelParam, int value) { Log.d(TAG, String.format("setModelParameter(handle=%d, param=%d, value=%d)", modelHandle, modelParam, value)); synchronized (SoundTriggerModule.this) { + checkValid(); mLoadedModels.get(modelHandle).setParameter(modelParam, value); } } @Override - public int getModelParameter(int modelHandle, int modelParam) throws RemoteException { + public int getModelParameter(int modelHandle, int modelParam) { Log.d(TAG, String.format("getModelParameter(handle=%d, param=%d)", modelHandle, modelParam)); synchronized (SoundTriggerModule.this) { + checkValid(); return mLoadedModels.get(modelHandle).getParameter(modelParam); } } @@ -299,6 +332,7 @@ class SoundTriggerModule { Log.d(TAG, String.format("queryModelParameterSupport(handle=%d, param=%d)", modelHandle, modelParam)); synchronized (SoundTriggerModule.this) { + checkValid(); return mLoadedModels.get(modelHandle).queryModelParameterSupport(modelParam); } } @@ -322,6 +356,25 @@ class SoundTriggerModule { } } + /** + * The underlying module HAL is dead. + */ + private void moduleDied() { + try { + mCallback.onModuleDied(); + removeSession(this); + mCallback = null; + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + } + + private void checkValid() { + if (mCallback == null) { + throw new ServiceSpecificException(Status.DEAD_OBJECT); + } + } + @Override public @NonNull IBinder asBinder() { @@ -350,10 +403,6 @@ class SoundTriggerModule { SoundTriggerModule.this.notifyAll(); } - private void waitStateChange() throws InterruptedException { - SoundTriggerModule.this.wait(); - } - private int load(@NonNull SoundModel model) { mModelType = model.type; ISoundTriggerHw.SoundModel hidlModel = ConversionUtil.aidl2hidlSoundModel(model); diff --git a/services/core/java/com/android/server/stats/StatsPullAtomService.java b/services/core/java/com/android/server/stats/StatsPullAtomService.java index cf887b30d245..91e39dad979a 100644 --- a/services/core/java/com/android/server/stats/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/StatsPullAtomService.java @@ -864,11 +864,22 @@ public class StatsPullAtomService extends SystemService { } private void registerSystemUptime() { - // No op. + int tagId = StatsLog.SYSTEM_UPTIME; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullSystemUptime(atomTag, data), + Executors.newSingleThreadExecutor() + ); } - private void pullSystemUptime() { - // No op. + private int pullSystemUptime(int atomTag, List<StatsEvent> pulledData) { + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeLong(SystemClock.elapsedRealtime()) + .build(); + pulledData.add(e); + return StatsManager.PULL_SUCCESS; } private void registerRemainingBatteryCapacity() { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 320be2d7cdbd..c9a702f002de 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1633,12 +1633,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A requestedVrComponent = (aInfo.requestedVrComponent == null) ? null : ComponentName.unflattenFromString(aInfo.requestedVrComponent); - lockTaskLaunchMode = aInfo.lockTaskLaunchMode; - if (info.applicationInfo.isPrivilegedApp() - && (lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_ALWAYS - || lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_NEVER)) { - lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT; - } + lockTaskLaunchMode = getLockTaskLaunchMode(aInfo, options); if (options != null) { pendingOptions = options; @@ -1646,13 +1641,25 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (usageReport != null) { appTimeTracker = new AppTimeTracker(usageReport); } - final boolean useLockTask = pendingOptions.getLockTaskMode(); + // Gets launch display id from options. It returns INVALID_DISPLAY if not set. + mHandoverLaunchDisplayId = options.getLaunchDisplayId(); + } + } + + static int getLockTaskLaunchMode(ActivityInfo aInfo, @Nullable ActivityOptions options) { + int lockTaskLaunchMode = aInfo.lockTaskLaunchMode; + if (aInfo.applicationInfo.isPrivilegedApp() + && (lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_ALWAYS + || lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_NEVER)) { + lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT; + } + if (options != null) { + final boolean useLockTask = options.getLockTaskMode(); if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) { lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED; } - // Gets launch display id from options. It returns INVALID_DISPLAY if not set. - mHandoverLaunchDisplayId = options.getLaunchDisplayId(); } + return lockTaskLaunchMode; } @Override diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java index df97caa76d2d..d61d29d1084e 100644 --- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java +++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java @@ -52,6 +52,7 @@ import android.os.UserHandle; import android.os.UserManager; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.BlockedAppActivity; import com.android.internal.app.HarmfulAppWarningActivity; import com.android.internal.app.SuspendedAppActivity; import com.android.internal.app.UnlaunchableAppActivity; @@ -166,6 +167,9 @@ class ActivityStartInterceptor { // no user action can undo this. return true; } + if (interceptLockTaskModeViolationPackageIfNeeded()) { + return true; + } if (interceptHarmfulAppIfNeeded()) { // If the app has a "harmful app" warning associated with it, we should ask to uninstall // before issuing the work challenge. @@ -262,6 +266,25 @@ class ActivityStartInterceptor { return true; } + private boolean interceptLockTaskModeViolationPackageIfNeeded() { + if (mAInfo == null || mAInfo.applicationInfo == null) { + return false; + } + LockTaskController controller = mService.getLockTaskController(); + String packageName = mAInfo.applicationInfo.packageName; + int lockTaskLaunchMode = ActivityRecord.getLockTaskLaunchMode(mAInfo, mActivityOptions); + if (controller.isActivityAllowed(mUserId, packageName, lockTaskLaunchMode)) { + return false; + } + mIntent = BlockedAppActivity.createIntent(mUserId, mAInfo.applicationInfo.packageName); + mCallingPid = mRealCallingPid; + mCallingUid = mRealCallingUid; + mResolvedType = null; + mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid); + mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); + return true; + } + private boolean interceptWorkProfileChallengeIfNeeded() { final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(mAInfo, mUserId); if (interceptingIntent == null) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index ded603c9fd77..3092a08099e1 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -28,7 +28,7 @@ import static android.Manifest.permission.REMOVE_TASKS; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.Manifest.permission.STOP_APP_SWITCHES; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; -import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; +import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW; import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; @@ -1436,7 +1436,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { int handleIncomingUser(int callingPid, int callingUid, int userId, String name) { return mAmInternal.handleIncomingUser(callingPid, callingUid, userId, false /* allowAll */, - ALLOW_FULL_ONLY, name, null /* callerPackage */); + ALLOW_NON_FULL, name, null /* callerPackage */); } @Override diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java index 02413bb48518..33b0453a25ee 100644 --- a/services/core/java/com/android/server/wm/LockTaskController.java +++ b/services/core/java/com/android/server/wm/LockTaskController.java @@ -23,6 +23,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Context.DEVICE_POLICY_SERVICE; import static android.content.Context.STATUS_BAR_SERVICE; import static android.content.Intent.ACTION_CALL_EMERGENCY; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER; import static android.os.UserHandle.USER_ALL; import static android.os.UserHandle.USER_CURRENT; import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT; @@ -339,6 +341,20 @@ public class LockTaskController { & DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD) != 0; } + boolean isActivityAllowed(int userId, String packageName, int lockTaskLaunchMode) { + if (mLockTaskModeState != LOCK_TASK_MODE_LOCKED) { + return true; + } + switch (lockTaskLaunchMode) { + case LOCK_TASK_LAUNCH_MODE_ALWAYS: + return true; + case LOCK_TASK_LAUNCH_MODE_NEVER: + return false; + default: + } + return isPackageWhitelisted(userId, packageName); + } + private boolean isEmergencyCallTask(Task task) { final Intent intent = task.intent; if (intent == null) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 74fdba1cd13e..e3b593e90fa5 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -4602,13 +4602,13 @@ public class WindowManagerService extends IWindowManager.Stub if (newFocus != null) { ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Gaining focus: %s", newFocus); - newFocus.reportFocusChangedSerialized(true, mInTouchMode); + newFocus.reportFocusChangedSerialized(true); notifyFocusChanged(); } if (lastFocus != null) { ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Losing focus: %s", lastFocus); - lastFocus.reportFocusChangedSerialized(false, mInTouchMode); + lastFocus.reportFocusChangedSerialized(false); } break; } @@ -4626,7 +4626,7 @@ public class WindowManagerService extends IWindowManager.Stub for (int i = 0; i < N; i++) { ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Losing delayed focus: %s", losers.get(i)); - losers.get(i).reportFocusChangedSerialized(false, mInTouchMode); + losers.get(i).reportFocusChangedSerialized(false); } break; } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index ba40f623ea66..c2eb0e4492d5 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -3339,11 +3339,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP * Report a focus change. Must be called with no locks held, and consistently * from the same serialized thread (such as dispatched from a handler). */ - void reportFocusChangedSerialized(boolean focused, boolean inTouchMode) { - try { - mClient.windowFocusChanged(focused, inTouchMode); - } catch (RemoteException e) { - } + void reportFocusChangedSerialized(boolean focused) { if (mFocusCallbacks != null) { final int N = mFocusCallbacks.beginBroadcast(); for (int i=0; i<N; i++) { diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 03969b01f3f9..77d814e3076b 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -51,7 +51,7 @@ cc_library_static { "com_android_server_VibratorService.cpp", "com_android_server_PersistentDataBlockService.cpp", "com_android_server_GraphicsStatsService.cpp", - "com_android_server_am_AppCompactor.cpp", + "com_android_server_am_CachedAppOptimizer.cpp", "com_android_server_am_LowMemDetector.cpp", "com_android_server_incremental_IncrementalManagerService.cpp", "onload.cpp", diff --git a/services/core/jni/com_android_server_am_AppCompactor.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp index de6aa8b3266b..6a6da0e2b395 100644 --- a/services/core/jni/com_android_server_am_AppCompactor.cpp +++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define LOG_TAG "AppCompactor" +#define LOG_TAG "CachedAppOptimizer" //#define LOG_NDEBUG 0 #include <dirent.h> @@ -42,7 +42,7 @@ namespace android { // or potentially some mainline modules. The only process that should definitely // not be compacted is system_server, since compacting system_server around the // time of BOOT_COMPLETE could result in perceptible issues. -static void com_android_server_am_AppCompactor_compactSystem(JNIEnv *, jobject) { +static void com_android_server_am_CachedAppOptimizer_compactSystem(JNIEnv *, jobject) { std::unique_ptr<DIR, decltype(&closedir)> proc(opendir("/proc"), closedir); struct dirent* current; while ((current = readdir(proc.get()))) { @@ -76,12 +76,12 @@ static void com_android_server_am_AppCompactor_compactSystem(JNIEnv *, jobject) static const JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ - {"compactSystem", "()V", (void*)com_android_server_am_AppCompactor_compactSystem}, + {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem}, }; -int register_android_server_am_AppCompactor(JNIEnv* env) +int register_android_server_am_CachedAppOptimizer(JNIEnv* env) { - return jniRegisterNativeMethods(env, "com/android/server/am/AppCompactor", + return jniRegisterNativeMethods(env, "com/android/server/am/CachedAppOptimizer", sMethods, NELEM(sMethods)); } diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index c0a6e4e30f3a..19fa062bd9f9 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -54,7 +54,7 @@ int register_android_hardware_display_DisplayViewport(JNIEnv* env); int register_android_server_net_NetworkStatsFactory(JNIEnv* env); int register_android_server_net_NetworkStatsService(JNIEnv* env); int register_android_server_security_VerityUtils(JNIEnv* env); -int register_android_server_am_AppCompactor(JNIEnv* env); +int register_android_server_am_CachedAppOptimizer(JNIEnv* env); int register_android_server_am_LowMemDetector(JNIEnv* env); int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl( JNIEnv* env); @@ -106,7 +106,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_net_NetworkStatsFactory(env); register_android_server_net_NetworkStatsService(env); register_android_server_security_VerityUtils(env); - register_android_server_am_AppCompactor(env); + register_android_server_am_CachedAppOptimizer(env); register_android_server_am_LowMemDetector(env); register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl( env); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index b6a8ca447213..ea2385fd61c5 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -213,6 +213,8 @@ public final class SystemServer { "com.android.server.print.PrintManagerService"; private static final String COMPANION_DEVICE_MANAGER_SERVICE_CLASS = "com.android.server.companion.CompanionDeviceManagerService"; + private static final String STATS_COMPANION_APEX_PATH = + "/apex/com.android.os.statsd/javalib/service-statsd.jar"; private static final String STATS_COMPANION_LIFECYCLE_CLASS = "com.android.server.stats.StatsCompanion$Lifecycle"; private static final String STATS_PULL_ATOM_SERVICE_CLASS = @@ -1980,7 +1982,8 @@ public final class SystemServer { // Statsd helper t.traceBegin("StartStatsCompanion"); - mSystemServiceManager.startService(STATS_COMPANION_LIFECYCLE_CLASS); + mSystemServiceManager.startServiceFromJar( + STATS_COMPANION_LIFECYCLE_CLASS, STATS_COMPANION_APEX_PATH); t.traceEnd(); // Statsd pulled atoms diff --git a/services/net/Android.bp b/services/net/Android.bp index 9c7cfc197bba..cf84bdfb5b6f 100644 --- a/services/net/Android.bp +++ b/services/net/Android.bp @@ -23,7 +23,6 @@ filegroup { name: "services-tethering-shared-srcs", srcs: [ ":framework-annotations", - "java/android/net/util/NetdService.java", "java/android/net/util/NetworkConstants.java", ], visibility: ["//frameworks/base/packages/Tethering"], diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AppCompactorTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AppCompactorTest.java deleted file mode 100644 index 48e459ff2d9d..000000000000 --- a/services/tests/mockingservicestests/src/com/android/server/am/AppCompactorTest.java +++ /dev/null @@ -1,680 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.am; - -import static com.android.server.am.ActivityManagerService.Injector; -import static com.android.server.am.AppCompactor.compactActionIntToString; - -import static com.google.common.truth.Truth.assertThat; - -import android.content.Context; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Process; -import android.platform.test.annotations.Presubmit; -import android.provider.DeviceConfig; -import android.text.TextUtils; - -import androidx.test.platform.app.InstrumentationRegistry; - -import com.android.server.ServiceThread; -import com.android.server.appop.AppOpsService; -import com.android.server.testables.TestableDeviceConfig; - -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import java.io.File; -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** - * Tests for {@link AppCompactor}. - * - * Build/Install/Run: - * atest FrameworksMockingServicesTests:AppCompactorTest - */ -@Presubmit -@RunWith(MockitoJUnitRunner.class) -public final class AppCompactorTest { - - private ServiceThread mThread; - - @Mock - private AppOpsService mAppOpsService; - private AppCompactor mCompactorUnderTest; - private HandlerThread mHandlerThread; - private Handler mHandler; - private CountDownLatch mCountDown; - - @Rule - public TestableDeviceConfig.TestableDeviceConfigRule - mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule(); - - @Before - public void setUp() { - mHandlerThread = new HandlerThread(""); - mHandlerThread.start(); - mHandler = new Handler(mHandlerThread.getLooper()); - - mThread = new ServiceThread("TestServiceThread", Process.THREAD_PRIORITY_DEFAULT, - true /* allowIo */); - mThread.start(); - - ActivityManagerService ams = new ActivityManagerService( - new TestInjector(InstrumentationRegistry.getInstrumentation().getContext()), - mThread); - mCompactorUnderTest = new AppCompactor(ams, - new AppCompactor.PropertyChangedCallbackForTest() { - @Override - public void onPropertyChanged() { - if (mCountDown != null) { - mCountDown.countDown(); - } - } - }); - } - - @After - public void tearDown() { - mHandlerThread.quit(); - mThread.quit(); - mCountDown = null; - } - - @Test - public void init_setsDefaults() { - mCompactorUnderTest.init(); - assertThat(mCompactorUnderTest.useCompaction()).isEqualTo( - AppCompactor.DEFAULT_USE_COMPACTION); - assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo( - compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1)); - assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo( - compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_1); - assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_2); - assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_3); - assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_4); - assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo( - AppCompactor.DEFAULT_STATSD_SAMPLE_RATE); - assertThat(mCompactorUnderTest.mFullAnonRssThrottleKb).isEqualTo( - AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB); - assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_5); - assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_6); - assertThat(mCompactorUnderTest.mFullAnonRssThrottleKb).isEqualTo( - AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB); - assertThat(mCompactorUnderTest.mFullDeltaRssThrottleKb).isEqualTo( - AppCompactor.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB); - - Set<Integer> expected = new HashSet<>(); - for (String s : TextUtils.split(AppCompactor.DEFAULT_COMPACT_PROC_STATE_THROTTLE, ",")) { - expected.add(Integer.parseInt(s)); - } - assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactlyElementsIn(expected); - } - - @Test - public void init_withDeviceConfigSetsParameters() { - // When the DeviceConfig already has a flag value stored (note this test will need to - // change if the default value changes from false). - assertThat(AppCompactor.DEFAULT_USE_COMPACTION).isFalse(); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_USE_COMPACTION, "true", false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_ACTION_1, - Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1), false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_ACTION_2, - Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1), false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_THROTTLE_1, - Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1), false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_THROTTLE_2, - Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1), false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_THROTTLE_3, - Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1), false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_THROTTLE_4, - Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1), false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_THROTTLE_5, - Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_5 + 1), false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_THROTTLE_6, - Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_6 + 1), false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_STATSD_SAMPLE_RATE, - Float.toString(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_FULL_RSS_THROTTLE_KB, - Long.toString(AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1), false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, - Long.toString(AppCompactor.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1), false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, "1,2,3", false); - - // Then calling init will read and set that flag. - mCompactorUnderTest.init(); - assertThat(mCompactorUnderTest.useCompaction()).isTrue(); - assertThat(mCompactorUnderTest.mCompactionThread.isAlive()).isTrue(); - - assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo( - compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1)); - assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo( - compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1); - assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1); - assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1); - assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1); - assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_5 + 1); - assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_6 + 1); - assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo( - AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f); - assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_5 + 1); - assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_6 + 1); - assertThat(mCompactorUnderTest.mFullAnonRssThrottleKb).isEqualTo( - AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1); - assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactly(1, 2, 3); - } - - @Test - public void useCompaction_listensToDeviceConfigChanges() throws InterruptedException { - assertThat(mCompactorUnderTest.useCompaction()).isEqualTo( - AppCompactor.DEFAULT_USE_COMPACTION); - // When we call init and change some the flag value... - mCompactorUnderTest.init(); - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_USE_COMPACTION, "true", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - - // Then that new flag value is updated in the implementation. - assertThat(mCompactorUnderTest.useCompaction()).isTrue(); - assertThat(mCompactorUnderTest.mCompactionThread.isAlive()).isTrue(); - - // And again, setting the flag the other way. - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_USE_COMPACTION, "false", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - assertThat(mCompactorUnderTest.useCompaction()).isFalse(); - } - - @Test - public void useCompaction_listensToDeviceConfigChangesBadValues() throws InterruptedException { - assertThat(mCompactorUnderTest.useCompaction()).isEqualTo( - AppCompactor.DEFAULT_USE_COMPACTION); - mCompactorUnderTest.init(); - - // When we push an invalid flag value... - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_USE_COMPACTION, "foobar", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - - // Then we set the default. - assertThat(mCompactorUnderTest.useCompaction()).isEqualTo( - AppCompactor.DEFAULT_USE_COMPACTION); - } - - @Test - public void compactAction_listensToDeviceConfigChanges() throws InterruptedException { - mCompactorUnderTest.init(); - - // When we override new values for the compaction action with reasonable values... - - // There are four possible values for compactAction[Some|Full]. - for (int i = 1; i < 5; i++) { - mCountDown = new CountDownLatch(2); - int expectedSome = (AppCompactor.DEFAULT_COMPACT_ACTION_1 + i) % 4 + 1; - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_ACTION_1, Integer.toString(expectedSome), false); - int expectedFull = (AppCompactor.DEFAULT_COMPACT_ACTION_2 + i) % 4 + 1; - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_ACTION_2, Integer.toString(expectedFull), false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - - // Then the updates are reflected in the flags. - assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo( - compactActionIntToString(expectedSome)); - assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo( - compactActionIntToString(expectedFull)); - } - } - - @Test - public void compactAction_listensToDeviceConfigChangesBadValues() throws InterruptedException { - mCompactorUnderTest.init(); - - // When we override new values for the compaction action with bad values ... - mCountDown = new CountDownLatch(2); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_ACTION_1, "foo", false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_ACTION_2, "foo", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - - // Then the default values are reflected in the flag - assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo( - compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1)); - assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo( - compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2)); - - mCountDown = new CountDownLatch(2); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_ACTION_1, "", false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_ACTION_2, "", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - - assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo( - compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1)); - assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo( - compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2)); - } - - @Test - public void compactThrottle_listensToDeviceConfigChanges() throws InterruptedException { - mCompactorUnderTest.init(); - - // When we override new reasonable throttle values after init... - mCountDown = new CountDownLatch(6); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_THROTTLE_1, - Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1), false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_THROTTLE_2, - Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1), false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_THROTTLE_3, - Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1), false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_THROTTLE_4, - Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1), false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_THROTTLE_5, - Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_5 + 1), false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_THROTTLE_6, - Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_6 + 1), false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - - // Then those flags values are reflected in the compactor. - assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1); - assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1); - assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1); - assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1); - assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_5 + 1); - assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_6 + 1); - } - - @Test - public void compactThrottle_listensToDeviceConfigChangesBadValues() - throws InterruptedException { - mCompactorUnderTest.init(); - - // When one of the throttles is overridden with a bad value... - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_THROTTLE_1, "foo", false); - // Then all the throttles have the defaults set. - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_1); - assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_2); - assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_3); - assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_4); - assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_5); - assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_6); - - // Repeat for each of the throttle keys. - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_THROTTLE_2, "foo", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_1); - assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_2); - assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_3); - assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_4); - assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_5); - assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_6); - - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_THROTTLE_3, "foo", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_1); - assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_2); - assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_3); - assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_4); - assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_5); - assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_6); - - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_THROTTLE_4, "foo", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_1); - assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_2); - assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_3); - assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_4); - assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_5); - assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_6); - - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_THROTTLE_5, "foo", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_1); - assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_2); - assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_3); - assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_4); - assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_5); - assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_6); - - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_THROTTLE_6, "foo", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_1); - assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_2); - assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_3); - assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_4); - assertThat(mCompactorUnderTest.mCompactThrottleBFGS).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_5); - assertThat(mCompactorUnderTest.mCompactThrottlePersistent).isEqualTo( - AppCompactor.DEFAULT_COMPACT_THROTTLE_6); - } - - @Test - public void statsdSampleRate_listensToDeviceConfigChanges() throws InterruptedException { - mCompactorUnderTest.init(); - - // When we override mStatsdSampleRate with a reasonable value ... - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_STATSD_SAMPLE_RATE, - Float.toString(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - - // Then that override is reflected in the compactor. - assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo( - AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f); - } - - @Test - public void statsdSampleRate_listensToDeviceConfigChangesBadValues() - throws InterruptedException { - mCompactorUnderTest.init(); - - // When we override mStatsdSampleRate with an unreasonable value ... - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_STATSD_SAMPLE_RATE, "foo", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - - // Then that override is reflected in the compactor. - assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo( - AppCompactor.DEFAULT_STATSD_SAMPLE_RATE); - } - - @Test - public void statsdSampleRate_listensToDeviceConfigChangesOutOfRangeValues() - throws InterruptedException { - mCompactorUnderTest.init(); - - // When we override mStatsdSampleRate with an value outside of [0..1]... - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_STATSD_SAMPLE_RATE, - Float.toString(-1.0f), false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - - // Then the values is capped in the range. - assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(0.0f); - - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_STATSD_SAMPLE_RATE, - Float.toString(1.01f), false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - - // Then the values is capped in the range. - assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(1.0f); - } - - @Test - public void fullCompactionRssThrottleKb_listensToDeviceConfigChanges() - throws InterruptedException { - mCompactorUnderTest.init(); - - // When we override mStatsdSampleRate with a reasonable value ... - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_FULL_RSS_THROTTLE_KB, - Long.toString(AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1), false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - - // Then that override is reflected in the compactor. - assertThat(mCompactorUnderTest.mFullAnonRssThrottleKb).isEqualTo( - AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1); - } - - @Test - public void fullCompactionRssThrottleKb_listensToDeviceConfigChangesBadValues() - throws InterruptedException { - mCompactorUnderTest.init(); - - // When we override mStatsdSampleRate with an unreasonable value ... - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_FULL_RSS_THROTTLE_KB, "foo", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - - // Then that override is reflected in the compactor. - assertThat(mCompactorUnderTest.mFullAnonRssThrottleKb).isEqualTo( - AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB); - - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_FULL_RSS_THROTTLE_KB, "-100", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - - // Then that override is reflected in the compactor. - assertThat(mCompactorUnderTest.mFullAnonRssThrottleKb).isEqualTo( - AppCompactor.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB); - } - - @Test - public void fullCompactionDeltaRssThrottleKb_listensToDeviceConfigChanges() - throws InterruptedException { - mCompactorUnderTest.init(); - - // When we override mStatsdSampleRate with a reasonable value ... - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, - Long.toString(AppCompactor.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1), false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - - // Then that override is reflected in the compactor. - assertThat(mCompactorUnderTest.mFullDeltaRssThrottleKb).isEqualTo( - AppCompactor.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1); - } - - @Test - public void fullCompactionDeltaRssThrottleKb_listensToDeviceConfigChangesBadValues() - throws InterruptedException { - mCompactorUnderTest.init(); - - // When we override mStatsdSampleRate with an unreasonable value ... - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, "foo", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - - // Then that override is reflected in the compactor. - assertThat(mCompactorUnderTest.mFullDeltaRssThrottleKb).isEqualTo( - AppCompactor.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB); - - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, "-100", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - - // Then that override is reflected in the compactor. - assertThat(mCompactorUnderTest.mFullDeltaRssThrottleKb).isEqualTo( - AppCompactor.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB); - } - - @Test - public void procStateThrottle_listensToDeviceConfigChanges() - throws InterruptedException { - mCompactorUnderTest.init(); - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, "1,2,3", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactly(1, 2, 3); - - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, "", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - assertThat(mCompactorUnderTest.mProcStateThrottle).isEmpty(); - } - - @Test - public void procStateThrottle_listensToDeviceConfigChangesBadValues() - throws InterruptedException { - mCompactorUnderTest.init(); - - Set<Integer> expected = new HashSet<>(); - for (String s : TextUtils.split(AppCompactor.DEFAULT_COMPACT_PROC_STATE_THROTTLE, ",")) { - expected.add(Integer.parseInt(s)); - } - - // Not numbers - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, "foo", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactlyElementsIn(expected); - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, "1,foo", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactlyElementsIn(expected); - - // Empty splits - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, ",", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactlyElementsIn(expected); - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, ",,3", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactlyElementsIn(expected); - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - AppCompactor.KEY_COMPACT_PROC_STATE_THROTTLE, "1,,3", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - assertThat(mCompactorUnderTest.mProcStateThrottle).containsExactlyElementsIn(expected); - } - - private class TestInjector extends Injector { - - TestInjector(Context context) { - super(context); - } - - @Override - public AppOpsService getAppOpsService(File file, Handler handler) { - return mAppOpsService; - } - - @Override - public Handler getUiHandler(ActivityManagerService service) { - return mHandler; - } - } -} diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java new file mode 100644 index 000000000000..f037692886ab --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java @@ -0,0 +1,692 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import static com.android.server.am.ActivityManagerService.Injector; +import static com.android.server.am.CachedAppOptimizer.compactActionIntToString; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Process; +import android.platform.test.annotations.Presubmit; +import android.provider.DeviceConfig; +import android.text.TextUtils; + +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.server.ServiceThread; +import com.android.server.appop.AppOpsService; +import com.android.server.testables.TestableDeviceConfig; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.io.File; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Tests for {@link CachedAppOptimizer}. + * + * Build/Install/Run: + * atest FrameworksMockingServicesTests:CachedAppOptimizerTest + */ +@Presubmit +@RunWith(MockitoJUnitRunner.class) +public final class CachedAppOptimizerTest { + + private ServiceThread mThread; + + @Mock + private AppOpsService mAppOpsService; + private CachedAppOptimizer mCachedAppOptimizerUnderTest; + private HandlerThread mHandlerThread; + private Handler mHandler; + private CountDownLatch mCountDown; + + @Rule + public TestableDeviceConfig.TestableDeviceConfigRule + mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule(); + + @Before + public void setUp() { + mHandlerThread = new HandlerThread(""); + mHandlerThread.start(); + mHandler = new Handler(mHandlerThread.getLooper()); + + mThread = new ServiceThread("TestServiceThread", Process.THREAD_PRIORITY_DEFAULT, + true /* allowIo */); + mThread.start(); + + ActivityManagerService ams = new ActivityManagerService( + new TestInjector(InstrumentationRegistry.getInstrumentation().getContext()), + mThread); + mCachedAppOptimizerUnderTest = new CachedAppOptimizer(ams, + new CachedAppOptimizer.PropertyChangedCallbackForTest() { + @Override + public void onPropertyChanged() { + if (mCountDown != null) { + mCountDown.countDown(); + } + } + }); + } + + @After + public void tearDown() { + mHandlerThread.quit(); + mThread.quit(); + mCountDown = null; + } + + @Test + public void init_setsDefaults() { + mCachedAppOptimizerUnderTest.init(); + assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo( + CachedAppOptimizer.DEFAULT_USE_COMPACTION); + assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo( + compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1)); + assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo( + compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2)); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4); + assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo( + CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE); + assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6); + assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB); + assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB); + + Set<Integer> expected = new HashSet<>(); + for (String s : TextUtils.split( + CachedAppOptimizer.DEFAULT_COMPACT_PROC_STATE_THROTTLE, ",")) { + expected.add(Integer.parseInt(s)); + } + assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle) + .containsExactlyElementsIn(expected); + } + + @Test + public void init_withDeviceConfigSetsParameters() { + // When the DeviceConfig already has a flag value stored (note this test will need to + // change if the default value changes from false). + assertThat(CachedAppOptimizer.DEFAULT_USE_COMPACTION).isFalse(); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_USE_COMPACTION, "true", false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_ACTION_1, + Integer.toString((CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_ACTION_2, + Integer.toString((CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_1, + Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_2, + Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2 + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_3, + Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3 + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_4, + Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4 + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_5, + Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_6, + Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE, + Float.toString(CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_FULL_RSS_THROTTLE_KB, + Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, + Long.toString( + CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "1,2,3", false); + + // Then calling init will read and set that flag. + mCachedAppOptimizerUnderTest.init(); + assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isTrue(); + assertThat(mCachedAppOptimizerUnderTest.mCachedAppOptimizerThread.isAlive()).isTrue(); + + assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo( + compactActionIntToString( + (CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1)); + assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo( + compactActionIntToString( + (CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1)); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2 + 1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3 + 1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4 + 1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1); + assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo( + CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1); + assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1); + assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle).containsExactly(1, 2, 3); + } + + @Test + public void useCompaction_listensToDeviceConfigChanges() throws InterruptedException { + assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo( + CachedAppOptimizer.DEFAULT_USE_COMPACTION); + // When we call init and change some the flag value... + mCachedAppOptimizerUnderTest.init(); + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_USE_COMPACTION, "true", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then that new flag value is updated in the implementation. + assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isTrue(); + assertThat(mCachedAppOptimizerUnderTest.mCachedAppOptimizerThread.isAlive()).isTrue(); + + // And again, setting the flag the other way. + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_USE_COMPACTION, "false", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isFalse(); + } + + @Test + public void useCompaction_listensToDeviceConfigChangesBadValues() throws InterruptedException { + assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo( + CachedAppOptimizer.DEFAULT_USE_COMPACTION); + mCachedAppOptimizerUnderTest.init(); + + // When we push an invalid flag value... + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_USE_COMPACTION, "foobar", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then we set the default. + assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo( + CachedAppOptimizer.DEFAULT_USE_COMPACTION); + } + + @Test + public void compactAction_listensToDeviceConfigChanges() throws InterruptedException { + mCachedAppOptimizerUnderTest.init(); + + // When we override new values for the compaction action with reasonable values... + + // There are four possible values for compactAction[Some|Full]. + for (int i = 1; i < 5; i++) { + mCountDown = new CountDownLatch(2); + int expectedSome = (CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1 + i) % 4 + 1; + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_ACTION_1, Integer.toString(expectedSome), false); + int expectedFull = (CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2 + i) % 4 + 1; + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_ACTION_2, Integer.toString(expectedFull), false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then the updates are reflected in the flags. + assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo( + compactActionIntToString(expectedSome)); + assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo( + compactActionIntToString(expectedFull)); + } + } + + @Test + public void compactAction_listensToDeviceConfigChangesBadValues() throws InterruptedException { + mCachedAppOptimizerUnderTest.init(); + + // When we override new values for the compaction action with bad values ... + mCountDown = new CountDownLatch(2); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_ACTION_1, "foo", false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_ACTION_2, "foo", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then the default values are reflected in the flag + assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo( + compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1)); + assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo( + compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2)); + + mCountDown = new CountDownLatch(2); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_ACTION_1, "", false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_ACTION_2, "", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo( + compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1)); + assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo( + compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2)); + } + + @Test + public void compactThrottle_listensToDeviceConfigChanges() throws InterruptedException { + mCachedAppOptimizerUnderTest.init(); + + // When we override new reasonable throttle values after init... + mCountDown = new CountDownLatch(6); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_1, + Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_2, + Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2 + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_3, + Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3 + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_4, + Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4 + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_5, + Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_6, + Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1), false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then those flags values are reflected in the compactor. + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2 + 1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3 + 1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4 + 1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1); + } + + @Test + public void compactThrottle_listensToDeviceConfigChangesBadValues() + throws InterruptedException { + mCachedAppOptimizerUnderTest.init(); + + // When one of the throttles is overridden with a bad value... + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_1, "foo", false); + // Then all the throttles have the defaults set. + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6); + + // Repeat for each of the throttle keys. + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_2, "foo", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6); + + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_3, "foo", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6); + + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_4, "foo", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6); + + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_5, "foo", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6); + + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_6, "foo", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6); + } + + @Test + public void statsdSampleRate_listensToDeviceConfigChanges() throws InterruptedException { + mCachedAppOptimizerUnderTest.init(); + + // When we override mStatsdSampleRate with a reasonable value ... + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE, + Float.toString(CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then that override is reflected in the compactor. + assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo( + CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f); + } + + @Test + public void statsdSampleRate_listensToDeviceConfigChangesBadValues() + throws InterruptedException { + mCachedAppOptimizerUnderTest.init(); + + // When we override mStatsdSampleRate with an unreasonable value ... + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE, "foo", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then that override is reflected in the compactor. + assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo( + CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE); + } + + @Test + public void statsdSampleRate_listensToDeviceConfigChangesOutOfRangeValues() + throws InterruptedException { + mCachedAppOptimizerUnderTest.init(); + + // When we override mStatsdSampleRate with an value outside of [0..1]... + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE, + Float.toString(-1.0f), false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then the values is capped in the range. + assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo(0.0f); + + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE, + Float.toString(1.01f), false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then the values is capped in the range. + assertThat(mCachedAppOptimizerUnderTest.mStatsdSampleRate).isEqualTo(1.0f); + } + + @Test + public void fullCompactionRssThrottleKb_listensToDeviceConfigChanges() + throws InterruptedException { + mCachedAppOptimizerUnderTest.init(); + + // When we override mStatsdSampleRate with a reasonable value ... + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_FULL_RSS_THROTTLE_KB, + Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1), false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then that override is reflected in the compactor. + assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1); + } + + @Test + public void fullCompactionRssThrottleKb_listensToDeviceConfigChangesBadValues() + throws InterruptedException { + mCachedAppOptimizerUnderTest.init(); + + // When we override mStatsdSampleRate with an unreasonable value ... + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_FULL_RSS_THROTTLE_KB, "foo", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then that override is reflected in the compactor. + assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB); + + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_FULL_RSS_THROTTLE_KB, "-100", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then that override is reflected in the compactor. + assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB); + } + + @Test + public void fullCompactionDeltaRssThrottleKb_listensToDeviceConfigChanges() + throws InterruptedException { + mCachedAppOptimizerUnderTest.init(); + + // When we override mStatsdSampleRate with a reasonable value ... + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, + Long.toString( + CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1), false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then that override is reflected in the compactor. + assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1); + } + + @Test + public void fullCompactionDeltaRssThrottleKb_listensToDeviceConfigChangesBadValues() + throws InterruptedException { + mCachedAppOptimizerUnderTest.init(); + + // When we override mStatsdSampleRate with an unreasonable value ... + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, "foo", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then that override is reflected in the compactor. + assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB); + + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, "-100", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then that override is reflected in the compactor. + assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB); + } + + @Test + public void procStateThrottle_listensToDeviceConfigChanges() + throws InterruptedException { + mCachedAppOptimizerUnderTest.init(); + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "1,2,3", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle).containsExactly(1, 2, 3); + + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle).isEmpty(); + } + + @Test + public void procStateThrottle_listensToDeviceConfigChangesBadValues() + throws InterruptedException { + mCachedAppOptimizerUnderTest.init(); + + Set<Integer> expected = new HashSet<>(); + for (String s : TextUtils.split( + CachedAppOptimizer.DEFAULT_COMPACT_PROC_STATE_THROTTLE, ",")) { + expected.add(Integer.parseInt(s)); + } + + // Not numbers + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "foo", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle) + .containsExactlyElementsIn(expected); + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "1,foo", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle) + .containsExactlyElementsIn(expected); + + // Empty splits + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, ",", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle) + .containsExactlyElementsIn(expected); + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, ",,3", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle) + .containsExactlyElementsIn(expected); + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "1,,3", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle) + .containsExactlyElementsIn(expected); + } + + private class TestInjector extends Injector { + + TestInjector(Context context) { + super(context); + } + + @Override + public AppOpsService getAppOpsService(File file, Handler handler) { + return mAppOpsService; + } + + @Override + public Handler getUiHandler(ActivityManagerService service) { + return mHandler; + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java index c223f135d3df..e1e9b7e7b7cb 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java @@ -75,6 +75,7 @@ import android.os.IBinder; import android.os.IPowerManager; import android.os.PowerManager; import android.os.Process; +import android.os.RemoteCallback; import android.os.RemoteException; import android.testing.DexmakerShareClassLoaderRule; import android.view.Display; @@ -693,6 +694,18 @@ public class AbstractAccessibilityServiceConnectionTest { assertThat(result, is(false)); } + @Test + public void takeScreenshot_returnNull() { + // no canTakeScreenshot, should return null. + when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(false); + assertThat(mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY), is(nullValue())); + + // no checkAccessibilityAccess, should return null. + when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(true); + when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(false); + assertThat(mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY), is(nullValue())); + } + private void updateServiceInfo(AccessibilityServiceInfo serviceInfo, int eventType, int feedbackType, int flags, String[] packageNames, int notificationTimeout) { serviceInfo.eventTypes = eventType; @@ -832,5 +845,8 @@ public class AbstractAccessibilityServiceConnectionTest { @Override public void onFingerprintGesture(int gesture) {} + + @Override + public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) {} } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java index 04ac7fe91008..150409766f47 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java @@ -346,6 +346,14 @@ public class AccessibilitySecurityPolicyTest { } @Test + public void canTakeScreenshot_hasCapability_returnTrue() { + when(mMockA11yServiceConnection.getCapabilities()) + .thenReturn(AccessibilityServiceInfo.CAPABILITY_CAN_TAKE_SCREENSHOT); + + assertTrue(mA11ySecurityPolicy.canTakeScreenshotLocked(mMockA11yServiceConnection)); + } + + @Test public void resolveProfileParent_userIdIsCurrentUser_returnCurrentUser() { final int currentUserId = 10; final int userId = currentUserId; 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 96d9c476bcde..ac5169c7c715 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java @@ -113,7 +113,6 @@ public class AccessibilityUserStateTest { mUserState.mAccessibilityButtonTargets.add(COMPONENT_NAME.flattenToString()); mUserState.setTouchExplorationEnabledLocked(true); mUserState.setDisplayMagnificationEnabledLocked(true); - mUserState.setNavBarMagnificationEnabledLocked(true); mUserState.setAutoclickEnabledLocked(true); mUserState.setUserNonInteractiveUiTimeoutLocked(30); mUserState.setUserInteractiveUiTimeoutLocked(30); @@ -132,7 +131,6 @@ public class AccessibilityUserStateTest { assertTrue(mUserState.mAccessibilityButtonTargets.isEmpty()); assertFalse(mUserState.isTouchExplorationEnabledLocked()); assertFalse(mUserState.isDisplayMagnificationEnabledLocked()); - assertFalse(mUserState.isNavBarMagnificationEnabledLocked()); assertFalse(mUserState.isAutoclickEnabledLocked()); assertEquals(0, mUserState.getUserNonInteractiveUiTimeoutLocked()); assertEquals(0, mUserState.getUserInteractiveUiTimeoutLocked()); diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index 79cc3db90fff..f1220146c5e5 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -31,6 +31,7 @@ import static com.android.server.am.UserController.USER_SWITCH_TIMEOUT_MSG; import static com.google.android.collect.Lists.newArrayList; import static com.google.android.collect.Sets.newHashSet; +import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertEquals; @@ -54,6 +55,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.annotation.UserIdInt; +import android.app.ActivityManager; import android.app.IUserSwitchObserver; import android.content.Context; import android.content.IIntentReceiver; @@ -74,10 +76,10 @@ import android.os.storage.IStorageManager; import android.platform.test.annotations.Presubmit; import android.util.Log; - import androidx.test.filters.SmallTest; import com.android.server.FgThread; +import com.android.server.am.UserState.KeyEvictedCallback; import com.android.server.pm.UserManagerService; import com.android.server.wm.WindowManagerService; @@ -107,6 +109,7 @@ public class UserControllerTest { private static final int TEST_USER_ID = 100; private static final int TEST_USER_ID1 = 101; private static final int TEST_USER_ID2 = 102; + private static final int TEST_USER_ID3 = 103; private static final int NONEXIST_USER_ID = 2; private static final int TEST_PRE_CREATED_USER_ID = 103; @@ -120,6 +123,8 @@ public class UserControllerTest { private TestInjector mInjector; private final HashMap<Integer, UserState> mUserStates = new HashMap<>(); + private final KeyEvictedCallback mKeyEvictedCallback = (userId) -> { /* ignore */ }; + private static final List<String> START_FOREGROUND_USER_ACTIONS = newArrayList( Intent.ACTION_USER_STARTED, Intent.ACTION_USER_SWITCHED, @@ -153,6 +158,7 @@ public class UserControllerTest { doNothing().when(mInjector).activityManagerOnUserStopped(anyInt()); doNothing().when(mInjector).clearBroadcastQueueForUser(anyInt()); doNothing().when(mInjector).stackSupervisorRemoveUser(anyInt()); + // All UserController params are set to default. mUserController = new UserController(mInjector); setUpUser(TEST_USER_ID, NO_USERINFO_FLAGS); setUpUser(TEST_PRE_CREATED_USER_ID, NO_USERINFO_FLAGS, /* preCreated=*/ true); @@ -187,7 +193,9 @@ public class UserControllerTest { @Test public void testStartUserUIDisabled() { - mUserController.mUserSwitchUiEnabled = false; + mUserController.setInitialConfig(/* userSwitchUiEnabled= */ false, + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + mUserController.startUser(TEST_USER_ID, true /* foreground */); verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt()); verify(mInjector.getWindowManager(), never()).stopFreezingScreen(); @@ -245,7 +253,9 @@ public class UserControllerTest { @Test public void testFailedStartUserInForeground() { - mUserController.mUserSwitchUiEnabled = false; + mUserController.setInitialConfig(/* userSwitchUiEnabled= */ false, + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + mUserController.startUserInForeground(NONEXIST_USER_ID); verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean()); verify(mInjector.getWindowManager()).setSwitchingUser(false); @@ -326,7 +336,9 @@ public class UserControllerTest { @Test public void testContinueUserSwitchUIDisabled() throws RemoteException { - mUserController.mUserSwitchUiEnabled = false; + mUserController.setInitialConfig(/* userSwitchUiEnabled= */ false, + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + // Start user -- this will update state of mUserController mUserController.startUser(TEST_USER_ID, true); Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG); @@ -389,11 +401,13 @@ public class UserControllerTest { * Test stopping of user from max running users limit. */ @Test - public void testUserStoppingForMultipleUsersNormalMode() + public void testUserLockingFromUserSwitchingForMultipleUsersNonDelayedLocking() throws InterruptedException, RemoteException { + mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + setUpUser(TEST_USER_ID1, 0); setUpUser(TEST_USER_ID2, 0); - mUserController.mMaxRunningUsers = 3; int numerOfUserSwitches = 1; addForegroundUserAndContinueUserSwitch(TEST_USER_ID, UserHandle.USER_SYSTEM, numerOfUserSwitches, false); @@ -430,12 +444,13 @@ public class UserControllerTest { * all middle steps which takes too much work to mock. */ @Test - public void testUserStoppingForMultipleUsersDelayedLockingMode() + public void testUserLockingFromUserSwitchingForMultipleUsersDelayedLockingMode() throws InterruptedException, RemoteException { + mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ true); + setUpUser(TEST_USER_ID1, 0); setUpUser(TEST_USER_ID2, 0); - mUserController.mMaxRunningUsers = 3; - mUserController.mDelayUserDataLocking = true; int numerOfUserSwitches = 1; addForegroundUserAndContinueUserSwitch(TEST_USER_ID, UserHandle.USER_SYSTEM, numerOfUserSwitches, false); @@ -456,7 +471,7 @@ public class UserControllerTest { // Skip all other steps and test unlock delaying only UserState uss = mUserStates.get(TEST_USER_ID); uss.setState(UserState.STATE_SHUTDOWN); // necessary state change from skipped part - mUserController.finishUserStopped(uss); + mUserController.finishUserStopped(uss, /* allowDelayedLocking= */ true); // Cannot mock FgThread handler, so confirm that there is no posted message left before // checking. waitForHandlerToComplete(FgThread.getHandler(), HANDLER_WAIT_TIME_MS); @@ -473,12 +488,87 @@ public class UserControllerTest { mUserController.getRunningUsersLU()); UserState ussUser1 = mUserStates.get(TEST_USER_ID1); ussUser1.setState(UserState.STATE_SHUTDOWN); - mUserController.finishUserStopped(ussUser1); + mUserController.finishUserStopped(ussUser1, /* allowDelayedLocking= */ true); waitForHandlerToComplete(FgThread.getHandler(), HANDLER_WAIT_TIME_MS); verify(mInjector.mStorageManagerMock, times(1)) .lockUserKey(TEST_USER_ID); } + /** + * Test locking user with mDelayUserDataLocking false. + */ + @Test + public void testUserLockingWithStopUserForNonDelayedLockingMode() throws Exception { + mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + + setUpAndStartUserInBackground(TEST_USER_ID); + assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID, /* delayedLocking= */ true, + /* keyEvictedCallback= */ null, /* expectLocking= */ true); + + setUpAndStartUserInBackground(TEST_USER_ID1); + assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* delayedLocking= */ true, + /* keyEvictedCallback= */ mKeyEvictedCallback, /* expectLocking= */ true); + + setUpAndStartUserInBackground(TEST_USER_ID2); + assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID2, /* delayedLocking= */ false, + /* keyEvictedCallback= */ null, /* expectLocking= */ true); + + setUpAndStartUserInBackground(TEST_USER_ID3); + assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID3, /* delayedLocking= */ false, + /* keyEvictedCallback= */ mKeyEvictedCallback, /* expectLocking= */ true); + } + + /** + * Test conditional delayed locking with mDelayUserDataLocking true. + */ + @Test + public void testUserLockingForDelayedLockingMode() throws Exception { + mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ true); + + // delayedLocking set and no KeyEvictedCallback, so it should not lock. + setUpAndStartUserInBackground(TEST_USER_ID); + assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID, /* delayedLocking= */ true, + /* keyEvictedCallback= */ null, /* expectLocking= */ false); + + setUpAndStartUserInBackground(TEST_USER_ID1); + assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* delayedLocking= */ true, + /* keyEvictedCallback= */ mKeyEvictedCallback, /* expectLocking= */ true); + + setUpAndStartUserInBackground(TEST_USER_ID2); + assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID2, /* delayedLocking= */ false, + /* keyEvictedCallback= */ null, /* expectLocking= */ true); + + setUpAndStartUserInBackground(TEST_USER_ID3); + assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID3, /* delayedLocking= */ false, + /* keyEvictedCallback= */ mKeyEvictedCallback, /* expectLocking= */ true); + } + + private void setUpAndStartUserInBackground(int userId) throws Exception { + setUpUser(userId, 0); + mUserController.startUser(userId, /* foreground= */ false); + verify(mInjector.mStorageManagerMock, times(1)) + .unlockUserKey(TEST_USER_ID, 0, null, null); + mUserStates.put(userId, mUserController.getStartedUserState(userId)); + } + + private void assertUserLockedOrUnlockedAfterStopping(int userId, boolean delayedLocking, + KeyEvictedCallback keyEvictedCallback, boolean expectLocking) throws Exception { + int r = mUserController.stopUser(userId, /* force= */ true, /* delayedLocking= */ + delayedLocking, null, keyEvictedCallback); + assertThat(r).isEqualTo(ActivityManager.USER_OP_SUCCESS); + // fake all interim steps + UserState ussUser = mUserStates.get(userId); + ussUser.setState(UserState.STATE_SHUTDOWN); + // Passing delayedLocking invalidates incorrect internal data passing but currently there is + // no easy way to get that information passed through lambda. + mUserController.finishUserStopped(ussUser, delayedLocking); + waitForHandlerToComplete(FgThread.getHandler(), HANDLER_WAIT_TIME_MS); + verify(mInjector.mStorageManagerMock, times(expectLocking ? 1 : 0)) + .lockUserKey(userId); + } + private void addForegroundUserAndContinueUserSwitch(int newUserId, int expectedOldUserId, int expectedNumberOfCalls, boolean expectOldUserStopping) throws RemoteException { diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 45729e5fd41f..d6703613d3fc 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -1968,6 +1968,29 @@ public class DevicePolicyManagerTest extends DpmTestBase { // TODO Make sure restrictions are written to the file. } + private static final Set<String> PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS = + Sets.newSet( + UserManager.DISALLOW_CONFIG_DATE_TIME, + UserManager.DISALLOW_ADD_USER, + UserManager.DISALLOW_BLUETOOTH, + UserManager.DISALLOW_BLUETOOTH_SHARING, + UserManager.DISALLOW_CONFIG_BLUETOOTH, + UserManager.DISALLOW_CONFIG_CELL_BROADCASTS, + UserManager.DISALLOW_CONFIG_LOCATION, + UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, + UserManager.DISALLOW_CONFIG_PRIVATE_DNS, + UserManager.DISALLOW_CONFIG_TETHERING, + UserManager.DISALLOW_CONFIG_WIFI, + UserManager.DISALLOW_CONTENT_CAPTURE, + UserManager.DISALLOW_CONTENT_SUGGESTIONS, + UserManager.DISALLOW_DATA_ROAMING, + UserManager.DISALLOW_DEBUGGING_FEATURES, + UserManager.DISALLOW_SAFE_BOOT, + UserManager.DISALLOW_SHARE_LOCATION, + UserManager.DISALLOW_SMS, + UserManager.DISALLOW_USB_FILE_TRANSFER + ); + public void testSetUserRestriction_asPoOfOrgOwnedDevice() throws Exception { final int MANAGED_PROFILE_USER_ID = DpmMockContext.CALLER_USER_HANDLE; final int MANAGED_PROFILE_ADMIN_UID = @@ -1980,15 +2003,9 @@ public class DevicePolicyManagerTest extends DpmTestBase { when(getServices().userManager.getProfileParent(MANAGED_PROFILE_USER_ID)) .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0)); - parentDpm.addUserRestriction(admin1, UserManager.DISALLOW_CONFIG_DATE_TIME); - verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions( - eq(MANAGED_PROFILE_USER_ID), - MockUtils.checkUserRestrictions(UserManager.DISALLOW_CONFIG_DATE_TIME), - eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE)); - reset(getServices().userManagerInternal); - - parentDpm.clearUserRestriction(admin1, UserManager.DISALLOW_CONFIG_DATE_TIME); - reset(getServices().userManagerInternal); + for (String restriction : PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS) { + addAndRemoveUserRestrictionOnParentDpm(restriction); + } parentDpm.setCameraDisabled(admin1, true); verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions( @@ -2005,6 +2022,20 @@ public class DevicePolicyManagerTest extends DpmTestBase { reset(getServices().userManagerInternal); } + private void addAndRemoveUserRestrictionOnParentDpm(String restriction) { + parentDpm.addUserRestriction(admin1, restriction); + verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions( + eq(DpmMockContext.CALLER_USER_HANDLE), + MockUtils.checkUserRestrictions(restriction), + eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE)); + parentDpm.clearUserRestriction(admin1, restriction); + DpmTestUtils.assertRestrictions( + DpmTestUtils.newRestrictions(), + parentDpm.getUserRestrictions(admin1) + ); + reset(getServices().userManagerInternal); + } + public void testNoDefaultEnabledUserRestrictions() throws Exception { mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS); mContext.callerPermissions.add(permission.MANAGE_USERS); diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java index 7a070ee72b5d..eda2b701fd8d 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java @@ -188,7 +188,7 @@ public class RuleEvaluatorTest { } @Test - public void testEvaluateRules_ruleNotInDNF_ignoreAndAllow() { + public void testEvaluateRules_orRules() { CompoundFormula compoundFormula = new CompoundFormula( CompoundFormula.OR, @@ -206,11 +206,11 @@ public class RuleEvaluatorTest { IntegrityCheckResult result = RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA); - assertEquals(ALLOW, result.getEffect()); + assertEquals(DENY, result.getEffect()); } @Test - public void testEvaluateRules_compoundFormulaWithNot_allow() { + public void testEvaluateRules_compoundFormulaWithNot_deny() { CompoundFormula openSubFormula = new CompoundFormula( CompoundFormula.AND, @@ -230,7 +230,7 @@ public class RuleEvaluatorTest { IntegrityCheckResult result = RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA); - assertEquals(ALLOW, result.getEffect()); + assertEquals(DENY, result.getEffect()); } @Test diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java index c7cc343dd77e..57274bf13499 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java @@ -53,17 +53,17 @@ public class ByteTrackedOutputStreamTest { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ByteTrackedOutputStream byteTrackedOutputStream = new ByteTrackedOutputStream(outputStream); - BitOutputStream bitOutputStream = new BitOutputStream(); + BitOutputStream bitOutputStream = new BitOutputStream(byteTrackedOutputStream); bitOutputStream.setNext(/* numOfBits= */5, /* value= */1); - byteTrackedOutputStream.write(bitOutputStream.toByteArray()); + bitOutputStream.flush(); // Even though we wrote 5 bits, this will complete to 1 byte. assertThat(byteTrackedOutputStream.getWrittenBytesCount()).isEqualTo(1); // Add a bit less than 2 bytes (10 bits). - bitOutputStream.clear(); bitOutputStream.setNext(/* numOfBits= */10, /* value= */1); - byteTrackedOutputStream.write(bitOutputStream.toByteArray()); + bitOutputStream.flush(); + assertThat(byteTrackedOutputStream.getWrittenBytesCount()).isEqualTo(3); assertThat(outputStream.toByteArray().length).isEqualTo(3); } diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java index 291cfeea5bbc..742952e056bc 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java @@ -115,8 +115,7 @@ public class RuleIndexingControllerTest { + getKeyValueString(START_INDEXING_KEY, 500) + getKeyValueString(END_INDEXING_KEY, 900) + getKeyValueString(START_INDEXING_KEY, 900) - + getKeyValueString(END_INDEXING_KEY, 945) - + getBits(1, 1)); + + getKeyValueString(END_INDEXING_KEY, 945)); ByteBuffer rule = ByteBuffer.allocate(stringBytes.length); rule.put(stringBytes); InputStream inputStream = new ByteArrayInputStream(rule.array()); @@ -153,8 +152,7 @@ public class RuleIndexingControllerTest { + getKeyValueString("888", 800) + getKeyValueString(END_INDEXING_KEY, 900) + getKeyValueString(START_INDEXING_KEY, 900) - + getKeyValueString(END_INDEXING_KEY, 945) - + getBits(1, 1)); + + getKeyValueString(END_INDEXING_KEY, 945)); ByteBuffer rule = ByteBuffer.allocate(stringBytes.length); rule.put(stringBytes); return new ByteArrayInputStream(rule.array()); diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java index bc2aac0acf10..8ee5f5fb9de8 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java @@ -139,10 +139,12 @@ public class RuleBinarySerializerTest { SERIALIZED_START_INDEXING_KEY + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32) + SERIALIZED_END_INDEXING_KEY - + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */32); + + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32); byte[] expectedIndexingBytes = - getBytes(serializedIndexingBytes + serializedIndexingBytes - + serializedIndexingBytes + getBits(1, 1)); + getBytes( + serializedIndexingBytes + + serializedIndexingBytes + + serializedIndexingBytes); expectedIndexingOutputStream.write(expectedIndexingBytes); assertThat(indexingOutputStream.toByteArray()) .isEqualTo(expectedIndexingOutputStream.toByteArray()); @@ -200,11 +202,14 @@ public class RuleBinarySerializerTest { SERIALIZED_START_INDEXING_KEY + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32) + SERIALIZED_END_INDEXING_KEY - + getBits(DEFAULT_FORMAT_VERSION_BYTES.length - + getBytes(expectedBits).length, /* numOfBits= */ 32); - expectedIndexingOutputStream.write(getBytes( - expectedIndexingBitsForIndexed + expectedIndexingBitsForIndexed - + expectedIndexingBitsForUnindexed + getBits(1, 1))); + + getBits( + DEFAULT_FORMAT_VERSION_BYTES.length + getBytes(expectedBits).length, + /* numOfBits= */ 32); + expectedIndexingOutputStream.write( + getBytes( + expectedIndexingBitsForIndexed + + expectedIndexingBitsForIndexed + + expectedIndexingBitsForUnindexed)); assertThat(indexingOutputStream.toByteArray()) .isEqualTo(expectedIndexingOutputStream.toByteArray()); @@ -513,16 +518,19 @@ public class RuleBinarySerializerTest { // and 225 non-indexed rules.. List<Rule> ruleList = new ArrayList(); for (int count = 0; count < ruleCount; count++) { - ruleList.add(getRuleWithPackageNameAndSampleInstallerName( - String.format("%s%04d", packagePrefix, count))); + ruleList.add( + getRuleWithPackageNameAndSampleInstallerName( + String.format("%s%04d", packagePrefix, count))); } for (int count = 0; count < ruleCount; count++) { - ruleList.add(getRuleWithAppCertificateAndSampleInstallerName( - String.format("%s%04d", appCertificatePrefix, count))); + ruleList.add( + getRuleWithAppCertificateAndSampleInstallerName( + String.format("%s%04d", appCertificatePrefix, count))); } for (int count = 0; count < ruleCount; count++) { - ruleList.add(getNonIndexedRuleWithInstallerName( - String.format("%s%04d", installerNamePrefix, count))); + ruleList.add( + getNonIndexedRuleWithInstallerName( + String.format("%s%04d", installerNamePrefix, count))); } // Serialize the rules. @@ -543,8 +551,7 @@ public class RuleBinarySerializerTest { int totalBytesWritten = DEFAULT_FORMAT_VERSION_BYTES.length; String expectedIndexingBytesForPackageNameIndexed = - SERIALIZED_START_INDEXING_KEY - + getBits(totalBytesWritten, /* numOfBits= */ 32); + SERIALIZED_START_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32); for (int count = 0; count < ruleCount; count++) { String packageName = String.format("%s%04d", packagePrefix, count); if (count > 0 && count % INDEXING_BLOCK_SIZE == 0) { @@ -556,18 +563,17 @@ public class RuleBinarySerializerTest { } byte[] bytesForPackage = - getBytes(getSerializedCompoundRuleWithPackageNameAndSampleInstallerName( - packageName)); + getBytes( + getSerializedCompoundRuleWithPackageNameAndSampleInstallerName( + packageName)); expectedOrderedRuleOutputStream.write(bytesForPackage); totalBytesWritten += bytesForPackage.length; } expectedIndexingBytesForPackageNameIndexed += - SERIALIZED_END_INDEXING_KEY - + getBits(totalBytesWritten, /* numOfBits= */ 32); + SERIALIZED_END_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32); String expectedIndexingBytesForAppCertificateIndexed = - SERIALIZED_START_INDEXING_KEY - + getBits(totalBytesWritten, /* numOfBits= */ 32); + SERIALIZED_START_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32); for (int count = 0; count < ruleCount; count++) { String appCertificate = String.format("%s%04d", appCertificatePrefix, count); if (count > 0 && count % INDEXING_BLOCK_SIZE == 0) { @@ -579,33 +585,32 @@ public class RuleBinarySerializerTest { } byte[] bytesForPackage = - getBytes(getSerializedCompoundRuleWithCertificateNameAndSampleInstallerName( - appCertificate)); + getBytes( + getSerializedCompoundRuleWithCertificateNameAndSampleInstallerName( + appCertificate)); expectedOrderedRuleOutputStream.write(bytesForPackage); totalBytesWritten += bytesForPackage.length; } expectedIndexingBytesForAppCertificateIndexed += - SERIALIZED_END_INDEXING_KEY - + getBits(totalBytesWritten, /* numOfBits= */ 32); + SERIALIZED_END_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32); String expectedIndexingBytesForUnindexed = - SERIALIZED_START_INDEXING_KEY - + getBits(totalBytesWritten, /* numOfBits= */ 32); + SERIALIZED_START_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32); for (int count = 0; count < ruleCount; count++) { byte[] bytesForPackage = - getBytes(getSerializedCompoundRuleWithInstallerNameAndInstallerCert( - String.format("%s%04d", installerNamePrefix, count))); + getBytes( + getSerializedCompoundRuleWithInstallerNameAndInstallerCert( + String.format("%s%04d", installerNamePrefix, count))); expectedOrderedRuleOutputStream.write(bytesForPackage); totalBytesWritten += bytesForPackage.length; } expectedIndexingBytesForUnindexed += - SERIALIZED_END_INDEXING_KEY - + getBits(totalBytesWritten, /* numOfBits= */ 32); + SERIALIZED_END_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32); expectedIndexingOutputStream.write( - getBytes(expectedIndexingBytesForPackageNameIndexed - + expectedIndexingBytesForAppCertificateIndexed - + expectedIndexingBytesForUnindexed - + getBits(1, 1))); + getBytes( + expectedIndexingBytesForPackageNameIndexed + + expectedIndexingBytesForAppCertificateIndexed + + expectedIndexingBytesForUnindexed)); assertThat(ruleOutputStream.toByteArray()) .isEqualTo(expectedOrderedRuleOutputStream.toByteArray()); diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index e1c489e65984..3d40637f22e2 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -1804,9 +1804,11 @@ public class NetworkPolicyManagerServiceTest { .getService(NetworkPolicyManagerInternal.class); npmi.onStatsProviderLimitReached("TEST"); - // Verifies that the limit reached leads to a force update. + // Verifies that the limit reached leads to a force update and new limit should be set. postMsgAndWaitForCompletion(); verify(mStatsService).forceUpdate(); + postMsgAndWaitForCompletion(); + verify(mStatsService).setStatsProviderLimit(TEST_IFACE, 10000L - 4999L - 1999L); } /** diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java index cc170af2c57b..c56034adb73d 100644 --- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java +++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java @@ -335,7 +335,9 @@ public class SoundTriggerMiddlewareImplTest { }).when(driver).getProperties_2_3(any()); } - mService = new SoundTriggerMiddlewareImpl(mHalDriver, mAudioSessionProvider); + mService = new SoundTriggerMiddlewareImpl(() -> { + return mHalDriver; + }, mAudioSessionProvider); } private Pair<Integer, SoundTriggerHwCallback> loadGenericModel_2_0(ISoundTriggerModule module, @@ -798,12 +800,12 @@ public class SoundTriggerMiddlewareImplTest { @Override public boolean linkToDeath(DeathRecipient recipient, long cookie) { - throw new UnsupportedOperationException(); + return true; } @Override public boolean unlinkToDeath(DeathRecipient recipient) { - throw new UnsupportedOperationException(); + return true; } }; diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java index 399cf49ac06f..135d00586329 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT; import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; @@ -44,6 +45,7 @@ import android.testing.DexmakerShareClassLoaderRule; import androidx.test.filters.SmallTest; +import com.android.internal.app.BlockedAppActivity; import com.android.internal.app.HarmfulAppWarningActivity; import com.android.internal.app.SuspendedAppActivity; import com.android.internal.app.UnlaunchableAppActivity; @@ -105,6 +107,8 @@ public class ActivityStartInterceptorTest { private PackageManagerService mPackageManager; @Mock private ActivityManagerInternal mAmInternal; + @Mock + private LockTaskController mLockTaskController; private ActivityStartInterceptor mInterceptor; private ActivityInfo mAInfo = new ActivityInfo(); @@ -145,6 +149,13 @@ public class ActivityStartInterceptorTest { when(mPackageManager.getHarmfulAppWarning(TEST_PACKAGE_NAME, TEST_USER_ID)) .thenReturn(null); + // Mock LockTaskController + mAInfo.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT; + when(mService.getLockTaskController()).thenReturn(mLockTaskController); + when(mLockTaskController.isActivityAllowed( + TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT)) + .thenReturn(true); + // Initialise activity info mAInfo.applicationInfo = new ApplicationInfo(); mAInfo.packageName = mAInfo.applicationInfo.packageName = TEST_PACKAGE_NAME; @@ -196,6 +207,18 @@ public class ActivityStartInterceptorTest { } @Test + public void testInterceptLockTaskModeViolationPackage() { + when(mLockTaskController.isActivityAllowed( + TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT)) + .thenReturn(false); + + assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null)); + + assertTrue(BlockedAppActivity.createIntent(TEST_USER_ID, TEST_PACKAGE_NAME) + .filterEquals(mInterceptor.mIntent)); + } + + @Test public void testInterceptQuietProfile() { // GIVEN that the user the activity is starting as is currently in quiet mode when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true); diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java index 039ff604f3f1..75ec53d69a71 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java @@ -29,6 +29,9 @@ import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME; import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD; import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE; import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER; import static android.os.Process.SYSTEM_UID; import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT; @@ -693,6 +696,38 @@ public class LockTaskControllerTest { assertTrue((StatusBarManager.DISABLE2_QUICK_SETTINGS & flags.second) != 0); } + @Test + public void testIsActivityAllowed() { + // WHEN lock task mode is not enabled + assertTrue(mLockTaskController.isActivityAllowed( + TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT)); + + // WHEN lock task mode is enabled + Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + mLockTaskController.startLockTaskMode(tr, false, TEST_UID); + + + // package with LOCK_TASK_LAUNCH_MODE_ALWAYS should always be allowed + assertTrue(mLockTaskController.isActivityAllowed( + TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_ALWAYS)); + + // unwhitelisted package should not be allowed + assertFalse(mLockTaskController.isActivityAllowed( + TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT)); + + // update the whitelist + String[] whitelist = new String[] { TEST_PACKAGE_NAME }; + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist); + + // whitelisted package should be allowed + assertTrue(mLockTaskController.isActivityAllowed( + TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT)); + + // package with LOCK_TASK_LAUNCH_MODE_NEVER should never be allowed + assertFalse(mLockTaskController.isActivityAllowed( + TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_NEVER)); + } + private Task getTask(int lockTaskAuth) { return getTask(TEST_PACKAGE_NAME, lockTaskAuth); } diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index 6005f77605a6..ab22d46df526 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -597,19 +597,25 @@ public class ProvisioningManager { /** * Notify the framework that an RCS autoconfiguration XML file has been received for * provisioning. + * <p> + * Requires Permission: Manifest.permission.MODIFY_PHONE_STATE or that the calling app has + * carrier privileges (see {@link #hasCarrierPrivileges}). * @param config The XML file to be read. ASCII/UTF8 encoded text if not compressed. * @param isCompressed The XML file is compressed in gzip format and must be decompressed * before being read. - * @hide + * */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) { if (config == null) { throw new IllegalArgumentException("Must include a non-null config XML file."); } - // TODO: Connect to ImsConfigImplBase. - throw new UnsupportedOperationException("notifyRcsAutoConfigurationReceived is not" - + "supported"); + try { + getITelephony().notifyRcsAutoConfigurationReceived(mSubId, config, isCompressed); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } private static boolean isImsAvailableOnDevice() { diff --git a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl index 53e459697958..57206c9f059a 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl @@ -40,4 +40,5 @@ interface IImsConfig { // Return result code defined in ImsConfig#OperationStatusConstants int setConfigString(int item, String value); void updateImsCarrierConfigs(in PersistableBundle bundle); + void notifyRcsAutoConfigurationReceived(in byte[] config, boolean isCompressed); } diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java index 60cf216627a3..6a2638bc7221 100644 --- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java @@ -17,6 +17,7 @@ package android.telephony.ims.stub; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.SystemApi; import android.annotation.TestApi; import android.content.Context; @@ -200,6 +201,12 @@ public class ImsConfigImplBase { } } + @Override + public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) + throws RemoteException { + getImsConfigImpl().notifyRcsAutoConfigurationReceived(config, isCompressed); + } + private void notifyImsConfigChanged(int item, int value) throws RemoteException { getImsConfigImpl().notifyConfigChanged(item, value); } @@ -358,9 +365,9 @@ public class ImsConfigImplBase { * @param config The XML file to be read, if not compressed, it should be in ASCII/UTF8 format. * @param isCompressed The XML file is compressed in gzip format and must be decompressed * before being read. - * @hide + * */ - public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) { + public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) { } /** diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 28f3974162b7..cdb95a8193b4 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -2137,4 +2137,9 @@ interface ITelephony { * Command line command to enable or disable handling of CEP data for test purposes. */ oneway void setCepEnabled(boolean isCepEnabled); + + /** + * Notify Rcs auto config received. + */ + void notifyRcsAutoConfigurationReceived(int subId, in byte[] config, boolean isCompressed); } diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java index 3877cc139a3e..ce20311170c1 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java @@ -23,12 +23,14 @@ import static com.google.common.truth.Truth.assertThat; import android.Manifest; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; import android.os.ParcelFileDescriptor; +import android.os.storage.StorageManager; import android.provider.DeviceConfig; import androidx.test.platform.app.InstrumentationRegistry; @@ -185,12 +187,6 @@ public class StagedRollbackTest { */ @Test public void testNativeWatchdogTriggersRollback_Phase1() throws Exception { - // When multiple staged sessions are installed on a device which doesn't support checkpoint, - // only the 1st one will prevail. We have to check no other rollbacks available to ensure - // TestApp.A is always the 1st and the only one to commit so rollback can work as intended. - // If there are leftover rollbacks from previous tests, this assertion will fail. - assertThat(RollbackUtils.getRollbackManager().getAvailableRollbacks()).isEmpty(); - Uninstall.packages(TestApp.A); Install.single(TestApp.A1).commit(); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); @@ -220,6 +216,64 @@ public class StagedRollbackTest { TestApp.A)).isNotNull(); } + /** + * Stage install an apk with rollback that will be later triggered by unattributable crash. + */ + @Test + public void testNativeWatchdogTriggersRollbackForAll_Phase1() throws Exception { + Uninstall.packages(TestApp.A); + Install.single(TestApp.A1).commit(); + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); + + Install.single(TestApp.A2).setEnableRollback().setStaged().commit(); + } + + /** + * Verify the rollback is available and then install another package with rollback. + */ + @Test + public void testNativeWatchdogTriggersRollbackForAll_Phase2() throws Exception { + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); + RollbackManager rm = RollbackUtils.getRollbackManager(); + assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), + TestApp.A)).isNotNull(); + + // Install another package with rollback + Uninstall.packages(TestApp.B); + Install.single(TestApp.B1).commit(); + assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1); + + Install.single(TestApp.B2).setEnableRollback().setStaged().commit(); + } + + /** + * Verify the rollbacks are available. + */ + @Test + public void testNativeWatchdogTriggersRollbackForAll_Phase3() throws Exception { + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); + assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2); + RollbackManager rm = RollbackUtils.getRollbackManager(); + assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), + TestApp.A)).isNotNull(); + assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), + TestApp.B)).isNotNull(); + } + + /** + * Verify the rollbacks are committed after crashing. + */ + @Test + public void testNativeWatchdogTriggersRollbackForAll_Phase4() throws Exception { + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); + assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1); + RollbackManager rm = RollbackUtils.getRollbackManager(); + assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(), + TestApp.A)).isNotNull(); + assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(), + TestApp.B)).isNotNull(); + } + @Test public void testNetworkFailedRollback_Phase1() throws Exception { // Remove available rollbacks and uninstall NetworkStack on /data/ @@ -438,6 +492,7 @@ public class StagedRollbackTest { RollbackManager rm = RollbackUtils.getRollbackManager(); rm.getAvailableRollbacks().stream().flatMap(info -> info.getPackages().stream()) .map(info -> info.getPackageName()).forEach(rm::expireRollbackForPackage); + assertThat(RollbackUtils.getRollbackManager().getAvailableRollbacks()).isEmpty(); } private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test"; @@ -498,4 +553,11 @@ public class StagedRollbackTest { .executeShellCommand(cmd); IoUtils.closeQuietly(pfd); } + + @Test + public void isCheckpointSupported() { + Context context = InstrumentationRegistry.getInstrumentation().getContext(); + StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); + assertThat(sm.isCheckpointSupported()).isTrue(); + } } diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java index 6daa6bc723c4..181e29a3081e 100644 --- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java +++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java @@ -17,6 +17,7 @@ package com.android.tests.rollback.host; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; import static org.testng.Assert.assertThrows; import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; @@ -62,6 +63,7 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { "rm -f /system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex " + "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex"); getDevice().reboot(); + runPhase("testCleanUp"); } @After @@ -95,7 +97,6 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { @Test public void testNativeWatchdogTriggersRollback() throws Exception { - //Stage install ModuleMetadata package - this simulates a Mainline module update runPhase("testNativeWatchdogTriggersRollback_Phase1"); // Reboot device to activate staged package @@ -121,6 +122,40 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { runPhase("testNativeWatchdogTriggersRollback_Phase3"); } + @Test + public void testNativeWatchdogTriggersRollbackForAll() throws Exception { + // This test requires committing multiple staged rollbacks + assumeTrue(isCheckpointSupported()); + + // Install a package with rollback enabled. + runPhase("testNativeWatchdogTriggersRollbackForAll_Phase1"); + getDevice().reboot(); + + // Once previous staged install is applied, install another package + runPhase("testNativeWatchdogTriggersRollbackForAll_Phase2"); + getDevice().reboot(); + + // Verify the new staged install has also been applied successfully. + runPhase("testNativeWatchdogTriggersRollbackForAll_Phase3"); + + // crash system_server enough times to trigger a rollback + crashProcess("system_server", NATIVE_CRASHES_THRESHOLD); + + // Rollback should be committed automatically now. + // Give time for rollback to be committed. This could take a while, + // because we need all of the following to happen: + // 1. system_server comes back up and boot completes. + // 2. Rollback health observer detects updatable crashing signal. + // 3. Staged rollback session becomes ready. + // 4. Device actually reboots. + // So we give a generous timeout here. + assertTrue(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5))); + getDevice().waitForDeviceAvailable(); + + // verify all available rollbacks have been committed + runPhase("testNativeWatchdogTriggersRollbackForAll_Phase4"); + } + /** * Tests failed network health check triggers watchdog staged rollbacks. */ @@ -244,4 +279,13 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { // Find the NetworkStack path (can be NetworkStack.apk or NetworkStackNext.apk) return getDevice().executeShellCommand("ls /system/priv-app/NetworkStack*/*.apk"); } + + private boolean isCheckpointSupported() throws Exception { + try { + runPhase("isCheckpointSupported"); + return true; + } catch (AssertionError ignore) { + return false; + } + } } diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java index a7328acb73b5..6005cc375d5c 100644 --- a/tests/net/common/java/android/net/LinkPropertiesTest.java +++ b/tests/net/common/java/android/net/LinkPropertiesTest.java @@ -27,8 +27,8 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import android.net.LinkProperties.CompareResult; import android.net.LinkProperties.ProvisioningChange; +import android.net.util.LinkPropertiesUtils.CompareResult; import android.system.OsConstants; import android.util.ArraySet; diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java index daf187d01533..91c9a2a38036 100644 --- a/tests/net/java/android/net/MacAddressTest.java +++ b/tests/net/java/android/net/MacAddressTest.java @@ -22,6 +22,8 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import android.net.util.MacAddressUtils; + import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -122,11 +124,11 @@ public class MacAddressTest { for (MacAddress mac : multicastAddresses) { String msg = mac.toString() + " expected to be a multicast address"; - assertTrue(msg, mac.isMulticastAddress()); + assertTrue(msg, MacAddressUtils.isMulticastAddress(mac)); } for (MacAddress mac : unicastAddresses) { String msg = mac.toString() + " expected not to be a multicast address"; - assertFalse(msg, mac.isMulticastAddress()); + assertFalse(msg, MacAddressUtils.isMulticastAddress(mac)); } } @@ -156,7 +158,7 @@ public class MacAddressTest { public void testMacAddressConversions() { final int iterations = 10000; for (int i = 0; i < iterations; i++) { - MacAddress mac = MacAddress.createRandomUnicastAddress(); + MacAddress mac = MacAddressUtils.createRandomUnicastAddress(); String stringRepr = mac.toString(); byte[] bytesRepr = mac.toByteArray(); @@ -188,7 +190,7 @@ public class MacAddressTest { final String expectedLocalOui = "26:5f:78"; final MacAddress base = MacAddress.fromString(anotherOui + ":0:0:0"); for (int i = 0; i < iterations; i++) { - MacAddress mac = MacAddress.createRandomUnicastAddress(base, r); + MacAddress mac = MacAddressUtils.createRandomUnicastAddress(base, r); String stringRepr = mac.toString(); assertTrue(stringRepr + " expected to be a locally assigned address", @@ -199,7 +201,7 @@ public class MacAddressTest { } for (int i = 0; i < iterations; i++) { - MacAddress mac = MacAddress.createRandomUnicastAddress(); + MacAddress mac = MacAddressUtils.createRandomUnicastAddress(); String stringRepr = mac.toString(); assertTrue(stringRepr + " expected to be a locally assigned address", diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index ee2b57535b50..d4fd90338885 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -29,6 +29,7 @@ import android.net.NetworkSpecifier; import android.net.ProxyInfo; import android.net.StaticIpConfiguration; import android.net.Uri; +import android.net.util.MacAddressUtils; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -1166,7 +1167,7 @@ public class WifiConfiguration implements Parcelable { * @return true if mac is good to use */ public static boolean isValidMacAddressForRandomization(MacAddress mac) { - return mac != null && !mac.isMulticastAddress() && mac.isLocallyAssigned() + return mac != null && !MacAddressUtils.isMulticastAddress(mac) && mac.isLocallyAssigned() && !MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS).equals(mac); } diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java index 8689a38c6b17..0ef75aa3eb5a 100644 --- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java @@ -27,6 +27,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import android.net.MacAddress; +import android.net.util.MacAddressUtils; import android.net.wifi.WifiConfiguration.KeyMgmt; import android.net.wifi.WifiConfiguration.NetworkSelectionStatus; import android.os.Parcel; @@ -63,7 +64,7 @@ public class WifiConfigurationTest { config.updateIdentifier = "1234"; config.fromWifiNetworkSpecifier = true; config.fromWifiNetworkSuggestion = true; - config.setRandomizedMacAddress(MacAddress.createRandomUnicastAddress()); + config.setRandomizedMacAddress(MacAddressUtils.createRandomUnicastAddress()); MacAddress macBeforeParcel = config.getRandomizedMacAddress(); Parcel parcelW = Parcel.obtain(); config.writeToParcel(parcelW, 0); @@ -169,7 +170,7 @@ public class WifiConfigurationTest { MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS); assertEquals(defaultMac, config.getRandomizedMacAddress()); - MacAddress macToChangeInto = MacAddress.createRandomUnicastAddress(); + MacAddress macToChangeInto = MacAddressUtils.createRandomUnicastAddress(); config.setRandomizedMacAddress(macToChangeInto); MacAddress macAfterChange = config.getRandomizedMacAddress(); |