Merge "MidiService: check UID in getDeviceStatus()"
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 77b26f8..131d5e2 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -32,23 +32,12 @@
}
/////////////////////////////////////////////////////////////////////
-// Common metalava configs
-/////////////////////////////////////////////////////////////////////
-
-stubs_defaults {
- name: "metalava-non-updatable-api-stubs-default",
- defaults: ["android-non-updatable-stubs-defaults"],
- api_levels_annotations_enabled: false,
- defaults_visibility: ["//visibility:private"],
-}
-
-/////////////////////////////////////////////////////////////////////
// These modules provide source files for the stub libraries
/////////////////////////////////////////////////////////////////////
droidstubs {
name: "api-stubs-docs-non-updatable",
- defaults: ["metalava-non-updatable-api-stubs-default"],
+ defaults: ["android-non-updatable-stubs-defaults"],
args: metalava_framework_docs_args,
check_api: {
current: {
@@ -97,7 +86,7 @@
droidstubs {
name: "system-api-stubs-docs-non-updatable",
- defaults: ["metalava-non-updatable-api-stubs-default"],
+ defaults: ["android-non-updatable-stubs-defaults"],
args: metalava_framework_docs_args + priv_apps,
check_api: {
current: {
@@ -133,7 +122,7 @@
droidstubs {
name: "test-api-stubs-docs-non-updatable",
- defaults: ["metalava-non-updatable-api-stubs-default"],
+ defaults: ["android-non-updatable-stubs-defaults"],
args: metalava_framework_docs_args + test + priv_apps_in_stubs,
check_api: {
current: {
@@ -175,7 +164,7 @@
droidstubs {
name: "module-lib-api-stubs-docs-non-updatable",
- defaults: ["metalava-non-updatable-api-stubs-default"],
+ defaults: ["android-non-updatable-stubs-defaults"],
args: metalava_framework_docs_args + priv_apps_in_stubs + module_libs,
check_api: {
current: {
diff --git a/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java b/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java
index cf94e9e..4cd9741 100644
--- a/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java
+++ b/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java
@@ -25,7 +25,6 @@
import android.app.Instrumentation;
import android.content.Context;
-import android.graphics.Rect;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.view.inputmethod.EditorInfo;
@@ -190,7 +189,7 @@
final View view = new View(mContext);
final EditorInfo editorInfo = new EditorInfo();
while (state.keepRunning()) {
- mHandwritingInitiator.onInputConnectionCreated(view, editorInfo);
+ mHandwritingInitiator.onInputConnectionCreated(view);
state.pauseTiming();
mHandwritingInitiator.onInputConnectionClosed(view);
state.resumeTiming();
@@ -201,24 +200,14 @@
public void onInputConnectionClosed() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
final View view = new View(mContext);
- final EditorInfo editorInfo = new EditorInfo();
while (state.keepRunning()) {
state.pauseTiming();
- mHandwritingInitiator.onInputConnectionCreated(view, editorInfo);
+ mHandwritingInitiator.onInputConnectionCreated(view);
state.resumeTiming();
mHandwritingInitiator.onInputConnectionClosed(view);
}
}
- @Test
- public void updateEditorBoundary() {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- final Rect rect = new Rect(0, 0, 100, 100);
- while (state.keepRunning()) {
- mHandwritingInitiator.updateEditorBound(rect);
- }
- }
-
private MotionEvent createMotionEvent(int action, int toolType, int x, int y, long eventTime) {
MotionEvent.PointerProperties[] properties = MotionEvent.PointerProperties.createArray(1);
properties[0].toolType = toolType;
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index ee09952..0ad70e4 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -183,7 +183,7 @@
COMPRESS_TIME ? 1 * ONE_MINUTE : 12 * ONE_HOUR,
COMPRESS_TIME ? 4 * ONE_MINUTE : 24 * ONE_HOUR,
COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR,
- COMPRESS_TIME ? 32 * ONE_MINUTE : 45 * ONE_DAY
+ COMPRESS_TIME ? 32 * ONE_MINUTE : 3 * ONE_DAY
};
/** The minimum allowed values for each index in {@link #DEFAULT_ELAPSED_TIME_THRESHOLDS}. */
@@ -2540,7 +2540,7 @@
switch (name) {
case KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS:
mInjector.mAutoRestrictedBucketDelayMs = Math.max(
- COMPRESS_TIME ? ONE_MINUTE : 2 * ONE_HOUR,
+ COMPRESS_TIME ? ONE_MINUTE : 4 * ONE_HOUR,
properties.getLong(KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS,
DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS));
break;
diff --git a/core/api/current.txt b/core/api/current.txt
index 1b5e926..78d4b4d 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1135,6 +1135,7 @@
field public static final int popupWindowStyle = 16842870; // 0x1010076
field public static final int port = 16842793; // 0x1010029
field public static final int positiveButtonText = 16843253; // 0x10101f5
+ field public static final int preferKeepClear;
field public static final int preferMinimalPostProcessing = 16844300; // 0x101060c
field public static final int preferenceCategoryStyle = 16842892; // 0x101008c
field public static final int preferenceFragmentStyle = 16844038; // 0x1010506
@@ -7314,6 +7315,7 @@
method public int getMaximumFailedPasswordsForWipe(@Nullable android.content.ComponentName);
method public long getMaximumTimeToLock(@Nullable android.content.ComponentName);
method @NonNull public java.util.List<java.lang.String> getMeteredDataDisabledPackages(@NonNull android.content.ComponentName);
+ method public int getMinimumRequiredWifiSecurityLevel();
method @RequiresPermission(value=android.Manifest.permission.READ_NEARBY_STREAMING_POLICY, conditional=true) public int getNearbyAppStreamingPolicy();
method @RequiresPermission(value=android.Manifest.permission.READ_NEARBY_STREAMING_POLICY, conditional=true) public int getNearbyNotificationStreamingPolicy();
method @Deprecated @ColorInt public int getOrganizationColor(@NonNull android.content.ComponentName);
@@ -7354,6 +7356,7 @@
method @NonNull public java.util.List<java.lang.String> getUserControlDisabledPackages(@NonNull android.content.ComponentName);
method @NonNull public android.os.Bundle getUserRestrictions(@NonNull android.content.ComponentName);
method @Nullable public String getWifiMacAddress(@NonNull android.content.ComponentName);
+ method @Nullable public android.app.admin.WifiSsidPolicy getWifiSsidPolicy();
method public boolean grantKeyPairToApp(@Nullable android.content.ComponentName, @NonNull String, @NonNull String);
method public boolean grantKeyPairToWifiAuth(@NonNull String);
method public boolean hasCaCertInstalled(@Nullable android.content.ComponentName, byte[]);
@@ -7458,6 +7461,7 @@
method public void setMaximumFailedPasswordsForWipe(@NonNull android.content.ComponentName, int);
method public void setMaximumTimeToLock(@NonNull android.content.ComponentName, long);
method @NonNull public java.util.List<java.lang.String> setMeteredDataDisabledPackages(@NonNull android.content.ComponentName, @NonNull java.util.List<java.lang.String>);
+ method public void setMinimumRequiredWifiSecurityLevel(int);
method public void setNearbyAppStreamingPolicy(int);
method public void setNearbyNotificationStreamingPolicy(int);
method public void setNetworkLoggingEnabled(@Nullable android.content.ComponentName, boolean);
@@ -7506,6 +7510,7 @@
method public void setUsbDataSignalingEnabled(boolean);
method public void setUserControlDisabledPackages(@NonNull android.content.ComponentName, @NonNull java.util.List<java.lang.String>);
method public void setUserIcon(@NonNull android.content.ComponentName, android.graphics.Bitmap);
+ method public void setWifiSsidPolicy(@Nullable android.app.admin.WifiSsidPolicy);
method public int startUserInBackground(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
method public int stopUser(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
method public boolean switchUser(@NonNull android.content.ComponentName, @Nullable android.os.UserHandle);
@@ -7676,6 +7681,10 @@
field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
+ field public static final int WIFI_SECURITY_ENTERPRISE_192 = 3; // 0x3
+ field public static final int WIFI_SECURITY_ENTERPRISE_EAP = 2; // 0x2
+ field public static final int WIFI_SECURITY_OPEN = 0; // 0x0
+ field public static final int WIFI_SECURITY_PERSONAL = 1; // 0x1
field public static final int WIPE_EUICC = 4; // 0x4
field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
@@ -7863,6 +7872,18 @@
field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.UnsafeStateException> CREATOR;
}
+ public final class WifiSsidPolicy implements android.os.Parcelable {
+ method @NonNull public static android.app.admin.WifiSsidPolicy createAllowlistPolicy(@NonNull java.util.Set<java.lang.String>);
+ method @NonNull public static android.app.admin.WifiSsidPolicy createDenylistPolicy(@NonNull java.util.Set<java.lang.String>);
+ method public int describeContents();
+ method public int getPolicyType();
+ method @NonNull public java.util.Set<java.lang.String> getSsids();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.WifiSsidPolicy> CREATOR;
+ field public static final int WIFI_SSID_POLICY_TYPE_ALLOWLIST = 0; // 0x0
+ field public static final int WIFI_SSID_POLICY_TYPE_DENYLIST = 1; // 0x1
+ }
+
}
package android.app.assist {
@@ -13190,6 +13211,7 @@
field public static final String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct";
field public static final String FEATURE_WIFI_PASSPOINT = "android.hardware.wifi.passpoint";
field public static final String FEATURE_WIFI_RTT = "android.hardware.wifi.rtt";
+ field public static final String FEATURE_WINDOW_MAGNIFICATION = "android.software.window_magnification";
field public static final int FLAG_PERMISSION_WHITELIST_INSTALLER = 2; // 0x2
field public static final int FLAG_PERMISSION_WHITELIST_SYSTEM = 1; // 0x1
field public static final int FLAG_PERMISSION_WHITELIST_UPGRADE = 4; // 0x4
@@ -41816,11 +41838,11 @@
field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
field public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool";
field public static final String KEY_CARRIER_USSD_METHOD_INT = "carrier_ussd_method_int";
- field public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL = "carrier_ut_provisioning_required_bool";
+ field @Deprecated public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL = "carrier_ut_provisioning_required_bool";
field public static final String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool";
field public static final String KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL = "carrier_volte_override_wfc_provisioning_bool";
- field public static final String KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
- field public static final String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL = "carrier_volte_provisioning_required_bool";
+ field @Deprecated public static final String KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
+ field @Deprecated public static final String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL = "carrier_volte_provisioning_required_bool";
field public static final String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL = "carrier_volte_tty_supported_bool";
field public static final String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
field @Deprecated public static final String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
@@ -42066,6 +42088,13 @@
field public static final int IPSEC_ENCRYPTION_ALGORITHM_AES_CBC = 2; // 0x2
field public static final int IPSEC_ENCRYPTION_ALGORITHM_DES_EDE3_CBC = 1; // 0x1
field public static final int IPSEC_ENCRYPTION_ALGORITHM_NULL = 0; // 0x0
+ field public static final String KEY_CAPABILITY_CALL_COMPOSER_INT_ARRAY = "ims.key_capability_type_call_composer_int_array";
+ field public static final String KEY_CAPABILITY_TYPE_OPTIONS_UCE_INT_ARRAY = "ims.key_capability_type_options_uce_int_array";
+ field public static final String KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY = "ims.key_capability_type_presence_uce_int_array";
+ field public static final String KEY_CAPABILITY_TYPE_SMS_INT_ARRAY = "ims.key_capability_type_sms_int_array";
+ field public static final String KEY_CAPABILITY_TYPE_UT_INT_ARRAY = "ims.key_capability_type_ut_int_array";
+ field public static final String KEY_CAPABILITY_TYPE_VIDEO_INT_ARRAY = "ims.key_capability_type_video_int_array";
+ field public static final String KEY_CAPABILITY_TYPE_VOICE_INT_ARRAY = "ims.key_capability_type_voice_int_array";
field public static final String KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL = "ims.enable_presence_capability_exchange_bool";
field public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL = "ims.enable_presence_group_subscribe_bool";
field public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL = "ims.enable_presence_publish_bool";
@@ -42080,11 +42109,13 @@
field public static final String KEY_IPV4_SIP_MTU_SIZE_CELLULAR_INT = "ims.ipv4_sip_mtu_size_cellular_int";
field public static final String KEY_IPV6_SIP_MTU_SIZE_CELLULAR_INT = "ims.ipv6_sip_mtu_size_cellular_int";
field public static final String KEY_KEEP_PDN_UP_IN_NO_VOPS_BOOL = "ims.keep_pdn_up_in_no_vops_bool";
+ field public static final String KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE = "ims.mmtel_requires_provisioning_bundle";
field public static final String KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT = "ims.non_rcs_capabilities_cache_expiration_sec_int";
field public static final String KEY_PHONE_CONTEXT_DOMAIN_NAME_STRING = "ims.phone_context_domain_name_string";
field public static final String KEY_PREFIX = "ims.";
field public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL = "ims.rcs_bulk_capability_exchange_bool";
field public static final String KEY_RCS_FEATURE_TAG_ALLOWED_STRING_ARRAY = "ims.rcs_feature_tag_allowed_string_array";
+ field public static final String KEY_RCS_REQUIRES_PROVISIONING_BUNDLE = "ims.rcs_requires_provisioning_bundle";
field public static final String KEY_REGISTRATION_EVENT_PACKAGE_SUPPORTED_BOOL = "ims.registration_event_package_supported_bool";
field public static final String KEY_REGISTRATION_EXPIRY_TIMER_SEC_INT = "ims.registration_expiry_timer_sec_int";
field public static final String KEY_REGISTRATION_RETRY_BASE_TIMER_MILLIS_INT = "ims.registration_retry_base_timer_millis_int";
@@ -42347,6 +42378,7 @@
field public static final String KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY = "iwlan.supported_ike_session_encryption_algorithms_int_array";
field public static final String KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY = "iwlan.supported_integrity_algorithms_int_array";
field public static final String KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY = "iwlan.supported_prf_algorithms_int_array";
+ field public static final String KEY_SUPPORTS_EAP_AKA_FAST_REAUTH_BOOL = "iwlan.supports_eap_aka_fast_reauth_bool";
}
public abstract class CellIdentity implements android.os.Parcelable {
@@ -44732,6 +44764,7 @@
public class ImsManager {
method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int);
method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int);
+ method @NonNull public android.telephony.ims.ProvisioningManager getProvisioningManager(int);
field public static final String ACTION_WFC_IMS_REGISTRATION_ERROR = "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR";
field public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
field public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE";
@@ -44982,6 +45015,23 @@
field public static final int REASON_UNKNOWN_TEMPORARY_ERROR = 1; // 0x1
}
+ public class ProvisioningManager {
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) @WorkerThread public boolean getProvisioningStatusForCapability(int, int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) @WorkerThread public boolean getRcsProvisioningStatusForCapability(int, int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public boolean isProvisioningRequiredForCapability(int, int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public boolean isRcsProvisioningRequiredForCapability(int, int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void registerFeatureProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.FeatureProvisioningCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(int, int, boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, int, boolean);
+ method public void unregisterFeatureProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.FeatureProvisioningCallback);
+ }
+
+ public static class ProvisioningManager.FeatureProvisioningCallback {
+ ctor public ProvisioningManager.FeatureProvisioningCallback();
+ method public void onFeatureProvisioningChanged(int, int, boolean);
+ method public void onRcsFeatureProvisioningChanged(int, int, boolean);
+ }
+
public class RcsUceAdapter {
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException;
}
@@ -45022,6 +45072,27 @@
field public static final int CAPABILITY_TYPE_VOICE = 1; // 0x1
}
+ public class RcsFeature {
+ }
+
+ public static class RcsFeature.RcsImsCapabilities {
+ field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
+ field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
+ field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
+ }
+
+}
+
+package android.telephony.ims.stub {
+
+ public class ImsRegistrationImplBase {
+ field public static final int REGISTRATION_TECH_CROSS_SIM = 2; // 0x2
+ field public static final int REGISTRATION_TECH_IWLAN = 1; // 0x1
+ field public static final int REGISTRATION_TECH_LTE = 0; // 0x0
+ field public static final int REGISTRATION_TECH_NONE = -1; // 0xffffffff
+ field public static final int REGISTRATION_TECH_NR = 3; // 0x3
+ }
+
}
package android.telephony.mbms {
@@ -50220,6 +50291,7 @@
method public float getPivotX();
method public float getPivotY();
method public android.view.PointerIcon getPointerIcon();
+ method @NonNull public final java.util.List<android.graphics.Rect> getPreferKeepClearRects();
method @Nullable public String[] getReceiveContentMimeTypes();
method public android.content.res.Resources getResources();
method public final boolean getRevealOnFocusHint();
@@ -50337,6 +50409,7 @@
method protected boolean isPaddingOffsetRequired();
method public boolean isPaddingRelative();
method public boolean isPivotSet();
+ method public final boolean isPreferKeepClear();
method public boolean isPressed();
method public boolean isSaveEnabled();
method public boolean isSaveFromParentEnabled();
@@ -50579,6 +50652,8 @@
method public void setPivotX(float);
method public void setPivotY(float);
method public void setPointerIcon(android.view.PointerIcon);
+ method public final void setPreferKeepClear(boolean);
+ method public final void setPreferKeepClearRects(@NonNull java.util.List<android.graphics.Rect>);
method public void setPressed(boolean);
method public void setRenderEffect(@Nullable android.graphics.RenderEffect);
method public final void setRevealOnFocusHint(boolean);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index b082629..2a4e7a6 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -147,10 +147,12 @@
field public static final int USB_DATA_TRANSFER_RATE_LOW_SPEED = 2; // 0x2
field public static final int USB_DATA_TRANSFER_RATE_UNKNOWN = -1; // 0xffffffff
field public static final int USB_HAL_NOT_SUPPORTED = -1; // 0xffffffff
+ field public static final int USB_HAL_RETRY = -2; // 0xfffffffe
field public static final int USB_HAL_V1_0 = 10; // 0xa
field public static final int USB_HAL_V1_1 = 11; // 0xb
field public static final int USB_HAL_V1_2 = 12; // 0xc
field public static final int USB_HAL_V1_3 = 13; // 0xd
+ field public static final int USB_HAL_V2_0 = 20; // 0x14
}
}
@@ -264,6 +266,32 @@
method public int getResourceId();
}
+ public class NetworkIdentity {
+ method public int getOemManaged();
+ method public int getRatType();
+ method @Nullable public String getSubscriberId();
+ method public int getType();
+ method @Nullable public String getWifiNetworkKey();
+ method public boolean isDefaultNetwork();
+ method public boolean isMetered();
+ method public boolean isRoaming();
+ }
+
+ public static final class NetworkIdentity.Builder {
+ ctor public NetworkIdentity.Builder();
+ method @NonNull public android.net.NetworkIdentity build();
+ method @NonNull public android.net.NetworkIdentity.Builder clearRatType();
+ method @NonNull public android.net.NetworkIdentity.Builder setDefaultNetwork(boolean);
+ method @NonNull public android.net.NetworkIdentity.Builder setMetered(boolean);
+ method @NonNull public android.net.NetworkIdentity.Builder setNetworkStateSnapshot(@NonNull android.net.NetworkStateSnapshot);
+ method @NonNull public android.net.NetworkIdentity.Builder setOemManaged(int);
+ method @NonNull public android.net.NetworkIdentity.Builder setRatType(int);
+ method @NonNull public android.net.NetworkIdentity.Builder setRoaming(boolean);
+ method @NonNull public android.net.NetworkIdentity.Builder setSubscriberId(@Nullable String);
+ method @NonNull public android.net.NetworkIdentity.Builder setType(int);
+ method @NonNull public android.net.NetworkIdentity.Builder setWifiNetworkKey(@Nullable String);
+ }
+
public class NetworkPolicyManager {
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public int getMultipathPreference(@NonNull android.net.Network);
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public int getRestrictBackgroundStatus(int);
@@ -291,6 +319,30 @@
field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStateSnapshot> CREATOR;
}
+ public final class NetworkStatsHistory implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<android.net.NetworkStatsHistory.Entry> getEntries();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStatsHistory> CREATOR;
+ }
+
+ public static final class NetworkStatsHistory.Builder {
+ ctor public NetworkStatsHistory.Builder(long, int);
+ method @NonNull public android.net.NetworkStatsHistory.Builder addEntry(@NonNull android.net.NetworkStatsHistory.Entry);
+ method @NonNull public android.net.NetworkStatsHistory build();
+ }
+
+ public static final class NetworkStatsHistory.Entry {
+ ctor public NetworkStatsHistory.Entry(long, long, long, long, long, long, long);
+ method public long getActiveTime();
+ method public long getBucketStart();
+ method public long getOperations();
+ method public long getRxBytes();
+ method public long getRxPackets();
+ method public long getTxBytes();
+ method public long getTxPackets();
+ }
+
public final class NetworkTemplate implements android.os.Parcelable {
method public int describeContents();
method public int getDefaultNetworkStatus();
@@ -346,6 +398,10 @@
method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo);
}
+ public class TrafficStats {
+ method public static void init(@NonNull android.content.Context);
+ }
+
public final class UnderlyingNetworkInfo implements android.os.Parcelable {
ctor public UnderlyingNetworkInfo(int, @NonNull String, @NonNull java.util.List<java.lang.String>);
method public int describeContents();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index f554e1f..34a8a8b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2,6 +2,7 @@
package android {
public static final class Manifest.permission {
+ field public static final String ACCESS_AMBIENT_CONTEXT_EVENT = "android.permission.ACCESS_AMBIENT_CONTEXT_EVENT";
field public static final String ACCESS_AMBIENT_LIGHT_STATS = "android.permission.ACCESS_AMBIENT_LIGHT_STATS";
field public static final String ACCESS_BROADCAST_RADIO = "android.permission.ACCESS_BROADCAST_RADIO";
field public static final String ACCESS_CACHE_FILESYSTEM = "android.permission.ACCESS_CACHE_FILESYSTEM";
@@ -38,6 +39,7 @@
field public static final String BACKGROUND_CAMERA = "android.permission.BACKGROUND_CAMERA";
field public static final String BACKUP = "android.permission.BACKUP";
field public static final String BATTERY_PREDICTION = "android.permission.BATTERY_PREDICTION";
+ field public static final String BIND_AMBIENT_CONTEXT_DETECTION_SERVICE = "android.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE";
field public static final String BIND_ATTENTION_SERVICE = "android.permission.BIND_ATTENTION_SERVICE";
field public static final String BIND_AUGMENTED_AUTOFILL_SERVICE = "android.permission.BIND_AUGMENTED_AUTOFILL_SERVICE";
field public static final String BIND_CALL_DIAGNOSTIC_SERVICE = "android.permission.BIND_CALL_DIAGNOSTIC_SERVICE";
@@ -160,6 +162,7 @@
field public static final String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING";
field public static final String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
+ field public static final String MANAGE_GAME_MODE = "android.permission.MANAGE_GAME_MODE";
field public static final String MANAGE_HOTWORD_DETECTION = "android.permission.MANAGE_HOTWORD_DETECTION";
field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
field public static final String MANAGE_MUSIC_RECOGNITION = "android.permission.MANAGE_MUSIC_RECOGNITION";
@@ -756,7 +759,17 @@
}
public final class GameManager {
- method @RequiresPermission("android.permission.MANAGE_GAME_MODE") public void setGameMode(@NonNull String, int);
+ method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE) public android.app.GameModeInfo getGameModeInfo(@NonNull String);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE) public void setGameMode(@NonNull String, int);
+ }
+
+ public final class GameModeInfo implements android.os.Parcelable {
+ ctor public GameModeInfo(int, @NonNull int[]);
+ method public int describeContents();
+ method public int getActiveGameMode();
+ method @NonNull public int[] getAvailableGameModes();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.GameModeInfo> CREATOR;
}
public abstract class InstantAppResolverService extends android.app.Service {
@@ -1130,7 +1143,36 @@
}
public static final class DevicePolicyResources.Strings {
- field public static final String INVALID_ID = "INVALID_ID";
+ field public static final String UNDEFINED = "UNDEFINED";
+ }
+
+ public static final class DevicePolicyResources.Strings.DocumentsUi {
+ field public static final String CANT_SAVE_TO_PERSONAL_MESSAGE = "DocumentsUi.CANT_SAVE_TO_PERSONAL_MESSAGE";
+ field public static final String CANT_SAVE_TO_PERSONAL_TITLE = "DocumentsUi.CANT_SAVE_TO_PERSONAL_TITLE";
+ field public static final String CANT_SAVE_TO_WORK_MESSAGE = "DocumentsUi.CANT_SAVE_TO_WORK_MESSAGE";
+ field public static final String CANT_SAVE_TO_WORK_TITLE = "DocumentsUi.CANT_SAVE_TO_WORK_TITLE";
+ field public static final String CANT_SELECT_PERSONAL_FILES_MESSAGE = "DocumentsUi.CANT_SELECT_PERSONAL_FILES_MESSAGE";
+ field public static final String CANT_SELECT_PERSONAL_FILES_TITLE = "DocumentsUi.CANT_SELECT_PERSONAL_FILES_TITLE";
+ field public static final String CANT_SELECT_WORK_FILES_MESSAGE = "DocumentsUi.CANT_SELECT_WORK_FILES_MESSAGE";
+ field public static final String CANT_SELECT_WORK_FILES_TITLE = "DocumentsUi.CANT_SELECT_WORK_FILES_TITLE";
+ field public static final String CROSS_PROFILE_NOT_ALLOWED_MESSAGE = "DocumentsUi.CROSS_PROFILE_NOT_ALLOWED_MESSAGE";
+ field public static final String CROSS_PROFILE_NOT_ALLOWED_TITLE = "DocumentsUi.CROSS_PROFILE_NOT_ALLOWED_TITLE";
+ field public static final String PERSONAL_TAB = "DocumentsUi.PERSONAL_TAB";
+ field public static final String PREVIEW_WORK_FILE_ACCESSIBILITY = "DocumentsUi.PREVIEW_WORK_FILE_ACCESSIBILITY";
+ field public static final String WORK_ACCESSIBILITY = "DocumentsUi.WORK_ACCESSIBILITY";
+ field public static final String WORK_PROFILE_OFF_ENABLE_BUTTON = "DocumentsUi.WORK_PROFILE_OFF_ENABLE_BUTTON";
+ field public static final String WORK_PROFILE_OFF_ERROR_TITLE = "DocumentsUi.WORK_PROFILE_OFF_ERROR_TITLE";
+ field public static final String WORK_TAB = "DocumentsUi.WORK_TAB";
+ }
+
+ public static final class DevicePolicyResources.Strings.MediaProvider {
+ field public static final String BLOCKED_BY_ADMIN_TITLE = "MediaProvider.BLOCKED_BY_ADMIN_TITLE";
+ field public static final String BLOCKED_FROM_PERSONAL_MESSAGE = "MediaProvider.BLOCKED_FROM_PERSONAL_MESSAGE";
+ field public static final String BLOCKED_FROM_WORK_MESSAGE = "MediaProvider.BLOCKED_FROM_WORK_MESSAGE";
+ field public static final String SWITCH_TO_PERSONAL_MESSAGE = "MediaProvider.SWITCH_TO_PERSONAL_MESSAGE";
+ field public static final String SWITCH_TO_WORK_MESSAGE = "MediaProvider.SWITCH_TO_WORK_MESSAGE";
+ field public static final String WORK_PROFILE_PAUSED_MESSAGE = "MediaProvider.WORK_PROFILE_PAUSED_MESSAGE";
+ field public static final String WORK_PROFILE_PAUSED_TITLE = "MediaProvider.WORK_PROFILE_PAUSED_TITLE";
}
public final class DevicePolicyStringResource implements android.os.Parcelable {
@@ -1213,6 +1255,87 @@
}
+package android.app.ambientcontext {
+
+ public final class AmbientContextEvent implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getConfidenceLevel();
+ method public int getDensityLevel();
+ method @NonNull public java.time.Instant getEndTime();
+ method public int getEventType();
+ method @NonNull public java.time.Instant getStartTime();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.ambientcontext.AmbientContextEvent> CREATOR;
+ field public static final int EVENT_COUGH = 1; // 0x1
+ field public static final int EVENT_SNORE = 2; // 0x2
+ field public static final int EVENT_UNKNOWN = 0; // 0x0
+ field public static final int LEVEL_HIGH = 5; // 0x5
+ field public static final int LEVEL_LOW = 1; // 0x1
+ field public static final int LEVEL_MEDIUM = 3; // 0x3
+ field public static final int LEVEL_MEDIUM_HIGH = 4; // 0x4
+ field public static final int LEVEL_MEDIUM_LOW = 2; // 0x2
+ field public static final int LEVEL_UNKNOWN = 0; // 0x0
+ }
+
+ public static final class AmbientContextEvent.Builder {
+ ctor public AmbientContextEvent.Builder();
+ method @NonNull public android.app.ambientcontext.AmbientContextEvent build();
+ method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setConfidenceLevel(int);
+ method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setDensityLevel(int);
+ method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setEndTime(@NonNull java.time.Instant);
+ method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setEventType(int);
+ method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setStartTime(@NonNull java.time.Instant);
+ }
+
+ public final class AmbientContextEventRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.Set<java.lang.Integer> getEventTypes();
+ method @NonNull public android.os.PersistableBundle getOptions();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.ambientcontext.AmbientContextEventRequest> CREATOR;
+ }
+
+ public static final class AmbientContextEventRequest.Builder {
+ ctor public AmbientContextEventRequest.Builder();
+ method @NonNull public android.app.ambientcontext.AmbientContextEventRequest.Builder addEventType(int);
+ method @NonNull public android.app.ambientcontext.AmbientContextEventRequest build();
+ method @NonNull public android.app.ambientcontext.AmbientContextEventRequest.Builder setOptions(@NonNull android.os.PersistableBundle);
+ }
+
+ public final class AmbientContextEventResponse implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.app.PendingIntent getActionPendingIntent();
+ method @NonNull public java.util.List<android.app.ambientcontext.AmbientContextEvent> getEvents();
+ method @NonNull public String getPackageName();
+ method public int getStatusCode();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.ambientcontext.AmbientContextEventResponse> CREATOR;
+ field public static final int STATUS_ACCESS_DENIED = 5; // 0x5
+ field public static final int STATUS_MICROPHONE_DISABLED = 4; // 0x4
+ field public static final int STATUS_NOT_SUPPORTED = 2; // 0x2
+ field public static final int STATUS_SERVICE_UNAVAILABLE = 3; // 0x3
+ field public static final int STATUS_SUCCESS = 1; // 0x1
+ field public static final int STATUS_UNKNOWN = 0; // 0x0
+ }
+
+ public static final class AmbientContextEventResponse.Builder {
+ ctor public AmbientContextEventResponse.Builder();
+ method @NonNull public android.app.ambientcontext.AmbientContextEventResponse.Builder addEvent(@NonNull android.app.ambientcontext.AmbientContextEvent);
+ method @NonNull public android.app.ambientcontext.AmbientContextEventResponse build();
+ method @NonNull public android.app.ambientcontext.AmbientContextEventResponse.Builder setActionPendingIntent(@NonNull android.app.PendingIntent);
+ method @NonNull public android.app.ambientcontext.AmbientContextEventResponse.Builder setPackageName(@NonNull String);
+ method @NonNull public android.app.ambientcontext.AmbientContextEventResponse.Builder setStatusCode(int);
+ }
+
+ public final class AmbientContextManager {
+ method @Nullable public static android.app.ambientcontext.AmbientContextEventResponse getResponseFromIntent(@NonNull android.content.Intent);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT) public void registerObserver(@NonNull android.app.ambientcontext.AmbientContextEventRequest, @NonNull android.app.PendingIntent);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT) public void unregisterObserver();
+ field public static final String EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE = "android.app.ambientcontext.extra.AMBIENT_CONTEXT_EVENT_RESPONSE";
+ }
+
+}
+
package android.app.assist {
public class ActivityId {
@@ -2353,6 +2476,15 @@
field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
}
+ public final class BluetoothPbapClient implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+ method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
+ method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+ field @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED";
+ }
+
public interface BluetoothProfile {
field public static final int A2DP_SINK = 11; // 0xb
field public static final int AVRCP_CONTROLLER = 12; // 0xc
@@ -2694,6 +2826,7 @@
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) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
+ field public static final String AMBIENT_CONTEXT_SERVICE = "ambient_context";
field public static final String APP_HIBERNATION_SERVICE = "app_hibernation";
field public static final String APP_INTEGRITY_SERVICE = "app_integrity";
field public static final String APP_PREDICTION_SERVICE = "app_prediction";
@@ -5086,8 +5219,20 @@
}
public final class UsbPort {
+ method @CheckResult @RequiresPermission(android.Manifest.permission.MANAGE_USB) public int enableLimitPowerTransfer(boolean);
+ method @CheckResult @RequiresPermission(android.Manifest.permission.MANAGE_USB) public int enableUsbData(boolean);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USB) public android.hardware.usb.UsbPortStatus getStatus();
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setRoles(int, int);
+ field public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_INTERNAL = 1; // 0x1
+ field public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_NOT_SUPPORTED = 2; // 0x2
+ field public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_OTHER = 4; // 0x4
+ field public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_PORT_MISMATCH = 3; // 0x3
+ field public static final int ENABLE_LIMIT_POWER_TRANSFER_SUCCESS = 0; // 0x0
+ field public static final int ENABLE_USB_DATA_ERROR_INTERNAL = 1; // 0x1
+ field public static final int ENABLE_USB_DATA_ERROR_NOT_SUPPORTED = 2; // 0x2
+ field public static final int ENABLE_USB_DATA_ERROR_OTHER = 4; // 0x4
+ field public static final int ENABLE_USB_DATA_ERROR_PORT_MISMATCH = 3; // 0x3
+ field public static final int ENABLE_USB_DATA_SUCCESS = 0; // 0x0
}
public final class UsbPortStatus implements android.os.Parcelable {
@@ -5097,6 +5242,7 @@
method public int getCurrentPowerRole();
method public int getSupportedRoleCombinations();
method public boolean isConnected();
+ method public boolean isPowerTransferLimited();
method public boolean isRoleCombinationSupported(int, int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.usb.UsbPortStatus> CREATOR;
@@ -7067,7 +7213,7 @@
}
public abstract class SectionSettings extends android.media.tv.tuner.filter.Settings {
- method public int getBitWidthOfLengthField();
+ method public int getLengthFieldBitWidth();
method public boolean isCrcEnabled();
method public boolean isRaw();
method public boolean isRepeat();
@@ -8702,6 +8848,7 @@
method @Nullable public android.net.wifi.nl80211.DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String);
method @NonNull public java.util.List<android.net.wifi.nl80211.NativeScanResult> getScanResults(@NonNull String, int);
method @Nullable public android.net.wifi.nl80211.WifiNl80211Manager.TxPacketCounters getTxPacketCounters(@NonNull String);
+ method public boolean notifyCountryCodeChanged();
method @Nullable public static android.net.wifi.nl80211.WifiNl80211Manager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]);
method @Deprecated public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.SoftApCallback);
method public boolean registerCountryCodeChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.CountryCodeChangedListener);
@@ -9940,6 +10087,7 @@
method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperty(@NonNull String, @NonNull String, @Nullable String, boolean);
field public static final String NAMESPACE_ACTIVITY_MANAGER = "activity_manager";
field public static final String NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT = "activity_manager_native_boot";
+ field public static final String NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE = "ambient_context_manager_service";
field public static final String NAMESPACE_APPSEARCH = "appsearch";
field public static final String NAMESPACE_APP_COMPAT = "app_compat";
field public static final String NAMESPACE_APP_HIBERNATION = "app_hibernation";
@@ -10211,6 +10359,7 @@
field public static final String AUTO_REVOKE_DISABLED = "auto_revoke_disabled";
field public static final String COMPLETED_CATEGORY_PREFIX = "suggested.completed_category.";
field public static final String DOZE_ALWAYS_ON = "doze_always_on";
+ field public static final String FAST_PAIR_SCAN_ENABLED = "fast_pair_scan_enabled";
field public static final String HUSH_GESTURE_USED = "hush_gesture_used";
field public static final String INSTANT_APPS_ENABLED = "instant_apps_enabled";
field public static final String LAST_SETUP_SHOWN = "last_setup_shown";
@@ -10511,6 +10660,18 @@
}
+package android.service.ambientcontext {
+
+ public abstract class AmbientContextDetectionService extends android.app.Service {
+ ctor public AmbientContextDetectionService();
+ method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method public abstract void onStartDetection(@NonNull android.app.ambientcontext.AmbientContextEventRequest, @NonNull String, @NonNull java.util.function.Consumer<android.app.ambientcontext.AmbientContextEventResponse>);
+ method public abstract void onStopDetection(@NonNull String);
+ field public static final String SERVICE_INTERFACE = "android.service.ambientcontext.AmbientContextDetectionService";
+ }
+
+}
+
package android.service.appprediction {
public abstract class AppPredictionService extends android.app.Service {
@@ -10962,6 +11123,7 @@
ctor public GameSession();
method public void onCreate();
method public void onDestroy();
+ method public void onGameTaskFocusChanged(boolean);
method public void setTaskOverlayView(@NonNull android.view.View, @NonNull android.view.ViewGroup.LayoutParams);
method public void takeScreenshot(@NonNull java.util.concurrent.Executor, @NonNull android.service.games.GameSession.ScreenshotCallback);
}
@@ -14394,18 +14556,16 @@
public class ProvisioningManager {
method @NonNull public static android.telephony.ims.ProvisioningManager createForSubscriptionId(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public int getProvisioningIntValue(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getProvisioningStatusForCapability(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 @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getRcsProvisioningStatusForCapability(int);
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public boolean isRcsVolteSingleRegistrationCapable() throws android.telephony.ims.ImsException;
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(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public void registerRcsProvisioningCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.RcsProvisioningCallback) 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(int, int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void setRcsClientConfiguration(@NonNull android.telephony.ims.RcsClientConfiguration) throws android.telephony.ims.ImsException;
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void triggerRcsReconfiguration();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public void unregisterRcsProvisioningCallback(@NonNull android.telephony.ims.ProvisioningManager.RcsProvisioningCallback);
@@ -14838,9 +14998,6 @@
method public void addCapabilities(int);
method public boolean isCapable(int);
method public void removeCapabilities(int);
- field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
- field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
- field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
}
}
@@ -14990,11 +15147,6 @@
method public void triggerFullNetworkRegistration(@IntRange(from=100, to=699) int, @Nullable String);
method public void triggerSipDelegateDeregistration();
method public void updateSipDelegateRegistration();
- field public static final int REGISTRATION_TECH_CROSS_SIM = 2; // 0x2
- field public static final int REGISTRATION_TECH_IWLAN = 1; // 0x1
- field public static final int REGISTRATION_TECH_LTE = 0; // 0x0
- field public static final int REGISTRATION_TECH_NONE = -1; // 0xffffffff
- field public static final int REGISTRATION_TECH_NR = 3; // 0x3
}
public class ImsSmsImplBase {
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index 9a06423..3d756ba 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -91,6 +91,10 @@
+NoSettingsProvider: android.provider.Settings.Secure#FAST_PAIR_SCAN_ENABLED:
+ New setting keys are not allowed (Field: FAST_PAIR_SCAN_ENABLED); use getters/setters in relevant manager class
+
+
OnNameExpected: android.service.smartspace.SmartspaceService#notifySmartspaceEvent(android.app.smartspace.SmartspaceSessionId, android.app.smartspace.SmartspaceTargetEvent):
Methods implemented by developers should follow the on<Something> style, was `notifySmartspaceEvent`
diff --git a/core/java/android/accounts/CantAddAccountActivity.java b/core/java/android/accounts/CantAddAccountActivity.java
index f7f232e..107efc3 100644
--- a/core/java/android/accounts/CantAddAccountActivity.java
+++ b/core/java/android/accounts/CantAddAccountActivity.java
@@ -16,9 +16,13 @@
package android.accounts;
+import static android.app.admin.DevicePolicyResources.Strings.Core.CANT_ADD_ACCOUNT_MESSAGE;
+
import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
import android.os.Bundle;
import android.view.View;
+import android.widget.TextView;
import com.android.internal.R;
@@ -33,6 +37,12 @@
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.app_not_authorized);
+
+ TextView view = findViewById(R.id.description);
+ String text = getSystemService(DevicePolicyManager.class).getString(
+ CANT_ADD_ACCOUNT_MESSAGE,
+ () -> getString(R.string.error_message_change_not_allowed));
+ view.setText(text);
}
public void onCancelButtonClicked(View view) {
diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
index 2e9f73c..0d82ac9 100644
--- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java
+++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
@@ -15,7 +15,10 @@
*/
package android.accounts;
+import static android.app.admin.DevicePolicyResources.Strings.Core.CANT_ADD_ACCOUNT_MESSAGE;
+
import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
import android.content.Intent;
import android.os.Bundle;
import android.os.Parcelable;
@@ -199,7 +202,14 @@
if (mPossiblyVisibleAccounts.isEmpty() && mDisallowAddAccounts) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
+
setContentView(R.layout.app_not_authorized);
+ TextView view = findViewById(R.id.description);
+ String text = getSystemService(DevicePolicyManager.class).getString(
+ CANT_ADD_ACCOUNT_MESSAGE,
+ () -> getString(R.string.error_message_change_not_allowed));
+ view.setText(text);
+
mDontShowPicker = true;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 686ca3b..3ddbe9e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -105,6 +105,7 @@
import android.media.MediaServiceManager;
import android.net.ConnectivityManager;
import android.net.Proxy;
+import android.net.TrafficStats;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
@@ -6725,6 +6726,13 @@
NetworkSecurityConfigProvider.install(appContext);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ // For backward compatibility, TrafficStats needs static access to the application context.
+ // But for isolated apps which cannot access network related services, service discovery
+ // is restricted. Hence, calling this would result in NPE.
+ if (!Process.isIsolated()) {
+ TrafficStats.init(appContext);
+ }
+
// Continue loading instrumentation.
if (ii != null) {
initInstrumentation(ii, data, appContext);
diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java
index 29e1b70..76471d3 100644
--- a/core/java/android/app/GameManager.java
+++ b/core/java/android/app/GameManager.java
@@ -119,6 +119,31 @@
}
/**
+ * Returns the {@link GameModeInfo} associated with the game associated with
+ * the given {@code packageName}. If the given package is not a game, {@code null} is
+ * always returned.
+ * <p>
+ * An application can use <code>android:isGame="true"</code> or
+ * <code>android:appCategory="game"</code> to indicate that the application is a game.
+ * If the manifest doesn't define a category, the category can also be
+ * provided by the installer via
+ * {@link android.content.pm.PackageManager#setApplicationCategoryHint(String, int)}.
+ * <p>
+ *
+ * @hide
+ */
+ @SystemApi
+ @UserHandleAware
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+ public @Nullable GameModeInfo getGameModeInfo(@NonNull String packageName) {
+ try {
+ return mService.getGameModeInfo(packageName, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Sets the game mode for the given package.
* <p>
* The caller must have {@link android.Manifest.permission#MANAGE_GAME_MODE}.
diff --git a/core/java/android/app/GameModeInfo.aidl b/core/java/android/app/GameModeInfo.aidl
new file mode 100644
index 0000000..3b13201
--- /dev/null
+++ b/core/java/android/app/GameModeInfo.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 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;
+
+/**
+ * @hide
+ */
+parcelable GameModeInfo;
\ No newline at end of file
diff --git a/core/java/android/app/GameModeInfo.java b/core/java/android/app/GameModeInfo.java
new file mode 100644
index 0000000..fe0ac35
--- /dev/null
+++ b/core/java/android/app/GameModeInfo.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2021 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;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * GameModeInfo returned from {@link GameManager#getGameModeInfo(String)}.
+ * @hide
+ */
+@SystemApi
+public final class GameModeInfo implements Parcelable {
+
+ public static final @NonNull Creator<GameModeInfo> CREATOR = new Creator<GameModeInfo>() {
+ @Override
+ public GameModeInfo createFromParcel(Parcel in) {
+ return new GameModeInfo(in);
+ }
+
+ @Override
+ public GameModeInfo[] newArray(int size) {
+ return new GameModeInfo[size];
+ }
+ };
+
+ public GameModeInfo(@GameManager.GameMode int activeGameMode,
+ @NonNull @GameManager.GameMode int[] availableGameModes) {
+ mActiveGameMode = activeGameMode;
+ mAvailableGameModes = availableGameModes;
+ }
+
+ GameModeInfo(Parcel in) {
+ mActiveGameMode = in.readInt();
+ final int availableGameModesCount = in.readInt();
+ mAvailableGameModes = new int[availableGameModesCount];
+ in.readIntArray(mAvailableGameModes);
+ }
+
+ /**
+ * Returns the {@link GameManager.GameMode} the application is currently using.
+ * Developers can enable game modes by adding
+ * <code>
+ * <meta-data android:name="android.game_mode_intervention"
+ * android:resource="@xml/GAME_MODE_CONFIG_FILE" />
+ * </code>
+ * to the {@link <application> tag}, where the GAME_MODE_CONFIG_FILE is an XML file that
+ * specifies the game mode enablement and configuration:
+ * <code>
+ * <game-mode-config xmlns:android="http://schemas.android.com/apk/res/android"
+ * android:gameModePerformance="true"
+ * android:gameModeBattery="false"
+ * />
+ * </code>
+ */
+ public @GameManager.GameMode int getActiveGameMode() {
+ return mActiveGameMode;
+ }
+
+ /**
+ * The collection of {@link GameManager.GameMode GameModes} that can be applied to the game.
+ */
+ @NonNull
+ public @GameManager.GameMode int[] getAvailableGameModes() {
+ return mAvailableGameModes;
+ }
+
+ // Ideally there should be callback that the caller can register to know when the available
+ // GameMode and/or the active GameMode is changed, however, there's no concrete use case
+ // at the moment so there's no callback mechanism introduced .
+ private final @GameManager.GameMode int[] mAvailableGameModes;
+ private final @GameManager.GameMode int mActiveGameMode;
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mActiveGameMode);
+ dest.writeInt(mAvailableGameModes.length);
+ dest.writeIntArray(mAvailableGameModes);
+ }
+}
diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl
index d9aa586..57de8c7 100644
--- a/core/java/android/app/IGameManagerService.aidl
+++ b/core/java/android/app/IGameManagerService.aidl
@@ -16,6 +16,7 @@
package android.app;
+import android.app.GameModeInfo;
import android.app.GameState;
/**
@@ -27,4 +28,5 @@
int[] getAvailableGameModes(String packageName);
boolean getAngleEnabled(String packageName, int userId);
void setGameState(String packageName, in GameState gameState, int userId);
-}
\ No newline at end of file
+ GameModeInfo getGameModeInfo(String packageName, int userId);
+}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 63c1fd8..f0208c6 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -24,6 +24,8 @@
import android.app.ContextImpl.ServiceInitializationState;
import android.app.admin.DevicePolicyManager;
import android.app.admin.IDevicePolicyManager;
+import android.app.ambientcontext.AmbientContextManager;
+import android.app.ambientcontext.IAmbientContextEventObserver;
import android.app.appsearch.AppSearchManagerFrameworkInitializer;
import android.app.blob.BlobStoreManagerFrameworkInitializer;
import android.app.contentsuggestions.ContentSuggestionsManager;
@@ -1523,6 +1525,18 @@
}
});
+ registerService(Context.AMBIENT_CONTEXT_SERVICE, AmbientContextManager.class,
+ new CachedServiceFetcher<AmbientContextManager>() {
+ @Override
+ public AmbientContextManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder iBinder = ServiceManager.getServiceOrThrow(
+ Context.AMBIENT_CONTEXT_SERVICE);
+ IAmbientContextEventObserver manager =
+ IAmbientContextEventObserver.Stub.asInterface(iBinder);
+ return new AmbientContextManager(ctx.getOuterContext(), manager);
+ }});
+
sInitializing = true;
try {
// Note: the following functions need to be @SystemApis, once they become mainline
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index cefd25a..fa0af2d 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1578,6 +1578,78 @@
public static final int FLAG_SUPPORTED_MODES_DEVICE_OWNER = 1 << 2;
/**
+ * Constant for {@link #getMinimumRequiredWifiSecurityLevel()} and
+ * {@link #setMinimumRequiredWifiSecurityLevel(int)}: no minimum security level.
+ *
+ * <p> When returned from {@link #getMinimumRequiredWifiSecurityLevel()}, the constant
+ * represents the current minimum security level required.
+ * When passed to {@link #setMinimumRequiredWifiSecurityLevel(int)}, it sets the
+ * minimum security level a Wi-Fi network must meet.
+ *
+ * @see #WIFI_SECURITY_PERSONAL
+ * @see #WIFI_SECURITY_ENTERPRISE_EAP
+ * @see #WIFI_SECURITY_ENTERPRISE_192
+ */
+ public static final int WIFI_SECURITY_OPEN = 0;
+
+ /**
+ * Constant for {@link #getMinimumRequiredWifiSecurityLevel()} and
+ * {@link #setMinimumRequiredWifiSecurityLevel(int)}: personal network such as WEP, WPA2-PSK.
+ *
+ * <p> When returned from {@link #getMinimumRequiredWifiSecurityLevel()}, the constant
+ * represents the current minimum security level required.
+ * When passed to {@link #setMinimumRequiredWifiSecurityLevel(int)}, it sets the
+ * minimum security level a Wi-Fi network must meet.
+ *
+ * @see #WIFI_SECURITY_OPEN
+ * @see #WIFI_SECURITY_ENTERPRISE_EAP
+ * @see #WIFI_SECURITY_ENTERPRISE_192
+ */
+ public static final int WIFI_SECURITY_PERSONAL = 1;
+
+ /**
+ * Constant for {@link #getMinimumRequiredWifiSecurityLevel()} and
+ * {@link #setMinimumRequiredWifiSecurityLevel(int)}: enterprise EAP network.
+ *
+ * <p> When returned from {@link #getMinimumRequiredWifiSecurityLevel()}, the constant
+ * represents the current minimum security level required.
+ * When passed to {@link #setMinimumRequiredWifiSecurityLevel(int)}, it sets the
+ * minimum security level a Wi-Fi network must meet.
+ *
+ * @see #WIFI_SECURITY_OPEN
+ * @see #WIFI_SECURITY_PERSONAL
+ * @see #WIFI_SECURITY_ENTERPRISE_192
+ */
+ public static final int WIFI_SECURITY_ENTERPRISE_EAP = 2;
+
+ /**
+ * Constant for {@link #getMinimumRequiredWifiSecurityLevel()} and
+ * {@link #setMinimumRequiredWifiSecurityLevel(int)}: enterprise 192 bit network.
+ *
+ * <p> When returned from {@link #getMinimumRequiredWifiSecurityLevel()}, the constant
+ * represents the current minimum security level required.
+ * When passed to {@link #setMinimumRequiredWifiSecurityLevel(int)}, it sets the
+ * minimum security level a Wi-Fi network must meet.
+ *
+ * @see #WIFI_SECURITY_OPEN
+ * @see #WIFI_SECURITY_PERSONAL
+ * @see #WIFI_SECURITY_ENTERPRISE_EAP
+ */
+ public static final int WIFI_SECURITY_ENTERPRISE_192 = 3;
+
+ /**
+ * Possible Wi-Fi minimum security levels
+ *
+ * @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"WIFI_SECURITY_"}, value = {
+ WIFI_SECURITY_OPEN,
+ WIFI_SECURITY_PERSONAL,
+ WIFI_SECURITY_ENTERPRISE_EAP,
+ WIFI_SECURITY_ENTERPRISE_192})
+ public @interface WifiSecurity {}
+
+ /**
* This MIME type is used for starting the device owner provisioning.
*
* <p>During device owner provisioning a device admin app is set as the owner of the device.
@@ -14560,6 +14632,105 @@
}
/**
+ * Called by device owner or profile owner of an organization-owned managed profile to
+ * specify the minimum security level required for Wi-Fi networks.
+ * The device may not connect to networks that do not meet the minimum security level.
+ * If the current network does not meet the minimum security level set, it will be disconnected.
+ *
+ *
+ * @param level minimum security level
+ * @throws SecurityException if the caller is not a device owner or a profile owner on
+ * an organization-owned managed profile.
+ */
+ public void setMinimumRequiredWifiSecurityLevel(@WifiSecurity int level) {
+ throwIfParentInstance("setMinimumRequiredWifiSecurityLevel");
+ if (mService != null) {
+ try {
+ mService.setMinimumRequiredWifiSecurityLevel(level);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Returns the current Wi-Fi minimum security level.
+ *
+ * @see #setMinimumRequiredWifiSecurityLevel(int)
+ */
+ public @WifiSecurity int getMinimumRequiredWifiSecurityLevel() {
+ throwIfParentInstance("getMinimumRequiredWifiSecurityLevel");
+ if (mService == null) {
+ return WIFI_SECURITY_OPEN;
+ }
+ try {
+ return mService.getMinimumRequiredWifiSecurityLevel();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Called by device owner or profile owner of an organization-owned managed profile to
+ * specify the Wi-Fi SSID policy ({@link WifiSsidPolicy}).
+ * Wi-Fi SSID policy specifies the SSID restriction the network must satisfy
+ * in order to be eligible for a connection. Providing a null policy results in the
+ * deactivation of the SSID restriction
+ *
+ * @param policy Wi-Fi SSID policy
+ * @throws SecurityException if the caller is not a device owner or a profile owner on
+ * an organization-owned managed profile.
+ */
+ public void setWifiSsidPolicy(@Nullable WifiSsidPolicy policy) {
+ throwIfParentInstance("setWifiSsidPolicy");
+ if (mService != null) {
+ try {
+ if (policy == null) {
+ mService.setSsidAllowlist(new ArrayList<>());
+ } else {
+ int policyType = policy.getPolicyType();
+ if (policyType == WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST) {
+ mService.setSsidAllowlist(new ArrayList<>(policy.getSsids()));
+ } else {
+ mService.setSsidDenylist(new ArrayList<>(policy.getSsids()));
+ }
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Returns the current Wi-Fi SSID policy.
+ * If the policy has not been set, it will return NULL.
+ *
+ * @see #setWifiSsidPolicy(WifiSsidPolicy)
+ * @throws SecurityException if the caller is not a device owner or a profile owner on
+ * an organization-owned managed profile or a system app.
+ */
+ @Nullable
+ public WifiSsidPolicy getWifiSsidPolicy() {
+ throwIfParentInstance("getWifiSsidPolicy");
+ if (mService == null) {
+ return null;
+ }
+ try {
+ List<String> allowlist = mService.getSsidAllowlist();
+ if (!allowlist.isEmpty()) {
+ return WifiSsidPolicy.createAllowlistPolicy(new ArraySet<>(allowlist));
+ }
+ List<String> denylist = mService.getSsidDenylist();
+ if (!denylist.isEmpty()) {
+ return WifiSsidPolicy.createDenylistPolicy(new ArraySet<>(denylist));
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return null;
+ }
+
+ /**
* For each {@link DevicePolicyDrawableResource} item in {@code drawables}, if
* {@link DevicePolicyDrawableResource#getDrawableSource()} is not set or is set to
* {@link DevicePolicyResources.Drawable.Source#UNDEFINED}, it updates the drawable resource for
@@ -14896,7 +15067,7 @@
Objects.requireNonNull(stringId, "stringId can't be null");
Objects.requireNonNull(defaultStringLoader, "defaultStringLoader can't be null");
- if (stringId.equals(DevicePolicyResources.Strings.INVALID_ID)) {
+ if (stringId.equals(DevicePolicyResources.Strings.UNDEFINED)) {
return ParcelableResource.loadDefaultString(defaultStringLoader);
}
if (mService != null) {
@@ -14944,7 +15115,7 @@
Objects.requireNonNull(stringId, "stringId can't be null");
Objects.requireNonNull(defaultStringLoader, "defaultStringLoader can't be null");
- if (stringId.equals(DevicePolicyResources.Strings.INVALID_ID)) {
+ if (stringId.equals(DevicePolicyResources.Strings.UNDEFINED)) {
return ParcelableResource.loadDefaultString(defaultStringLoader);
}
if (mService != null) {
diff --git a/core/java/android/app/admin/DevicePolicyResources.java b/core/java/android/app/admin/DevicePolicyResources.java
index 21e20cd..46e2cce 100644
--- a/core/java/android/app/admin/DevicePolicyResources.java
+++ b/core/java/android/app/admin/DevicePolicyResources.java
@@ -16,6 +16,64 @@
package android.app.admin;
+import static android.app.admin.DevicePolicyResources.Strings.Core.CANT_ADD_ACCOUNT_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.LOCATION_CHANGED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.LOCATION_CHANGED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.NETWORK_LOGGING_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.NETWORK_LOGGING_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.NOTIFICATION_CHANNEL_DEVICE_ADMIN;
+import static android.app.admin.DevicePolicyResources.Strings.Core.NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_DELETED_BY_DO;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_INSTALLED_BY_DO;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_UPDATED_BY_DO;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PERSONAL_APP_SUSPENSION_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PERSONAL_APP_SUSPENSION_SOON_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PERSONAL_APP_SUSPENSION_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PRINTING_DISABLED_NAMED_ADMIN;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_DETAIL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_PERSONAL_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_WORK_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_PERSONAL_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_PERSONAL_TAB_ACCESSIBILITY;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PAUSED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PROFILE_NOT_SUPPORTED;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_TAB_ACCESSIBILITY;
+import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_PERSONAL_LABEL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_WORK_LABEL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.UNLAUNCHABLE_APP_WORK_PAUSED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.UNLAUNCHABLE_APP_WORK_PAUSED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_BADGED_LABEL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_GENERIC_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SAVE_TO_PERSONAL_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SAVE_TO_PERSONAL_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SAVE_TO_WORK_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SAVE_TO_WORK_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SELECT_PERSONAL_FILES_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SELECT_PERSONAL_FILES_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SELECT_WORK_FILES_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SELECT_WORK_FILES_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CROSS_PROFILE_NOT_ALLOWED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CROSS_PROFILE_NOT_ALLOWED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.PERSONAL_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.PREVIEW_WORK_FILE_ACCESSIBILITY;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.WORK_ACCESSIBILITY;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.WORK_PROFILE_OFF_ENABLE_BUTTON;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.WORK_PROFILE_OFF_ERROR_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.WORK_TAB;
import static android.app.admin.DevicePolicyResources.Strings.Launcher.ALL_APPS_PERSONAL_TAB;
import static android.app.admin.DevicePolicyResources.Strings.Launcher.ALL_APPS_PERSONAL_TAB_ACCESSIBILITY;
import static android.app.admin.DevicePolicyResources.Strings.Launcher.ALL_APPS_WORK_TAB;
@@ -28,8 +86,13 @@
import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_EDU_ACCEPT;
import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_ENABLE_BUTTON;
import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_PAUSED_DESCRIPTION;
-import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_PAUSED_TITLE;
import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_PAUSE_BUTTON;
+import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.BLOCKED_BY_ADMIN_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.BLOCKED_FROM_PERSONAL_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.BLOCKED_FROM_WORK_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.SWITCH_TO_PERSONAL_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.SWITCH_TO_WORK_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.WORK_PROFILE_PAUSED_MESSAGE;
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_LOCK_FAILED_ATTEMPTS;
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_PASSWORD_LAST_ATTEMPT;
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_PATTERN_LAST_ATTEMPT;
@@ -65,11 +128,11 @@
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.STATUS_BAR_WORK_ICON_ACCESSIBILITY;
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.WORK_LOCK_ACCESSIBILITY;
-
import android.annotation.IntDef;
import android.annotation.StringDef;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.os.UserHandle;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -138,7 +201,7 @@
@Retention(RetentionPolicy.SOURCE)
@StringDef({
// Launcher Strings
- WORK_PROFILE_EDU, WORK_PROFILE_EDU_ACCEPT, WORK_PROFILE_PAUSED_TITLE,
+ WORK_PROFILE_EDU, WORK_PROFILE_EDU_ACCEPT, Strings.Launcher.WORK_PROFILE_PAUSED_TITLE,
WORK_PROFILE_PAUSED_DESCRIPTION, WORK_PROFILE_PAUSE_BUTTON, WORK_PROFILE_ENABLE_BUTTON,
ALL_APPS_WORK_TAB, ALL_APPS_PERSONAL_TAB, ALL_APPS_WORK_TAB_ACCESSIBILITY,
ALL_APPS_PERSONAL_TAB_ACCESSIBILITY, WORK_FOLDER_NAME, WIDGETS_WORK_TAB,
@@ -160,7 +223,41 @@
BIOMETRIC_DIALOG_WORK_PATTERN_LAST_ATTEMPT, BIOMETRIC_DIALOG_WORK_PASSWORD_LAST_ATTEMPT,
BIOMETRIC_DIALOG_WORK_LOCK_FAILED_ATTEMPTS, STATUS_BAR_WORK_ICON_ACCESSIBILITY,
ONGOING_PRIVACY_DIALOG_WORK, KEYGUARD_MANAGEMENT_DISCLOSURE,
- KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE, WORK_LOCK_ACCESSIBILITY
+ KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE, WORK_LOCK_ACCESSIBILITY,
+
+ // Core Strings
+ WORK_PROFILE_DELETED_TITLE, WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE,
+ WORK_PROFILE_DELETED_GENERIC_MESSAGE, WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE,
+ PERSONAL_APP_SUSPENSION_TITLE, PERSONAL_APP_SUSPENSION_MESSAGE,
+ PERSONAL_APP_SUSPENSION_SOON_MESSAGE, PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE,
+ PRINTING_DISABLED_NAMED_ADMIN, LOCATION_CHANGED_TITLE, LOCATION_CHANGED_MESSAGE,
+ NETWORK_LOGGING_TITLE, NETWORK_LOGGING_MESSAGE,
+ NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION, NOTIFICATION_CHANNEL_DEVICE_ADMIN,
+ SWITCH_TO_WORK_LABEL, SWITCH_TO_PERSONAL_LABEL, FORWARD_INTENT_TO_WORK,
+ FORWARD_INTENT_TO_PERSONAL, RESOLVER_WORK_PROFILE_NOT_SUPPORTED, RESOLVER_PERSONAL_TAB,
+ RESOLVER_WORK_TAB, RESOLVER_PERSONAL_TAB_ACCESSIBILITY, RESOLVER_WORK_TAB_ACCESSIBILITY,
+ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE, RESOLVER_CANT_SHARE_WITH_PERSONAL,
+ RESOLVER_CANT_SHARE_WITH_WORK, RESOLVER_CANT_ACCESS_PERSONAL, RESOLVER_CANT_ACCESS_WORK,
+ RESOLVER_WORK_PAUSED_TITLE, RESOLVER_NO_WORK_APPS, RESOLVER_NO_PERSONAL_APPS,
+ CANT_ADD_ACCOUNT_MESSAGE, PACKAGE_INSTALLED_BY_DO, PACKAGE_UPDATED_BY_DO,
+ PACKAGE_DELETED_BY_DO, UNLAUNCHABLE_APP_WORK_PAUSED_TITLE,
+ UNLAUNCHABLE_APP_WORK_PAUSED_MESSAGE, PROFILE_ENCRYPTED_TITLE, PROFILE_ENCRYPTED_DETAIL,
+ PROFILE_ENCRYPTED_MESSAGE, WORK_PROFILE_BADGED_LABEL,
+
+ // DocsUi Strings
+ WORK_PROFILE_OFF_ERROR_TITLE, WORK_PROFILE_OFF_ENABLE_BUTTON,
+ CANT_SELECT_WORK_FILES_TITLE, CANT_SELECT_WORK_FILES_MESSAGE,
+ CANT_SELECT_PERSONAL_FILES_TITLE, CANT_SELECT_PERSONAL_FILES_MESSAGE,
+ CANT_SAVE_TO_WORK_TITLE, CANT_SAVE_TO_WORK_MESSAGE, CANT_SAVE_TO_PERSONAL_TITLE,
+ CANT_SAVE_TO_PERSONAL_MESSAGE, CROSS_PROFILE_NOT_ALLOWED_TITLE,
+ CROSS_PROFILE_NOT_ALLOWED_MESSAGE, PREVIEW_WORK_FILE_ACCESSIBILITY, PERSONAL_TAB,
+ WORK_TAB, WORK_ACCESSIBILITY,
+
+ // MediaProvider Strings
+ SWITCH_TO_WORK_MESSAGE, SWITCH_TO_PERSONAL_MESSAGE, BLOCKED_BY_ADMIN_TITLE,
+ BLOCKED_FROM_PERSONAL_MESSAGE, BLOCKED_FROM_PERSONAL_MESSAGE,
+ BLOCKED_FROM_WORK_MESSAGE, Strings.MediaProvider.WORK_PROFILE_PAUSED_TITLE,
+ WORK_PROFILE_PAUSED_MESSAGE
})
public @interface UpdatableStringId {
}
@@ -340,7 +437,7 @@
/**
* An ID for any string that can't be updated.
*/
- public static final String INVALID_ID = "INVALID_ID";
+ public static final String UNDEFINED = "UNDEFINED";
/**
* @hide
@@ -351,6 +448,9 @@
Set<String> strings = new HashSet<>();
strings.addAll(Launcher.buildStringsSet());
strings.addAll(SystemUi.buildStringsSet());
+ strings.addAll(Core.buildStringsSet());
+ strings.addAll(DocumentsUi.buildStringsSet());
+ strings.addAll(MediaProvider.buildStringsSet());
return strings;
}
@@ -364,78 +464,84 @@
private Launcher(){}
+ private static final String PREFIX = "Launcher.";
+
/**
* User on-boarding title for work profile apps.
*/
- public static final String WORK_PROFILE_EDU = "WORK_PROFILE_EDU";
+ public static final String WORK_PROFILE_EDU = PREFIX + "WORK_PROFILE_EDU";
/**
* Action label to finish work profile edu.
*/
- public static final String WORK_PROFILE_EDU_ACCEPT = "WORK_PROFILE_EDU_ACCEPT";
+ public static final String WORK_PROFILE_EDU_ACCEPT = PREFIX + "WORK_PROFILE_EDU_ACCEPT";
/**
* Title shown when user opens work apps tab while work profile is paused.
*/
- public static final String WORK_PROFILE_PAUSED_TITLE = "WORK_PROFILE_PAUSED_TITLE";
+ public static final String WORK_PROFILE_PAUSED_TITLE =
+ PREFIX + "WORK_PROFILE_PAUSED_TITLE";
/**
* Description shown when user opens work apps tab while work profile is paused.
*/
public static final String WORK_PROFILE_PAUSED_DESCRIPTION =
- "WORK_PROFILE_PAUSED_DESCRIPTION";
+ PREFIX + "WORK_PROFILE_PAUSED_DESCRIPTION";
/**
* Shown on the button to pause work profile.
*/
- public static final String WORK_PROFILE_PAUSE_BUTTON = "WORK_PROFILE_PAUSE_BUTTON";
+ public static final String WORK_PROFILE_PAUSE_BUTTON =
+ PREFIX + "WORK_PROFILE_PAUSE_BUTTON";
/**
* Shown on the button to enable work profile.
*/
- public static final String WORK_PROFILE_ENABLE_BUTTON = "WORK_PROFILE_ENABLE_BUTTON";
+ public static final String WORK_PROFILE_ENABLE_BUTTON =
+ PREFIX + "WORK_PROFILE_ENABLE_BUTTON";
/**
* Label on launcher tab to indicate work apps.
*/
- public static final String ALL_APPS_WORK_TAB = "ALL_APPS_WORK_TAB";
+ public static final String ALL_APPS_WORK_TAB = PREFIX + "ALL_APPS_WORK_TAB";
/**
* Label on launcher tab to indicate personal apps.
*/
- public static final String ALL_APPS_PERSONAL_TAB = "ALL_APPS_PERSONAL_TAB";
+ public static final String ALL_APPS_PERSONAL_TAB = PREFIX + "ALL_APPS_PERSONAL_TAB";
/**
* Accessibility description for launcher tab to indicate work apps.
*/
public static final String ALL_APPS_WORK_TAB_ACCESSIBILITY =
- "ALL_APPS_WORK_TAB_ACCESSIBILITY";
+ PREFIX + "ALL_APPS_WORK_TAB_ACCESSIBILITY";
/**
* Accessibility description for launcher tab to indicate personal apps.
*/
public static final String ALL_APPS_PERSONAL_TAB_ACCESSIBILITY =
- "ALL_APPS_PERSONAL_TAB_ACCESSIBILITY";
+ PREFIX + "ALL_APPS_PERSONAL_TAB_ACCESSIBILITY";
/**
* Work folder name.
*/
- public static final String WORK_FOLDER_NAME = "WORK_FOLDER_NAME";
+ public static final String WORK_FOLDER_NAME = PREFIX + "WORK_FOLDER_NAME";
/**
* Label on widget tab to indicate work app widgets.
*/
- public static final String WIDGETS_WORK_TAB = "WIDGETS_WORK_TAB";
+ public static final String WIDGETS_WORK_TAB = PREFIX + "WIDGETS_WORK_TAB";
/**
* Label on widget tab to indicate personal app widgets.
*/
- public static final String WIDGETS_PERSONAL_TAB = "WIDGETS_PERSONAL_TAB";
+ public static final String WIDGETS_PERSONAL_TAB = PREFIX + "WIDGETS_PERSONAL_TAB";
/**
* Message shown when a feature is disabled by the admin (e.g. changing wallpaper).
*/
- public static final String DISABLED_BY_ADMIN_MESSAGE = "DISABLED_BY_ADMIN_MESSAGE";
+ public static final String DISABLED_BY_ADMIN_MESSAGE =
+ PREFIX + "DISABLED_BY_ADMIN_MESSAGE";
/**
* @hide
@@ -470,231 +576,236 @@
private SystemUi() {
}
+ private static final String PREFIX = "SystemUi.";
/**
* Label in quick settings for toggling work profile on/off.
*/
- public static final String QS_WORK_PROFILE_LABEL = "QS_WORK_PROFILE_LABEL";
+ public static final String QS_WORK_PROFILE_LABEL = PREFIX + "QS_WORK_PROFILE_LABEL";
/**
* Disclosure at the bottom of Quick Settings to indicate device management.
*/
- public static final String QS_MSG_MANAGEMENT = "QS_MSG_MANAGEMENT";
+ public static final String QS_MSG_MANAGEMENT = PREFIX + "QS_MSG_MANAGEMENT";
/**
* Similar to {@link #QS_MSG_MANAGEMENT} but accepts the organization name as a
* param.
*/
- public static final String QS_MSG_NAMED_MANAGEMENT = "QS_MSG_NAMED_MANAGEMENT";
+ public static final String QS_MSG_NAMED_MANAGEMENT = PREFIX + "QS_MSG_NAMED_MANAGEMENT";
/**
* Disclosure at the bottom of Quick Settings to indicate device management monitoring.
*/
public static final String QS_MSG_MANAGEMENT_MONITORING =
- "QS_MSG_MANAGEMENT_MONITORING";
+ PREFIX + "QS_MSG_MANAGEMENT_MONITORING";
/**
* Similar to {@link #QS_MSG_MANAGEMENT_MONITORING} but accepts the
* organization name as a param.
*/
public static final String QS_MSG_NAMED_MANAGEMENT_MONITORING =
- "QS_MSG_NAMED_MANAGEMENT_MONITORING";
+ PREFIX + "QS_MSG_NAMED_MANAGEMENT_MONITORING";
/**
* Disclosure at the bottom of Quick Settings to indicate device management and the
* device is connected to a VPN, accepts VPN name as a param.
*/
public static final String QS_MSG_MANAGEMENT_NAMED_VPN =
- "QS_MSG_MANAGEMENT_NAMED_VPN";
+ PREFIX + "QS_MSG_MANAGEMENT_NAMED_VPN";
/**
* Similar to {@link #QS_MSG_MANAGEMENT_NAMED_VPN} but also accepts the
* organization name as a param.
*/
public static final String QS_MSG_NAMED_MANAGEMENT_NAMED_VPN =
- "QS_MSG_NAMED_MANAGEMENT_NAMED_VPN";
+ PREFIX + "QS_MSG_NAMED_MANAGEMENT_NAMED_VPN";
/**
* Disclosure at the bottom of Quick Settings to indicate device management and the
* device is connected to multiple VPNs.
*/
public static final String QS_MSG_MANAGEMENT_MULTIPLE_VPNS =
- "QS_MSG_MANAGEMENT_MULTIPLE_VPNS";
+ PREFIX + "QS_MSG_MANAGEMENT_MULTIPLE_VPNS";
/**
* Similar to {@link #QS_MSG_MANAGEMENT_MULTIPLE_VPNS} but also accepts the
* organization name as a param.
*/
public static final String QS_MSG_NAMED_MANAGEMENT_MULTIPLE_VPNS =
- "QS_MSG_NAMED_MANAGEMENT_MULTIPLE_VPNS";
+ PREFIX + "QS_MSG_NAMED_MANAGEMENT_MULTIPLE_VPNS";
/**
* Disclosure at the bottom of Quick Settings to indicate work profile monitoring.
*/
public static final String QS_MSG_WORK_PROFILE_MONITORING =
- "QS_MSG_WORK_PROFILE_MONITORING";
+ PREFIX + "QS_MSG_WORK_PROFILE_MONITORING";
/**
* Similar to {@link #QS_MSG_WORK_PROFILE_MONITORING} but accepts the
* organization name as a param.
*/
public static final String QS_MSG_NAMED_WORK_PROFILE_MONITORING =
- "QS_MSG_NAMED_WORK_PROFILE_MONITORING";
+ PREFIX + "QS_MSG_NAMED_WORK_PROFILE_MONITORING";
/**
* Disclosure at the bottom of Quick Settings to indicate network activity is visible to
* admin.
*/
- public static final String QS_MSG_WORK_PROFILE_NETWORK = "QS_MSG_WORK_PROFILE_NETWORK";
+ public static final String QS_MSG_WORK_PROFILE_NETWORK =
+ PREFIX + "QS_MSG_WORK_PROFILE_NETWORK";
/**
* Disclosure at the bottom of Quick Settings to indicate work profile is connected to a
* VPN, accepts VPN name as a param.
*/
public static final String QS_MSG_WORK_PROFILE_NAMED_VPN =
- "QS_MSG_WORK_PROFILE_NAMED_VPN";
+ PREFIX + "QS_MSG_WORK_PROFILE_NAMED_VPN";
/**
* Disclosure at the bottom of Quick Settings to indicate personal profile is connected
* to a VPN, accepts VPN name as a param.
*/
public static final String QS_MSG_PERSONAL_PROFILE_NAMED_VPN =
- "QS_MSG_PERSONAL_PROFILE_NAMED_VPN";
+ PREFIX + "QS_MSG_PERSONAL_PROFILE_NAMED_VPN";
/**
* Title for dialog to indicate device management.
*/
- public static final String QS_DIALOG_MANAGEMENT_TITLE = "QS_DIALOG_MANAGEMENT_TITLE";
+ public static final String QS_DIALOG_MANAGEMENT_TITLE =
+ PREFIX + "QS_DIALOG_MANAGEMENT_TITLE";
/**
* Label for button in the device management dialog to open a page with more information
* on the admin's abilities.
*/
- public static final String QS_DIALOG_VIEW_POLICIES = "QS_DIALOG_VIEW_POLICIES";
+ public static final String QS_DIALOG_VIEW_POLICIES =
+ PREFIX + "QS_DIALOG_VIEW_POLICIES";
/**
* Description for device management dialog to indicate admin abilities.
*/
- public static final String QS_DIALOG_MANAGEMENT = "QS_DIALOG_MANAGEMENT";
+ public static final String QS_DIALOG_MANAGEMENT = PREFIX + "QS_DIALOG_MANAGEMENT";
/**
* Similar to {@link #QS_DIALOG_MANAGEMENT} but accepts the organization name as a
* param.
*/
- public static final String QS_DIALOG_NAMED_MANAGEMENT = "QS_DIALOG_NAMED_MANAGEMENT";
+ public static final String QS_DIALOG_NAMED_MANAGEMENT =
+ PREFIX + "QS_DIALOG_NAMED_MANAGEMENT";
/**
* Description for the managed device certificate authorities in the device management
* dialog.
*/
public static final String QS_DIALOG_MANAGEMENT_CA_CERT =
- "QS_DIALOG_MANAGEMENT_CA_CERT";
+ PREFIX + "QS_DIALOG_MANAGEMENT_CA_CERT";
/**
* Description for the work profile certificate authorities in the device management
* dialog.
*/
public static final String QS_DIALOG_WORK_PROFILE_CA_CERT =
- "QS_DIALOG_WORK_PROFILE_CA_CERT";
+ PREFIX + "QS_DIALOG_WORK_PROFILE_CA_CERT";
/**
* Description for the managed device network logging in the device management dialog.
*/
public static final String QS_DIALOG_MANAGEMENT_NETWORK =
- "QS_DIALOG_MANAGEMENT_NETWORK";
+ PREFIX + "QS_DIALOG_MANAGEMENT_NETWORK";
/**
* Description for the work profile network logging in the device management dialog.
*/
public static final String QS_DIALOG_WORK_PROFILE_NETWORK =
- "QS_DIALOG_WORK_PROFILE_NETWORK";
+ PREFIX + "QS_DIALOG_WORK_PROFILE_NETWORK";
/**
* Description for an active VPN in the device management dialog, accepts VPN name as a
* param.
*/
public static final String QS_DIALOG_MANAGEMENT_NAMED_VPN =
- "QS_DIALOG_MANAGEMENT_NAMED_VPN";
+ PREFIX + "QS_DIALOG_MANAGEMENT_NAMED_VPN";
/**
* Description for two active VPN in the device management dialog, accepts two VPN names
* as params.
*/
public static final String QS_DIALOG_MANAGEMENT_TWO_NAMED_VPN =
- "QS_DIALOG_MANAGEMENT_TWO_NAMED_VPN";
+ PREFIX + "QS_DIALOG_MANAGEMENT_TWO_NAMED_VPN";
/**
* Description for an active work profile VPN in the device management dialog, accepts
* VPN name as a param.
*/
public static final String QS_DIALOG_WORK_PROFILE_NAMED_VPN =
- "QS_DIALOG_WORK_PROFILE_NAMED_VPN";
+ PREFIX + "QS_DIALOG_WORK_PROFILE_NAMED_VPN";
/**
* Description for an active personal profile VPN in the device management dialog,
* accepts VPN name as a param.
*/
public static final String QS_DIALOG_PERSONAL_PROFILE_NAMED_VPN =
- "QS_DIALOG_PERSONAL_PROFILE_NAMED_VPN";
+ PREFIX + "QS_DIALOG_PERSONAL_PROFILE_NAMED_VPN";
/**
* Content of a dialog shown when the user only has one attempt left to provide the
* correct pin before the work profile is removed.
*/
public static final String BIOMETRIC_DIALOG_WORK_PIN_LAST_ATTEMPT =
- "BIOMETRIC_DIALOG_WORK_PIN_LAST_ATTEMPT";
+ PREFIX + "BIOMETRIC_DIALOG_WORK_PIN_LAST_ATTEMPT";
/**
* Content of a dialog shown when the user only has one attempt left to provide the
* correct pattern before the work profile is removed.
*/
public static final String BIOMETRIC_DIALOG_WORK_PATTERN_LAST_ATTEMPT =
- "BIOMETRIC_DIALOG_WORK_PATTERN_LAST_ATTEMPT";
+ PREFIX + "BIOMETRIC_DIALOG_WORK_PATTERN_LAST_ATTEMPT";
/**
* Content of a dialog shown when the user only has one attempt left to provide the
* correct password before the work profile is removed.
*/
public static final String BIOMETRIC_DIALOG_WORK_PASSWORD_LAST_ATTEMPT =
- "BIOMETRIC_DIALOG_WORK_PASSWORD_LAST_ATTEMPT";
+ PREFIX + "BIOMETRIC_DIALOG_WORK_PASSWORD_LAST_ATTEMPT";
/**
* Content of a dialog shown when the user has failed to provide the work lock too many
* times and the work profile is removed.
*/
public static final String BIOMETRIC_DIALOG_WORK_LOCK_FAILED_ATTEMPTS =
- "BIOMETRIC_DIALOG_WORK_LOCK_FAILED_ATTEMPTS";
+ PREFIX + "BIOMETRIC_DIALOG_WORK_LOCK_FAILED_ATTEMPTS";
/**
* Accessibility label for managed profile icon in the status bar
*/
public static final String STATUS_BAR_WORK_ICON_ACCESSIBILITY =
- "STATUS_BAR_WORK_ICON_ACCESSIBILITY";
+ PREFIX + "STATUS_BAR_WORK_ICON_ACCESSIBILITY";
/**
* Text appended to privacy dialog, indicating that the application is in the work
* profile.
*/
public static final String ONGOING_PRIVACY_DIALOG_WORK =
- "ONGOING_PRIVACY_DIALOG_WORK";
+ PREFIX + "ONGOING_PRIVACY_DIALOG_WORK";
/**
* Text on keyguard screen indicating device management.
*/
public static final String KEYGUARD_MANAGEMENT_DISCLOSURE =
- "KEYGUARD_MANAGEMENT_DISCLOSURE";
+ PREFIX + "KEYGUARD_MANAGEMENT_DISCLOSURE";
/**
* Similar to {@link #KEYGUARD_MANAGEMENT_DISCLOSURE} but also accepts organization name
* as a param.
*/
public static final String KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE =
- "KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE";
+ PREFIX + "KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE";
/**
* Content description for the work profile lock screen.
*/
- public static final String WORK_LOCK_ACCESSIBILITY = "WORK_LOCK_ACCESSIBILITY";
+ public static final String WORK_LOCK_ACCESSIBILITY = PREFIX + "WORK_LOCK_ACCESSIBILITY";
/**
* @hide
@@ -739,5 +850,568 @@
return strings;
}
}
+
+ /**
+ * Class containing the identifiers used to update device management-related system strings
+ * in the android core package.
+ *
+ * @hide
+ */
+ public static final class Core {
+
+ private Core() {
+ }
+
+ private static final String PREFIX = "Core.";
+ /**
+ * Notification title when the system deletes the work profile.
+ */
+ public static final String WORK_PROFILE_DELETED_TITLE =
+ PREFIX + "WORK_PROFILE_DELETED_TITLE";
+
+ /**
+ * Content text for the "Work profile deleted" notification to indicates that a work
+ * profile has been deleted because the maximum failed password attempts as been
+ * reached.
+ */
+ public static final String WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE =
+ PREFIX + "WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE";
+
+ /**
+ * Content text for the "Work profile deleted" notification to indicate that a work
+ * profile has been deleted.
+ */
+ public static final String WORK_PROFILE_DELETED_GENERIC_MESSAGE =
+ PREFIX + "WORK_PROFILE_DELETED_GENERIC_MESSAGE";
+
+ /**
+ * Content text for the "Work profile deleted" notification to indicates that a work
+ * profile has been deleted because the admin of an organization-owned device has
+ * relinquishes it.
+ */
+ public static final String WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE =
+ PREFIX + "WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE";
+
+ /**
+ * Notification title for when personal apps are either blocked or will be blocked
+ * soon due to a work policy from their admin.
+ */
+ public static final String PERSONAL_APP_SUSPENSION_TITLE =
+ PREFIX + "PERSONAL_APP_SUSPENSION_TITLE";
+
+ /**
+ * Content text for the personal app suspension notification to indicate that personal
+ * apps are blocked due to a work policy from the admin.
+ */
+ public static final String PERSONAL_APP_SUSPENSION_MESSAGE =
+ PREFIX + "PERSONAL_APP_SUSPENSION_MESSAGE";
+
+ /**
+ * Content text for the personal app suspension notification to indicate that personal
+ * apps will be blocked at a particular time due to a work policy from their admin.
+ * It also explains for how many days the profile is allowed to be off.
+ * <ul>Takes in the following as params:
+ * <li> The date that the personal apps will get suspended at</li>
+ * <li> The time that the personal apps will get suspended at</li>
+ * <li> The max allowed days for the work profile stay switched off</li>
+ * </ul>
+ */
+ public static final String PERSONAL_APP_SUSPENSION_SOON_MESSAGE =
+ PREFIX + "PERSONAL_APP_SUSPENSION_SOON_MESSAGE";
+
+ /**
+ * Title for the button that turns work profile in the personal app suspension
+ * notification.
+ */
+ public static final String PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE =
+ PREFIX + "PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE";
+
+ /**
+ * A toast message displayed when printing is attempted but disabled by policy, accepts
+ * admin name as a param.
+ */
+ public static final String PRINTING_DISABLED_NAMED_ADMIN =
+ PREFIX + "PRINTING_DISABLED_NAMED_ADMIN";
+
+ /**
+ * Notification title to indicate that the device owner has changed the location
+ * settings.
+ */
+ public static final String LOCATION_CHANGED_TITLE = PREFIX + "LOCATION_CHANGED_TITLE";
+
+ /**
+ * Content text for the location changed notification to indicate that the device owner
+ * has changed the location settings.
+ */
+ public static final String LOCATION_CHANGED_MESSAGE =
+ PREFIX + "LOCATION_CHANGED_MESSAGE";
+
+ /**
+ * Notification title to indicate that the device is managed and network logging was
+ * activated by a device owner.
+ */
+ public static final String NETWORK_LOGGING_TITLE = PREFIX + "NETWORK_LOGGING_TITLE";
+
+ /**
+ * Content text for the network logging notification to indicate that the device is
+ * managed and network logging was activated by a device owner.
+ */
+ public static final String NETWORK_LOGGING_MESSAGE = PREFIX + "NETWORK_LOGGING_MESSAGE";
+
+ /**
+ * Content description of the work profile icon in the notifications.
+ */
+ public static final String NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION =
+ PREFIX + "NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION";
+
+ /**
+ * Notification channel name for high-priority alerts from the user's IT admin for key
+ * updates about the device.
+ */
+ public static final String NOTIFICATION_CHANNEL_DEVICE_ADMIN =
+ PREFIX + "NOTIFICATION_CHANNEL_DEVICE_ADMIN";
+
+ /**
+ * Label returned from
+ * {@link android.content.pm.CrossProfileApps#getProfileSwitchingLabel(UserHandle)}
+ * that calling app can show to user for the semantic of switching to work profile.
+ */
+ public static final String SWITCH_TO_WORK_LABEL = PREFIX + "SWITCH_TO_WORK_LABEL";
+
+ /**
+ * Label returned from
+ * {@link android.content.pm.CrossProfileApps#getProfileSwitchingLabel(UserHandle)}
+ * that calling app can show to user for the semantic of switching to personal profile.
+ */
+ public static final String SWITCH_TO_PERSONAL_LABEL =
+ PREFIX + "SWITCH_TO_PERSONAL_LABEL";
+
+ /**
+ * Message to show when an intent automatically switches users into the work profile.
+ */
+ public static final String FORWARD_INTENT_TO_WORK = PREFIX + "FORWARD_INTENT_TO_WORK";
+
+ /**
+ * Message to show when an intent automatically switches users into the personal
+ * profile.
+ */
+ public static final String FORWARD_INTENT_TO_PERSONAL =
+ PREFIX + "FORWARD_INTENT_TO_PERSONAL";
+
+ /**
+ * Text for the toast that is shown when the user clicks on a launcher that doesn't
+ * support the work profile, takes in the launcher name as a param.
+ */
+ public static final String RESOLVER_WORK_PROFILE_NOT_SUPPORTED =
+ PREFIX + "RESOLVER_WORK_PROFILE_NOT_SUPPORTED";
+
+ /**
+ * Label for the personal tab in the {@link com.android.internal.app.ResolverActivity).
+ */
+ public static final String RESOLVER_PERSONAL_TAB = PREFIX + "RESOLVER_PERSONAL_TAB";
+
+ /**
+ * Label for the work tab in the {@link com.android.internal.app.ResolverActivity).
+ */
+ public static final String RESOLVER_WORK_TAB = PREFIX + "RESOLVER_WORK_TAB";
+
+ /**
+ * Accessibility Label for the personal tab in the
+ * {@link com.android.internal.app.ResolverActivity).
+ */
+ public static final String RESOLVER_PERSONAL_TAB_ACCESSIBILITY =
+ PREFIX + "RESOLVER_PERSONAL_TAB_ACCESSIBILITY";
+
+ /**
+ * Accessibility Label for the work tab in the
+ * {@link com.android.internal.app.ResolverActivity).
+ */
+ public static final String RESOLVER_WORK_TAB_ACCESSIBILITY =
+ PREFIX + "RESOLVER_WORK_TAB_ACCESSIBILITY";
+
+ /**
+ * Title for resolver screen to let the user know that their IT admin doesn't allow
+ * them to share this content across profiles.
+ */
+ public static final String RESOLVER_CROSS_PROFILE_BLOCKED_TITLE =
+ PREFIX + "RESOLVER_CROSS_PROFILE_BLOCKED_TITLE";
+
+ /**
+ * Description for resolver screen to let the user know that their IT admin doesn't
+ * allow them to share this content with apps in their personal profile.
+ */
+ public static final String RESOLVER_CANT_SHARE_WITH_PERSONAL =
+ PREFIX + "RESOLVER_CANT_SHARE_WITH_PERSONAL";
+
+ /**
+ * Description for resolver screen to let the user know that their IT admin doesn't
+ * allow them to share this content with apps in their work profile.
+ */
+ public static final String RESOLVER_CANT_SHARE_WITH_WORK =
+ PREFIX + "RESOLVER_CANT_SHARE_WITH_WORK";
+
+ /**
+ * Description for resolver screen to let the user know that their IT admin doesn't
+ * allow them to open this specific content with an app in their personal profile.
+ */
+ public static final String RESOLVER_CANT_ACCESS_PERSONAL =
+ PREFIX + "RESOLVER_CANT_ACCESS_PERSONAL";
+
+ /**
+ * Description for resolver screen to let the user know that their IT admin doesn't
+ * allow them to open this specific content with an app in their work profile.
+ */
+ public static final String RESOLVER_CANT_ACCESS_WORK =
+ PREFIX + "RESOLVER_CANT_ACCESS_WORK";
+
+ /**
+ * Title for resolver screen to let the user know that they need to turn on work apps
+ * in order to share or open content
+ */
+ public static final String RESOLVER_WORK_PAUSED_TITLE =
+ PREFIX + "RESOLVER_WORK_PAUSED_TITLE";
+
+ /**
+ * Text on resolver screen to let the user know that their current work apps don't
+ * support the specific content.
+ */
+ public static final String RESOLVER_NO_WORK_APPS = PREFIX + "RESOLVER_NO_WORK_APPS";
+
+ /**
+ * Text on resolver screen to let the user know that their current personal apps don't
+ * support the specific content.
+ */
+ public static final String RESOLVER_NO_PERSONAL_APPS =
+ PREFIX + "RESOLVER_NO_PERSONAL_APPS";
+
+ /**
+ * Message informing user that the adding the account is disallowed by an administrator.
+ */
+ public static final String CANT_ADD_ACCOUNT_MESSAGE =
+ PREFIX + "CANT_ADD_ACCOUNT_MESSAGE";
+
+ /**
+ * Notification shown when device owner silently installs a package.
+ */
+ public static final String PACKAGE_INSTALLED_BY_DO = PREFIX + "PACKAGE_INSTALLED_BY_DO";
+
+ /**
+ * Notification shown when device owner silently updates a package.
+ */
+ public static final String PACKAGE_UPDATED_BY_DO = PREFIX + "PACKAGE_UPDATED_BY_DO";
+
+ /**
+ * Notification shown when device owner silently deleted a package.
+ */
+ public static final String PACKAGE_DELETED_BY_DO = PREFIX + "PACKAGE_DELETED_BY_DO";
+
+ /**
+ * Title for dialog shown when user tries to open a work app when the work profile is
+ * turned off, confirming that the user wants to turn on access to their
+ * work apps.
+ */
+ public static final String UNLAUNCHABLE_APP_WORK_PAUSED_TITLE =
+ PREFIX + "UNLAUNCHABLE_APP_WORK_PAUSED_TITLE";
+
+ /**
+ * Text for dialog shown when user tries to open a work app when the work profile is
+ * turned off, confirming that the user wants to turn on access to their
+ * work apps.
+ */
+ public static final String UNLAUNCHABLE_APP_WORK_PAUSED_MESSAGE =
+ PREFIX + "UNLAUNCHABLE_APP_WORK_PAUSED_MESSAGE";
+
+ /**
+ * Notification title shown when work profile is credential encrypted and requires
+ * the user to unlock before it's usable.
+ */
+ public static final String PROFILE_ENCRYPTED_TITLE = PREFIX + "PROFILE_ENCRYPTED_TITLE";
+
+ /**
+ * Notification detail shown when work profile is credential encrypted and requires
+ * the user to unlock before it's usable.
+ */
+ public static final String PROFILE_ENCRYPTED_DETAIL =
+ PREFIX + "PROFILE_ENCRYPTED_DETAIL";
+
+ /**
+ * Notification message shown when work profile is credential encrypted and requires
+ * the user to unlock before it's usable.
+ */
+ public static final String PROFILE_ENCRYPTED_MESSAGE =
+ PREFIX + "PROFILE_ENCRYPTED_MESSAGE";
+
+ /**
+ * Used to badge a string with "Work" for work profile content, e.g. "Work Email".
+ * Accepts the string to badge as an argument.
+ * <p>See {@link android.content.pm.PackageManager#getUserBadgedLabel}</p>
+ */
+ public static final String WORK_PROFILE_BADGED_LABEL =
+ PREFIX + "WORK_PROFILE_BADGED_LABEL";
+
+ /**
+ * @hide
+ */
+ static Set<String> buildStringsSet() {
+ Set<String> strings = new HashSet<>();
+ strings.add(WORK_PROFILE_DELETED_TITLE);
+ strings.add(WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE);
+ strings.add(WORK_PROFILE_DELETED_GENERIC_MESSAGE);
+ strings.add(WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE);
+ strings.add(PERSONAL_APP_SUSPENSION_TITLE);
+ strings.add(PERSONAL_APP_SUSPENSION_MESSAGE);
+ strings.add(PERSONAL_APP_SUSPENSION_SOON_MESSAGE);
+ strings.add(PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE);
+ strings.add(PRINTING_DISABLED_NAMED_ADMIN);
+ strings.add(LOCATION_CHANGED_TITLE);
+ strings.add(LOCATION_CHANGED_MESSAGE);
+ strings.add(NETWORK_LOGGING_TITLE);
+ strings.add(NETWORK_LOGGING_MESSAGE);
+ strings.add(NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION);
+ strings.add(NOTIFICATION_CHANNEL_DEVICE_ADMIN);
+ strings.add(SWITCH_TO_WORK_LABEL);
+ strings.add(SWITCH_TO_PERSONAL_LABEL);
+ strings.add(FORWARD_INTENT_TO_WORK);
+ strings.add(FORWARD_INTENT_TO_PERSONAL);
+ strings.add(RESOLVER_WORK_PROFILE_NOT_SUPPORTED);
+ strings.add(RESOLVER_PERSONAL_TAB);
+ strings.add(RESOLVER_WORK_TAB);
+ strings.add(RESOLVER_PERSONAL_TAB_ACCESSIBILITY);
+ strings.add(RESOLVER_WORK_TAB_ACCESSIBILITY);
+ strings.add(RESOLVER_CROSS_PROFILE_BLOCKED_TITLE);
+ strings.add(RESOLVER_CANT_SHARE_WITH_PERSONAL);
+ strings.add(RESOLVER_CANT_SHARE_WITH_WORK);
+ strings.add(RESOLVER_CANT_ACCESS_PERSONAL);
+ strings.add(RESOLVER_CANT_ACCESS_WORK);
+ strings.add(RESOLVER_WORK_PAUSED_TITLE);
+ strings.add(RESOLVER_NO_WORK_APPS);
+ strings.add(RESOLVER_NO_PERSONAL_APPS);
+ strings.add(CANT_ADD_ACCOUNT_MESSAGE);
+ strings.add(PACKAGE_INSTALLED_BY_DO);
+ strings.add(PACKAGE_UPDATED_BY_DO);
+ strings.add(PACKAGE_DELETED_BY_DO);
+ strings.add(UNLAUNCHABLE_APP_WORK_PAUSED_TITLE);
+ strings.add(UNLAUNCHABLE_APP_WORK_PAUSED_MESSAGE);
+ strings.add(PROFILE_ENCRYPTED_TITLE);
+ strings.add(PROFILE_ENCRYPTED_DETAIL);
+ strings.add(PROFILE_ENCRYPTED_MESSAGE);
+ strings.add(WORK_PROFILE_BADGED_LABEL);
+ return strings;
+ }
+ }
+
+ /**
+ * Class containing the identifiers used to update device management-related system strings
+ * in the DocumentsUi package.
+ */
+ public static final class DocumentsUi {
+
+ private DocumentsUi() {
+ }
+
+ private static final String PREFIX = "DocumentsUi.";
+
+ /**
+ * Title for error message shown when work profile is turned off.
+ */
+ public static final String WORK_PROFILE_OFF_ERROR_TITLE =
+ PREFIX + "WORK_PROFILE_OFF_ERROR_TITLE";
+
+ /**
+ * Button text shown when work profile is turned off.
+ */
+ public static final String WORK_PROFILE_OFF_ENABLE_BUTTON =
+ PREFIX + "WORK_PROFILE_OFF_ENABLE_BUTTON";
+
+ /**
+ * Title for error message shown when a user's IT admin does not allow the user to
+ * select work files from a personal app.
+ */
+ public static final String CANT_SELECT_WORK_FILES_TITLE =
+ PREFIX + "CANT_SELECT_WORK_FILES_TITLE";
+
+ /**
+ * Message shown when a user's IT admin does not allow the user to select work files
+ * from a personal app.
+ */
+ public static final String CANT_SELECT_WORK_FILES_MESSAGE =
+ PREFIX + "CANT_SELECT_WORK_FILES_MESSAGE";
+
+ /**
+ * Title for error message shown when a user's IT admin does not allow the user to
+ * select personal files from a work app.
+ */
+ public static final String CANT_SELECT_PERSONAL_FILES_TITLE =
+ PREFIX + "CANT_SELECT_PERSONAL_FILES_TITLE";
+
+ /**
+ * Message shown when a user's IT admin does not allow the user to select personal files
+ * from a work app.
+ */
+ public static final String CANT_SELECT_PERSONAL_FILES_MESSAGE =
+ PREFIX + "CANT_SELECT_PERSONAL_FILES_MESSAGE";
+
+ /**
+ * Title for error message shown when a user's IT admin does not allow the user to save
+ * files from their personal profile to their work profile.
+ */
+ public static final String CANT_SAVE_TO_WORK_TITLE =
+ PREFIX + "CANT_SAVE_TO_WORK_TITLE";
+
+ /**
+ * Message shown when a user's IT admin does not allow the user to save files from their
+ * personal profile to their work profile.
+ */
+ public static final String CANT_SAVE_TO_WORK_MESSAGE =
+ PREFIX + "CANT_SAVE_TO_WORK_MESSAGE";
+
+ /**
+ * Title for error message shown when a user's IT admin does not allow the user to save
+ * files from their work profile to their personal profile.
+ */
+ public static final String CANT_SAVE_TO_PERSONAL_TITLE =
+ PREFIX + "CANT_SAVE_TO_PERSONAL_TITLE";
+
+ /**
+ * Message shown when a user's IT admin does not allow the user to save files from their
+ * work profile to their personal profile.
+ */
+ public static final String CANT_SAVE_TO_PERSONAL_MESSAGE =
+ PREFIX + "CANT_SAVE_TO_PERSONAL_MESSAGE";
+
+ /**
+ * Title for error message shown when a user tries to do something on their work
+ * device, but that action isn't allowed by their IT admin.
+ */
+ public static final String CROSS_PROFILE_NOT_ALLOWED_TITLE =
+ PREFIX + "CROSS_PROFILE_NOT_ALLOWED_TITLE";
+
+ /**
+ * Message shown when a user tries to do something on their work device, but that action
+ * isn't allowed by their IT admin.
+ */
+ public static final String CROSS_PROFILE_NOT_ALLOWED_MESSAGE =
+ PREFIX + "CROSS_PROFILE_NOT_ALLOWED_MESSAGE";
+
+ /**
+ * Content description text that's spoken by a screen reader for previewing a work file
+ * before opening it. Accepts file name as a param.
+ */
+ public static final String PREVIEW_WORK_FILE_ACCESSIBILITY =
+ PREFIX + "PREVIEW_WORK_FILE_ACCESSIBILITY";
+
+ /**
+ * Label for tab and sidebar to indicate personal content.
+ */
+ public static final String PERSONAL_TAB = PREFIX + "PERSONAL_TAB";
+
+ /**
+ * Label for tab and sidebar tab to indicate work content
+ */
+ public static final String WORK_TAB = PREFIX + "WORK_TAB";
+
+ /**
+ * Accessibility label to indicate the subject(e.g. file/folder) is from work profile.
+ */
+ public static final String WORK_ACCESSIBILITY = PREFIX + "WORK_ACCESSIBILITY";
+
+ /**
+ * @hide
+ */
+ static Set<String> buildStringsSet() {
+ Set<String> strings = new HashSet<>();
+ strings.add(WORK_PROFILE_OFF_ERROR_TITLE);
+ strings.add(WORK_PROFILE_OFF_ENABLE_BUTTON);
+ strings.add(CANT_SELECT_WORK_FILES_TITLE);
+ strings.add(CANT_SELECT_WORK_FILES_MESSAGE);
+ strings.add(CANT_SELECT_PERSONAL_FILES_TITLE);
+ strings.add(CANT_SELECT_PERSONAL_FILES_MESSAGE);
+ strings.add(CANT_SAVE_TO_WORK_TITLE);
+ strings.add(CANT_SAVE_TO_WORK_MESSAGE);
+ strings.add(CANT_SAVE_TO_PERSONAL_TITLE);
+ strings.add(CANT_SAVE_TO_PERSONAL_MESSAGE);
+ strings.add(CROSS_PROFILE_NOT_ALLOWED_TITLE);
+ strings.add(CROSS_PROFILE_NOT_ALLOWED_MESSAGE);
+ strings.add(PREVIEW_WORK_FILE_ACCESSIBILITY);
+ strings.add(PERSONAL_TAB);
+ strings.add(WORK_TAB);
+ strings.add(WORK_ACCESSIBILITY);
+ return strings;
+ }
+ }
+
+ /**
+ * Class containing the identifiers used to update device management-related system strings
+ * in the MediaProvider module.
+ */
+ public static final class MediaProvider {
+
+ private MediaProvider() {
+ }
+
+ private static final String PREFIX = "MediaProvider.";
+
+ /**
+ * The text shown to switch to the work profile in PhotoPicker.
+ */
+ public static final String SWITCH_TO_WORK_MESSAGE =
+ PREFIX + "SWITCH_TO_WORK_MESSAGE";
+
+ /**
+ * The text shown to switch to the personal profile in PhotoPicker.
+ */
+ public static final String SWITCH_TO_PERSONAL_MESSAGE =
+ PREFIX + "SWITCH_TO_PERSONAL_MESSAGE";
+
+ /**
+ * The title for error dialog in PhotoPicker when the admin blocks cross user
+ * interaction for the intent.
+ */
+ public static final String BLOCKED_BY_ADMIN_TITLE =
+ PREFIX + "BLOCKED_BY_ADMIN_TITLE";
+
+ /**
+ * The message for error dialog in PhotoPicker when the admin blocks cross user
+ * interaction from the personal profile.
+ */
+ public static final String BLOCKED_FROM_PERSONAL_MESSAGE =
+ PREFIX + "BLOCKED_FROM_PERSONAL_MESSAGE";
+
+ /**
+ * The message for error dialog in PhotoPicker when the admin blocks cross user
+ * interaction from the work profile.
+ */
+ public static final String BLOCKED_FROM_WORK_MESSAGE =
+ PREFIX + "BLOCKED_FROM_WORK_MESSAGE";
+
+ /**
+ * The title of the error dialog in PhotoPicker when the user tries to switch to work
+ * content, but work profile is off.
+ */
+ public static final String WORK_PROFILE_PAUSED_TITLE =
+ PREFIX + "WORK_PROFILE_PAUSED_TITLE";
+
+ /**
+ * The message of the error dialog in PhotoPicker when the user tries to switch to work
+ * content, but work profile is off.
+ */
+ public static final String WORK_PROFILE_PAUSED_MESSAGE =
+ PREFIX + "WORK_PROFILE_PAUSED_MESSAGE";
+
+ /**
+ * @hide
+ */
+ static Set<String> buildStringsSet() {
+ Set<String> strings = new HashSet<>();
+ strings.add(SWITCH_TO_WORK_MESSAGE);
+ strings.add(SWITCH_TO_PERSONAL_MESSAGE);
+ strings.add(BLOCKED_BY_ADMIN_TITLE);
+ strings.add(BLOCKED_FROM_PERSONAL_MESSAGE);
+ strings.add(BLOCKED_FROM_WORK_MESSAGE);
+ strings.add(WORK_PROFILE_PAUSED_TITLE);
+ strings.add(WORK_PROFILE_PAUSED_MESSAGE);
+ return strings;
+ }
+ }
}
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index fae64d7..f663c17 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -533,6 +533,14 @@
boolean isUsbDataSignalingEnabledForUser(int userId);
boolean canUsbDataSignalingBeDisabled();
+ void setMinimumRequiredWifiSecurityLevel(int level);
+ int getMinimumRequiredWifiSecurityLevel();
+
+ void setSsidAllowlist(in List<String> ssids);
+ List<String> getSsidAllowlist();
+ void setSsidDenylist(in List<String> ssids);
+ List<String> getSsidDenylist();
+
List<UserHandle> listForegroundAffiliatedUsers();
void setDrawables(in List<DevicePolicyDrawableResource> drawables);
void resetDrawables(in int[] drawableIds);
diff --git a/core/java/android/app/admin/WifiSsidPolicy.java b/core/java/android/app/admin/WifiSsidPolicy.java
new file mode 100644
index 0000000..3715017
--- /dev/null
+++ b/core/java/android/app/admin/WifiSsidPolicy.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2022 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.admin;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Set;
+
+/**
+ * Used to indicate the Wi-Fi SSID restriction policy the network must satisfy
+ * in order to be eligible for a connection.
+ *
+ * If the policy type is a denylist, the device may not connect to networks on the denylist.
+ * If the policy type is an allowlist, the device may only connect to networks on the allowlist.
+ * Admin configured networks are not exempt from this restriction.
+ * This policy only prohibits connecting to a restricted network and
+ * does not affect adding a restricted network.
+ * If the current network is present in the denylist or not present in the allowlist,
+ * it will be disconnected.
+ */
+public final class WifiSsidPolicy implements Parcelable {
+ /**
+ * SSID policy type indicator for {@link WifiSsidPolicy}.
+ *
+ * <p> When returned from {@link WifiSsidPolicy#getPolicyType()}, the constant
+ * indicates that the SSID policy type is an allowlist.
+ *
+ * @see #WIFI_SSID_POLICY_TYPE_DENYLIST
+ */
+ public static final int WIFI_SSID_POLICY_TYPE_ALLOWLIST = 0;
+
+ /**
+ * SSID policy type indicator for {@link WifiSsidPolicy}.
+ *
+ * <p> When returned from {@link WifiSsidPolicy#getPolicyType()}, the constant
+ * indicates that the SSID policy type is a denylist.
+ *
+ * @see #WIFI_SSID_POLICY_TYPE_ALLOWLIST
+ */
+ public static final int WIFI_SSID_POLICY_TYPE_DENYLIST = 1;
+
+ /**
+ * Possible SSID policy types
+ *
+ * @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"WIFI_SSID_POLICY_TYPE_"}, value = {
+ WIFI_SSID_POLICY_TYPE_ALLOWLIST,
+ WIFI_SSID_POLICY_TYPE_DENYLIST})
+ public @interface WifiSsidPolicyType {}
+
+ private @WifiSsidPolicyType int mPolicyType;
+ private ArraySet<String> mSsids;
+
+ private WifiSsidPolicy(@WifiSsidPolicyType int policyType, @NonNull Set<String> ssids) {
+ mPolicyType = policyType;
+ mSsids = new ArraySet<>(ssids);
+ }
+
+ private WifiSsidPolicy(Parcel in) {
+ mPolicyType = in.readInt();
+ mSsids = (ArraySet<String>) in.readArraySet(null);
+ }
+ /**
+ * Create the allowlist Wi-Fi SSID Policy.
+ *
+ * @param ssids allowlist of SSIDs in UTF-8 without double quotes format
+ * @throws IllegalArgumentException if the input ssids list is empty
+ */
+ @NonNull
+ public static WifiSsidPolicy createAllowlistPolicy(@NonNull Set<String> ssids) {
+ if (ssids.isEmpty()) {
+ throw new IllegalArgumentException("SSID list cannot be empty");
+ }
+ return new WifiSsidPolicy(WIFI_SSID_POLICY_TYPE_ALLOWLIST, ssids);
+ }
+
+ /**
+ * Create the denylist Wi-Fi SSID Policy.
+ *
+ * @param ssids denylist of SSIDs in UTF-8 without double quotes format
+ * @throws IllegalArgumentException if the input ssids list is empty
+ */
+ @NonNull
+ public static WifiSsidPolicy createDenylistPolicy(@NonNull Set<String> ssids) {
+ if (ssids.isEmpty()) {
+ throw new IllegalArgumentException("SSID list cannot be empty");
+ }
+ return new WifiSsidPolicy(WIFI_SSID_POLICY_TYPE_DENYLIST, ssids);
+ }
+
+ /**
+ * Returns the set of SSIDs in UTF-8 without double quotes format.
+ */
+ @NonNull
+ public Set<String> getSsids() {
+ return mSsids;
+ }
+
+ /**
+ * Returns the policy type.
+ */
+ public @WifiSsidPolicyType int getPolicyType() {
+ return mPolicyType;
+ }
+
+ /**
+ * @see Parcelable.Creator
+ */
+ @NonNull
+ public static final Creator<WifiSsidPolicy> CREATOR = new Creator<WifiSsidPolicy>() {
+ @Override
+ public WifiSsidPolicy createFromParcel(Parcel source) {
+ return new WifiSsidPolicy(source);
+ }
+
+ @Override
+ public WifiSsidPolicy[] newArray(int size) {
+ return new WifiSsidPolicy[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mPolicyType);
+ dest.writeArraySet(mSsids);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/app/ambientcontext/AmbientContextEvent.aidl b/core/java/android/app/ambientcontext/AmbientContextEvent.aidl
new file mode 100644
index 0000000..0965b1a
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEvent.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 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.ambientcontext;
+
+parcelable AmbientContextEvent;
diff --git a/core/java/android/app/ambientcontext/AmbientContextEvent.java b/core/java/android/app/ambientcontext/AmbientContextEvent.java
new file mode 100644
index 0000000..11e695ad
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEvent.java
@@ -0,0 +1,492 @@
+/*
+ * Copyright (C) 2022 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.ambientcontext;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
+
+
+/**
+ * Represents a detected ambient event. Each event has a type, start time, end time,
+ * plus some optional data.
+ *
+ * @hide
+ */
+@SystemApi
+@DataClass(
+ genBuilder = true,
+ genConstructor = false,
+ genHiddenConstDefs = true,
+ genParcelable = true,
+ genToString = true
+)
+public final class AmbientContextEvent implements Parcelable {
+ /**
+ * The integer indicating an unknown event was detected.
+ */
+ public static final int EVENT_UNKNOWN = 0;
+
+ /**
+ * The integer indicating a cough event was detected.
+ */
+ public static final int EVENT_COUGH = 1;
+
+ /**
+ * The integer indicating a snore event was detected.
+ */
+ public static final int EVENT_SNORE = 2;
+
+ /** @hide */
+ @IntDef(prefix = { "EVENT_" }, value = {
+ EVENT_UNKNOWN,
+ EVENT_COUGH,
+ EVENT_SNORE,
+ }) public @interface EventCode {}
+
+ /** The integer indicating an unknown level. */
+ public static final int LEVEL_UNKNOWN = 0;
+
+ /** The integer indicating a low level. */
+ public static final int LEVEL_LOW = 1;
+
+ /** The integer indicating a medium low level. */
+ public static final int LEVEL_MEDIUM_LOW = 2;
+
+ /** The integer indicating a medium Level. */
+ public static final int LEVEL_MEDIUM = 3;
+
+ /** The integer indicating a medium high level. */
+ public static final int LEVEL_MEDIUM_HIGH = 4;
+
+ /** The integer indicating a high level. */
+ public static final int LEVEL_HIGH = 5;
+
+ /** @hide */
+ @IntDef(prefix = {"LEVEL_"}, value = {
+ LEVEL_UNKNOWN,
+ LEVEL_LOW,
+ LEVEL_MEDIUM_LOW,
+ LEVEL_MEDIUM,
+ LEVEL_MEDIUM_HIGH,
+ LEVEL_HIGH
+ }) public @interface LevelValue {}
+
+ @EventCode private final int mEventType;
+ private static int defaultEventType() {
+ return EVENT_UNKNOWN;
+ }
+
+ /** Event start time */
+ @DataClass.ParcelWith(Parcelling.BuiltIn.ForInstant.class)
+ @NonNull private final Instant mStartTime;
+ @NonNull private static Instant defaultStartTime() {
+ return Instant.MIN;
+ }
+
+ /** Event end time */
+ @DataClass.ParcelWith(Parcelling.BuiltIn.ForInstant.class)
+ @NonNull private final Instant mEndTime;
+ @NonNull private static Instant defaultEndTime() {
+ return Instant.MAX;
+ }
+
+ /**
+ * Confidence level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
+ * Apps can add post-processing filter using this value if needed.
+ */
+ @LevelValue private final int mConfidenceLevel;
+ private static int defaultConfidenceLevel() {
+ return LEVEL_UNKNOWN;
+ }
+
+ /**
+ * Density level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
+ * Apps can add post-processing filter using this value if needed.
+ */
+ @LevelValue private final int mDensityLevel;
+ private static int defaultDensityLevel() {
+ return LEVEL_UNKNOWN;
+ }
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/ambientcontext/AmbientContextEvent.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /** @hide */
+ @IntDef(prefix = "EVENT_", value = {
+ EVENT_UNKNOWN,
+ EVENT_COUGH,
+ EVENT_SNORE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @DataClass.Generated.Member
+ public @interface Event {}
+
+ /** @hide */
+ @DataClass.Generated.Member
+ public static String eventToString(@Event int value) {
+ switch (value) {
+ case EVENT_UNKNOWN:
+ return "EVENT_UNKNOWN";
+ case EVENT_COUGH:
+ return "EVENT_COUGH";
+ case EVENT_SNORE:
+ return "EVENT_SNORE";
+ default: return Integer.toHexString(value);
+ }
+ }
+
+ /** @hide */
+ @IntDef(prefix = "LEVEL_", value = {
+ LEVEL_UNKNOWN,
+ LEVEL_LOW,
+ LEVEL_MEDIUM_LOW,
+ LEVEL_MEDIUM,
+ LEVEL_MEDIUM_HIGH,
+ LEVEL_HIGH
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @DataClass.Generated.Member
+ public @interface Level {}
+
+ /** @hide */
+ @DataClass.Generated.Member
+ public static String levelToString(@Level int value) {
+ switch (value) {
+ case LEVEL_UNKNOWN:
+ return "LEVEL_UNKNOWN";
+ case LEVEL_LOW:
+ return "LEVEL_LOW";
+ case LEVEL_MEDIUM_LOW:
+ return "LEVEL_MEDIUM_LOW";
+ case LEVEL_MEDIUM:
+ return "LEVEL_MEDIUM";
+ case LEVEL_MEDIUM_HIGH:
+ return "LEVEL_MEDIUM_HIGH";
+ case LEVEL_HIGH:
+ return "LEVEL_HIGH";
+ default: return Integer.toHexString(value);
+ }
+ }
+
+ @DataClass.Generated.Member
+ /* package-private */ AmbientContextEvent(
+ @EventCode int eventType,
+ @NonNull Instant startTime,
+ @NonNull Instant endTime,
+ @LevelValue int confidenceLevel,
+ @LevelValue int densityLevel) {
+ this.mEventType = eventType;
+ com.android.internal.util.AnnotationValidations.validate(
+ EventCode.class, null, mEventType);
+ this.mStartTime = startTime;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mStartTime);
+ this.mEndTime = endTime;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mEndTime);
+ this.mConfidenceLevel = confidenceLevel;
+ com.android.internal.util.AnnotationValidations.validate(
+ LevelValue.class, null, mConfidenceLevel);
+ this.mDensityLevel = densityLevel;
+ com.android.internal.util.AnnotationValidations.validate(
+ LevelValue.class, null, mDensityLevel);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public @EventCode int getEventType() {
+ return mEventType;
+ }
+
+ /**
+ * Event start time
+ */
+ @DataClass.Generated.Member
+ public @NonNull Instant getStartTime() {
+ return mStartTime;
+ }
+
+ /**
+ * Event end time
+ */
+ @DataClass.Generated.Member
+ public @NonNull Instant getEndTime() {
+ return mEndTime;
+ }
+
+ /**
+ * Confidence level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
+ * Apps can add post-processing filter using this value if needed.
+ */
+ @DataClass.Generated.Member
+ public @LevelValue int getConfidenceLevel() {
+ return mConfidenceLevel;
+ }
+
+ /**
+ * Density level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
+ * Apps can add post-processing filter using this value if needed.
+ */
+ @DataClass.Generated.Member
+ public @LevelValue int getDensityLevel() {
+ return mDensityLevel;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "AmbientContextEvent { " +
+ "eventType = " + mEventType + ", " +
+ "startTime = " + mStartTime + ", " +
+ "endTime = " + mEndTime + ", " +
+ "confidenceLevel = " + mConfidenceLevel + ", " +
+ "densityLevel = " + mDensityLevel +
+ " }";
+ }
+
+ @DataClass.Generated.Member
+ static Parcelling<Instant> sParcellingForStartTime =
+ Parcelling.Cache.get(
+ Parcelling.BuiltIn.ForInstant.class);
+ static {
+ if (sParcellingForStartTime == null) {
+ sParcellingForStartTime = Parcelling.Cache.put(
+ new Parcelling.BuiltIn.ForInstant());
+ }
+ }
+
+ @DataClass.Generated.Member
+ static Parcelling<Instant> sParcellingForEndTime =
+ Parcelling.Cache.get(
+ Parcelling.BuiltIn.ForInstant.class);
+ static {
+ if (sParcellingForEndTime == null) {
+ sParcellingForEndTime = Parcelling.Cache.put(
+ new Parcelling.BuiltIn.ForInstant());
+ }
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeInt(mEventType);
+ sParcellingForStartTime.parcel(mStartTime, dest, flags);
+ sParcellingForEndTime.parcel(mEndTime, dest, flags);
+ dest.writeInt(mConfidenceLevel);
+ dest.writeInt(mDensityLevel);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ AmbientContextEvent(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int eventType = in.readInt();
+ Instant startTime = sParcellingForStartTime.unparcel(in);
+ Instant endTime = sParcellingForEndTime.unparcel(in);
+ int confidenceLevel = in.readInt();
+ int densityLevel = in.readInt();
+
+ this.mEventType = eventType;
+ com.android.internal.util.AnnotationValidations.validate(
+ EventCode.class, null, mEventType);
+ this.mStartTime = startTime;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mStartTime);
+ this.mEndTime = endTime;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mEndTime);
+ this.mConfidenceLevel = confidenceLevel;
+ com.android.internal.util.AnnotationValidations.validate(
+ LevelValue.class, null, mConfidenceLevel);
+ this.mDensityLevel = densityLevel;
+ com.android.internal.util.AnnotationValidations.validate(
+ LevelValue.class, null, mDensityLevel);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<AmbientContextEvent> CREATOR
+ = new Parcelable.Creator<AmbientContextEvent>() {
+ @Override
+ public AmbientContextEvent[] newArray(int size) {
+ return new AmbientContextEvent[size];
+ }
+
+ @Override
+ public AmbientContextEvent createFromParcel(@NonNull android.os.Parcel in) {
+ return new AmbientContextEvent(in);
+ }
+ };
+
+ /**
+ * A builder for {@link AmbientContextEvent}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static final class Builder {
+
+ private @EventCode int mEventType;
+ private @NonNull Instant mStartTime;
+ private @NonNull Instant mEndTime;
+ private @LevelValue int mConfidenceLevel;
+ private @LevelValue int mDensityLevel;
+
+ private long mBuilderFieldsSet = 0L;
+
+ public Builder() {
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Builder setEventType(@EventCode int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mEventType = value;
+ return this;
+ }
+
+ /**
+ * Event start time
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setStartTime(@NonNull Instant value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mStartTime = value;
+ return this;
+ }
+
+ /**
+ * Event end time
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setEndTime(@NonNull Instant value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mEndTime = value;
+ return this;
+ }
+
+ /**
+ * Confidence level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
+ * Apps can add post-processing filter using this value if needed.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setConfidenceLevel(@LevelValue int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8;
+ mConfidenceLevel = value;
+ return this;
+ }
+
+ /**
+ * Density level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
+ * Apps can add post-processing filter using this value if needed.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setDensityLevel(@LevelValue int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x10;
+ mDensityLevel = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull AmbientContextEvent build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x20; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x1) == 0) {
+ mEventType = defaultEventType();
+ }
+ if ((mBuilderFieldsSet & 0x2) == 0) {
+ mStartTime = defaultStartTime();
+ }
+ if ((mBuilderFieldsSet & 0x4) == 0) {
+ mEndTime = defaultEndTime();
+ }
+ if ((mBuilderFieldsSet & 0x8) == 0) {
+ mConfidenceLevel = defaultConfidenceLevel();
+ }
+ if ((mBuilderFieldsSet & 0x10) == 0) {
+ mDensityLevel = defaultDensityLevel();
+ }
+ AmbientContextEvent o = new AmbientContextEvent(
+ mEventType,
+ mStartTime,
+ mEndTime,
+ mConfidenceLevel,
+ mDensityLevel);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x20) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1642040319323L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/app/ambientcontext/AmbientContextEvent.java",
+ inputSignatures = "public static final int EVENT_UNKNOWN\npublic static final int EVENT_COUGH\npublic static final int EVENT_SNORE\npublic static final int LEVEL_UNKNOWN\npublic static final int LEVEL_LOW\npublic static final int LEVEL_MEDIUM_LOW\npublic static final int LEVEL_MEDIUM\npublic static final int LEVEL_MEDIUM_HIGH\npublic static final int LEVEL_HIGH\nprivate final @android.app.ambientcontext.AmbientContextEvent.EventCode int mEventType\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mStartTime\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mEndTime\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mConfidenceLevel\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mDensityLevel\nprivate static int defaultEventType()\nprivate static @android.annotation.NonNull java.time.Instant defaultStartTime()\nprivate static @android.annotation.NonNull java.time.Instant defaultEndTime()\nprivate static int defaultConfidenceLevel()\nprivate static int defaultDensityLevel()\nclass AmbientContextEvent extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=false, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/app/ambientcontext/AmbientContextEventRequest.aidl b/core/java/android/app/ambientcontext/AmbientContextEventRequest.aidl
new file mode 100644
index 0000000..e24c6ad
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEventRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 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.ambientcontext;
+
+parcelable AmbientContextEventRequest;
diff --git a/core/java/android/app/ambientcontext/AmbientContextEventRequest.java b/core/java/android/app/ambientcontext/AmbientContextEventRequest.java
new file mode 100644
index 0000000..82b16a2
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEventRequest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2022 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.ambientcontext;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.util.ArraySet;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Represents the request for ambient event detection.
+ *
+ * @hide
+ */
+@SystemApi
+public final class AmbientContextEventRequest implements Parcelable {
+ @NonNull private final Set<Integer> mEventTypes;
+ @NonNull private final PersistableBundle mOptions;
+
+ AmbientContextEventRequest(
+ @NonNull Set<Integer> eventTypes,
+ @NonNull PersistableBundle options) {
+ this.mEventTypes = eventTypes;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mEventTypes);
+ this.mOptions = options;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mOptions);
+ }
+
+ /**
+ * The event types to detect.
+ */
+ public @NonNull Set<Integer> getEventTypes() {
+ return mEventTypes;
+ }
+
+ /**
+ * Optional detection options.
+ */
+ public @NonNull PersistableBundle getOptions() {
+ return mOptions;
+ }
+
+ @Override
+ public String toString() {
+ return "AmbientContextEventRequest { " + "eventTypes = " + mEventTypes + ", "
+ + "options = " + mOptions + " }";
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeArraySet(new ArraySet<>(mEventTypes));
+ dest.writeTypedObject(mOptions, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ AmbientContextEventRequest(@NonNull Parcel in) {
+ Set<Integer> eventTypes = (Set<Integer>) in.readArraySet(Integer.class.getClassLoader());
+ PersistableBundle options = (PersistableBundle) in.readTypedObject(
+ PersistableBundle.CREATOR);
+
+ this.mEventTypes = eventTypes;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mEventTypes);
+ this.mOptions = options;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mOptions);
+ }
+
+ public static final @NonNull Parcelable.Creator<AmbientContextEventRequest> CREATOR =
+ new Parcelable.Creator<AmbientContextEventRequest>() {
+ @Override
+ public AmbientContextEventRequest[] newArray(int size) {
+ return new AmbientContextEventRequest[size];
+ }
+
+ @Override
+ public AmbientContextEventRequest createFromParcel(@NonNull Parcel in) {
+ return new AmbientContextEventRequest(in);
+ }
+ };
+
+ /**
+ * A builder for {@link AmbientContextEventRequest}
+ */
+ @SuppressWarnings("WeakerAccess")
+ public static final class Builder {
+ private @NonNull Set<Integer> mEventTypes;
+ private @NonNull PersistableBundle mOptions;
+
+ private long mBuilderFieldsSet = 0L;
+
+ public Builder() {
+ }
+
+ /**
+ * Add an event type to detect.
+ */
+ public @NonNull Builder addEventType(@AmbientContextEvent.EventCode int value) {
+ checkNotUsed();
+ if (mEventTypes == null) {
+ mBuilderFieldsSet |= 0x1;
+ mEventTypes = new HashSet<>();
+ }
+ mEventTypes.add(value);
+ return this;
+ }
+
+ /**
+ * Optional detection options.
+ */
+ public @NonNull Builder setOptions(@NonNull PersistableBundle value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mOptions = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull AmbientContextEventRequest build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x1) == 0) {
+ mEventTypes = new HashSet<>();
+ }
+ if ((mBuilderFieldsSet & 0x2) == 0) {
+ mOptions = new PersistableBundle();
+ }
+ AmbientContextEventRequest o = new AmbientContextEventRequest(
+ mEventTypes,
+ mOptions);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x4) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+}
diff --git a/core/java/android/app/ambientcontext/AmbientContextEventResponse.aidl b/core/java/android/app/ambientcontext/AmbientContextEventResponse.aidl
new file mode 100644
index 0000000..4dc6466
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEventResponse.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 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.ambientcontext;
+
+parcelable AmbientContextEventResponse;
\ No newline at end of file
diff --git a/core/java/android/app/ambientcontext/AmbientContextEventResponse.java b/core/java/android/app/ambientcontext/AmbientContextEventResponse.java
new file mode 100644
index 0000000..472a78b
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEventResponse.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2022 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.ambientcontext;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.PendingIntent;
+import android.os.Parcelable;
+
+import com.android.internal.util.AnnotationValidations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a response from the {@code AmbientContextEvent} service.
+ *
+ * @hide
+ */
+@SystemApi
+public final class AmbientContextEventResponse implements Parcelable {
+ /**
+ * An unknown status.
+ */
+ public static final int STATUS_UNKNOWN = 0;
+ /**
+ * The value of the status code that indicates success.
+ */
+ public static final int STATUS_SUCCESS = 1;
+ /**
+ * The value of the status code that indicates one or more of the
+ * requested events are not supported.
+ */
+ public static final int STATUS_NOT_SUPPORTED = 2;
+ /**
+ * The value of the status code that indicates service not available.
+ */
+ public static final int STATUS_SERVICE_UNAVAILABLE = 3;
+ /**
+ * The value of the status code that microphone is disabled.
+ */
+ public static final int STATUS_MICROPHONE_DISABLED = 4;
+ /**
+ * The value of the status code that the app is not granted access.
+ */
+ public static final int STATUS_ACCESS_DENIED = 5;
+
+ /** @hide */
+ @IntDef(prefix = { "STATUS_" }, value = {
+ STATUS_UNKNOWN,
+ STATUS_SUCCESS,
+ STATUS_NOT_SUPPORTED,
+ STATUS_SERVICE_UNAVAILABLE,
+ STATUS_MICROPHONE_DISABLED,
+ STATUS_ACCESS_DENIED
+ }) public @interface StatusCode {}
+
+ @StatusCode private final int mStatusCode;
+ @NonNull private final List<AmbientContextEvent> mEvents;
+ @NonNull private final String mPackageName;
+ @Nullable private final PendingIntent mActionPendingIntent;
+
+ /** @hide */
+ public static String statusToString(@StatusCode int value) {
+ switch (value) {
+ case STATUS_UNKNOWN:
+ return "STATUS_UNKNOWN";
+ case STATUS_SUCCESS:
+ return "STATUS_SUCCESS";
+ case STATUS_NOT_SUPPORTED:
+ return "STATUS_NOT_SUPPORTED";
+ case STATUS_SERVICE_UNAVAILABLE:
+ return "STATUS_SERVICE_UNAVAILABLE";
+ case STATUS_MICROPHONE_DISABLED:
+ return "STATUS_MICROPHONE_DISABLED";
+ case STATUS_ACCESS_DENIED:
+ return "STATUS_ACCESS_DENIED";
+ default: return Integer.toHexString(value);
+ }
+ }
+
+ AmbientContextEventResponse(
+ @StatusCode int statusCode,
+ @NonNull List<AmbientContextEvent> events,
+ @NonNull String packageName,
+ @Nullable PendingIntent actionPendingIntent) {
+ this.mStatusCode = statusCode;
+ AnnotationValidations.validate(StatusCode.class, null, mStatusCode);
+ this.mEvents = events;
+ AnnotationValidations.validate(NonNull.class, null, mEvents);
+ this.mPackageName = packageName;
+ AnnotationValidations.validate(NonNull.class, null, mPackageName);
+ this.mActionPendingIntent = actionPendingIntent;
+ }
+
+ /**
+ * The status of the response.
+ */
+ public @StatusCode int getStatusCode() {
+ return mStatusCode;
+ }
+
+ /**
+ * The detected event.
+ */
+ public @NonNull List<AmbientContextEvent> getEvents() {
+ return mEvents;
+ }
+
+ /**
+ * The package to deliver the response to.
+ */
+ public @NonNull String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * A {@link PendingIntent} that the client should call to allow further actions by user.
+ * For example, with {@link STATUS_ACCESS_DENIED}, the PendingIntent can redirect users to the
+ * grant access activity.
+ */
+ public @Nullable PendingIntent getActionPendingIntent() {
+ return mActionPendingIntent;
+ }
+
+ @Override
+ public String toString() {
+ return "AmbientContextEventResponse { " + "statusCode = " + mStatusCode + ", "
+ + "events = " + mEvents + ", " + "packageName = " + mPackageName + ", "
+ + "callbackPendingIntent = " + mActionPendingIntent + " }";
+ }
+
+ @Override
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ byte flg = 0;
+ if (mActionPendingIntent != null) flg |= 0x8;
+ dest.writeByte(flg);
+ dest.writeInt(mStatusCode);
+ dest.writeParcelableList(mEvents, flags);
+ dest.writeString(mPackageName);
+ if (mActionPendingIntent != null) dest.writeTypedObject(mActionPendingIntent, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ AmbientContextEventResponse(@NonNull android.os.Parcel in) {
+ byte flg = in.readByte();
+ int statusCode = in.readInt();
+ List<AmbientContextEvent> events = new ArrayList<>();
+ in.readParcelableList(events, AmbientContextEvent.class.getClassLoader(),
+ AmbientContextEvent.class);
+ String packageName = in.readString();
+ PendingIntent callbackPendingIntent = (flg & 0x8) == 0 ? null
+ : (PendingIntent) in.readTypedObject(PendingIntent.CREATOR);
+
+ this.mStatusCode = statusCode;
+ AnnotationValidations.validate(
+ StatusCode.class, null, mStatusCode);
+ this.mEvents = events;
+ AnnotationValidations.validate(
+ NonNull.class, null, mEvents);
+ this.mPackageName = packageName;
+ AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ this.mActionPendingIntent = callbackPendingIntent;
+ }
+
+ public static final @NonNull Parcelable.Creator<AmbientContextEventResponse> CREATOR =
+ new Parcelable.Creator<AmbientContextEventResponse>() {
+ @Override
+ public AmbientContextEventResponse[] newArray(int size) {
+ return new AmbientContextEventResponse[size];
+ }
+
+ @Override
+ public AmbientContextEventResponse createFromParcel(@NonNull android.os.Parcel in) {
+ return new AmbientContextEventResponse(in);
+ }
+ };
+
+ /**
+ * A builder for {@link AmbientContextEventResponse}
+ */
+ @SuppressWarnings("WeakerAccess")
+ public static final class Builder {
+ private @StatusCode int mStatusCode;
+ private @NonNull List<AmbientContextEvent> mEvents;
+ private @NonNull String mPackageName;
+ private @Nullable PendingIntent mCallbackPendingIntent;
+ private long mBuilderFieldsSet = 0L;
+
+ public Builder() {
+ }
+
+ /**
+ * The status of the response.
+ */
+ public @NonNull Builder setStatusCode(@StatusCode int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mStatusCode = value;
+ return this;
+ }
+
+ /**
+ * Adds an event to the builder.
+ */
+ public @NonNull Builder addEvent(@NonNull AmbientContextEvent value) {
+ checkNotUsed();
+ if (mEvents == null) {
+ mBuilderFieldsSet |= 0x2;
+ mEvents = new ArrayList<>();
+ }
+ mEvents.add(value);
+ return this;
+ }
+
+ /**
+ * The package to deliver the response to.
+ */
+ public @NonNull Builder setPackageName(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mPackageName = value;
+ return this;
+ }
+
+ /**
+ * A {@link PendingIntent} that the client should call to allow further actions by user.
+ * For example, with {@link STATUS_ACCESS_DENIED}, the PendingIntent can redirect users to
+ * the grant access activity.
+ */
+ public @NonNull Builder setActionPendingIntent(@NonNull PendingIntent value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8;
+ mCallbackPendingIntent = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull AmbientContextEventResponse build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x10; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x1) == 0) {
+ mStatusCode = STATUS_UNKNOWN;
+ }
+ if ((mBuilderFieldsSet & 0x2) == 0) {
+ mEvents = new ArrayList<>();
+ }
+ if ((mBuilderFieldsSet & 0x4) == 0) {
+ mPackageName = "";
+ }
+ if ((mBuilderFieldsSet & 0x8) == 0) {
+ mCallbackPendingIntent = null;
+ }
+ AmbientContextEventResponse o = new AmbientContextEventResponse(
+ mStatusCode,
+ mEvents,
+ mPackageName,
+ mCallbackPendingIntent);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x10) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+}
diff --git a/core/java/android/app/ambientcontext/AmbientContextManager.java b/core/java/android/app/ambientcontext/AmbientContextManager.java
new file mode 100644
index 0000000..6841d1b
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextManager.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2022 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.ambientcontext;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Allows granted apps to register for particular pre-defined {@link AmbientContextEvent}s.
+ * After successful registration, the app receives a callback on the provided {@link PendingIntent}
+ * when the requested event is detected.
+ * <p />
+ *
+ * Example:
+ *
+ * <pre><code>
+ * // Create request
+ * AmbientContextEventRequest request = new AmbientContextEventRequest.Builder()
+ * .addEventType(AmbientContextEvent.EVENT_COUGH)
+ * .addEventTYpe(AmbientContextEvent.EVENT_SNORE)
+ * .build();
+ * // Create PendingIntent
+ * Intent intent = new Intent(actionString, null, context, MyBroadcastReceiver.class)
+ * .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ * PendingIntent pendingIntent = PendingIntents.getBroadcastMutable(context, 0, intent, 0);
+ * // Register for events
+ * AmbientContextManager ambientContextManager =
+ * context.getSystemService(AmbientContextManager.class);
+ * ambientContextManager.registerObserver(request, pendingIntent);
+ *
+ * // Handle the callback intent in your receiver
+ * {@literal @}Override
+ * protected void onReceive(Context context, Intent intent) {
+ * AmbientContextEventResponse response =
+ * AmbientContextManager.getResponseFromIntent(intent);
+ * if (response != null) {
+ * if (response.getStatusCode() == AmbientContextEventResponse.STATUS_SUCCESS) {
+ * // Do something useful with response.getEvent()
+ * } else if (response.getStatusCode() == AmbientContextEventResponse.STATUS_ACCESS_DENIED) {
+ * // Redirect users to grant access
+ * PendingIntent callbackPendingIntent = response.getCallbackPendingIntent();
+ * if (callbackPendingIntent != null) {
+ * callbackPendingIntent.send();
+ * }
+ * } else ...
+ * }
+ * }
+ * </code></pre>
+ *
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.AMBIENT_CONTEXT_SERVICE)
+public final class AmbientContextManager {
+
+ /**
+ * The key of an Intent extra indicating the response.
+ */
+ public static final String EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE =
+ "android.app.ambientcontext.extra.AMBIENT_CONTEXT_EVENT_RESPONSE";
+
+ /**
+ * Allows clients to retrieve the response from the intent.
+ * @param intent received from the PendingIntent callback
+ *
+ * @return the AmbientContextEventResponse, or null if not present
+ */
+ @Nullable
+ public static AmbientContextEventResponse getResponseFromIntent(
+ @NonNull Intent intent) {
+ if (intent.hasExtra(AmbientContextManager.EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE)) {
+ return intent.getParcelableExtra(EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE);
+ } else {
+ return null;
+ }
+ }
+
+ private final Context mContext;
+ private final IAmbientContextEventObserver mService;
+
+ /**
+ * {@hide}
+ */
+ public AmbientContextManager(Context context, IAmbientContextEventObserver service) {
+ mContext = context;
+ mService = service;
+ }
+
+ /**
+ * Allows app to register as a {@link AmbientContextEvent} observer. The
+ * observer receives a callback on the provided {@link PendingIntent} when the requested
+ * event is detected. Registering another observer from the same package that has already been
+ * registered will override the previous observer.
+ *
+ * @param request The request with events to observe.
+ * @param pendingIntent A mutable {@link PendingIntent} that will be dispatched when any
+ * requested event is detected.
+ */
+ @RequiresPermission(Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT)
+ public void registerObserver(
+ @NonNull AmbientContextEventRequest request,
+ @NonNull PendingIntent pendingIntent) {
+ Preconditions.checkArgument(!pendingIntent.isImmutable());
+ try {
+ mService.registerObserver(request, pendingIntent);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unregisters the requesting app as an {@code AmbientContextEvent} observer. Unregistering an
+ * observer that was already unregistered or never registered will have no effect.
+ */
+ @RequiresPermission(Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT)
+ public void unregisterObserver() {
+ try {
+ mService.unregisterObserver(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/app/ambientcontext/IAmbientContextEventObserver.aidl b/core/java/android/app/ambientcontext/IAmbientContextEventObserver.aidl
new file mode 100644
index 0000000..9032fe1
--- /dev/null
+++ b/core/java/android/app/ambientcontext/IAmbientContextEventObserver.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2021 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.ambientcontext;
+
+import android.app.PendingIntent;
+import android.app.ambientcontext.AmbientContextEventRequest;
+
+/**
+ * Interface for an AmbientContextEventManager that provides access to AmbientContextEvents.
+ *
+ * @hide
+ */
+oneway interface IAmbientContextEventObserver {
+ void registerObserver(in AmbientContextEventRequest request, in PendingIntent pendingIntent);
+ void unregisterObserver(in String callingPackage);
+}
\ No newline at end of file
diff --git a/core/java/android/app/ambientcontext/OWNERS b/core/java/android/app/ambientcontext/OWNERS
new file mode 100644
index 0000000..a863297
--- /dev/null
+++ b/core/java/android/app/ambientcontext/OWNERS
@@ -0,0 +1,3 @@
+enxun@google.com
+kxchen@google.com
+tgadh@google.com
diff --git a/core/java/android/app/wallpapereffectsgeneration/OWNERS b/core/java/android/app/wallpapereffectsgeneration/OWNERS
new file mode 100644
index 0000000..2bc0154
--- /dev/null
+++ b/core/java/android/app/wallpapereffectsgeneration/OWNERS
@@ -0,0 +1,5 @@
+susharon@google.com
+shanh@google.com
+huiwu@google.com
+srazdan@google.com
+
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 845d23c..ce2efcf 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -41,6 +41,7 @@
import android.app.IApplicationThread;
import android.app.IServiceConnection;
import android.app.VrManager;
+import android.app.ambientcontext.AmbientContextManager;
import android.app.people.PeopleManager;
import android.app.time.TimeManager;
import android.compat.annotation.UnsupportedAppUsage;
@@ -5931,6 +5932,17 @@
public static final String NEARBY_SERVICE = "nearby";
/**
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.app.ambientcontext.AmbientContextManager}.
+ *
+ * @see #getSystemService(String)
+ * @see AmbientContextManager
+ * @hide
+ */
+ @SystemApi
+ public static final String AMBIENT_CONTEXT_SERVICE = "ambient_context";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 11b2ea1..1e88758 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -15,6 +15,9 @@
*/
package android.content.pm;
+import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_PERSONAL_LABEL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_WORK_LABEL;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -22,6 +25,7 @@
import android.annotation.TestApi;
import android.app.Activity;
import android.app.AppOpsManager.Mode;
+import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -241,12 +245,24 @@
public @NonNull CharSequence getProfileSwitchingLabel(@NonNull UserHandle userHandle) {
verifyCanAccessUser(userHandle);
- final int stringRes = mUserManager.isManagedProfile(userHandle.getIdentifier())
- ? R.string.managed_profile_label
- : R.string.user_owner_label;
+ final boolean isManagedProfile = mUserManager.isManagedProfile(userHandle.getIdentifier());
+ final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(
+ getUpdatableProfileSwitchingLabelId(isManagedProfile),
+ () -> getDefaultProfileSwitchingLabel(isManagedProfile));
+ }
+
+ private String getUpdatableProfileSwitchingLabelId(boolean isManagedProfile) {
+ return isManagedProfile ? SWITCH_TO_WORK_LABEL : SWITCH_TO_PERSONAL_LABEL;
+ }
+
+ private String getDefaultProfileSwitchingLabel(boolean isManagedProfile) {
+ final int stringRes = isManagedProfile
+ ? R.string.managed_profile_label : R.string.user_owner_label;
return mResources.getString(stringRes);
}
+
/**
* Return a drawable that calling app can show to user for the semantic of profile switching --
* launching its own activity in specified user profile. For example, it may return a briefcase
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index c8f88f2..1021d3e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4077,6 +4077,14 @@
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_DREAM_OVERLAY = "android.software.dream_overlay";
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
+ * supports window magnification.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_WINDOW_MAGNIFICATION =
+ "android.software.window_magnification";
+
/** @hide */
public static final boolean APP_ENUMERATION_ENABLED_BY_DEFAULT = true;
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index 7f07af7..1d0837e 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -18,6 +18,7 @@
import android.app.PendingIntent;
import android.content.ComponentName;
+import android.hardware.usb.IUsbOperationInternal;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.ParcelableUsbPort;
@@ -136,7 +137,7 @@
void resetUsbGadget();
/* Set USB data on or off */
- boolean enableUsbDataSignal(boolean enable);
+ boolean enableUsbData(in String portId, boolean enable, int operationId, in IUsbOperationInternal callback);
/* Gets the USB Hal Version. */
int getUsbHalVersion();
@@ -156,9 +157,14 @@
/* Sets the port's current role. */
void setPortRoles(in String portId, int powerRole, int dataRole);
+ /* Limit power transfer in & out of the port within the allowed limit by the USB
+ * specification.
+ */
+ void enableLimitPowerTransfer(in String portId, boolean limit, int operationId, in IUsbOperationInternal callback);
+
/* Enable/disable contaminant detection */
void enableContaminantDetection(in String portId, boolean enable);
- /* Sets USB device connection handler. */
- void setUsbDeviceConnectionHandler(in ComponentName usbDeviceConnectionHandler);
+ /* Sets USB device connection handler. */
+ void setUsbDeviceConnectionHandler(in ComponentName usbDeviceConnectionHandler);
}
diff --git a/core/java/android/hardware/usb/IUsbOperationInternal.aidl b/core/java/android/hardware/usb/IUsbOperationInternal.aidl
new file mode 100644
index 0000000..3f3bbf6
--- /dev/null
+++ b/core/java/android/hardware/usb/IUsbOperationInternal.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2021 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.hardware.usb;
+
+/**
+ * @hide
+ */
+oneway interface IUsbOperationInternal {
+void onOperationComplete(in int status);
+}
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index c29a948..eb3e84d 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -36,6 +36,8 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.hardware.usb.gadget.V1_0.GadgetFunction;
import android.hardware.usb.gadget.V1_2.UsbSpeed;
+import android.hardware.usb.IUsbOperationInternal;
+import android.hardware.usb.UsbPort;
import android.os.Build;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
@@ -48,6 +50,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.StringJoiner;
/**
@@ -517,6 +520,14 @@
public static final int USB_DATA_TRANSFER_RATE_40G = 40 * 1024;
/**
+ * Returned when the client has to retry querying the version.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int USB_HAL_RETRY = -2;
+
+ /**
* The Value for USB hal is not presented.
*
* {@hide}
@@ -557,6 +568,14 @@
public static final int USB_HAL_V1_3 = 13;
/**
+ * Value for USB Hal Version v2.0.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int USB_HAL_V2_0 = 20;
+
+ /**
* Code for the charging usb function. Passed into {@link #setCurrentFunctions(long)}
* {@hide}
*/
@@ -665,6 +684,7 @@
USB_HAL_V1_1,
USB_HAL_V1_2,
USB_HAL_V1_3,
+ USB_HAL_V2_0,
})
public @interface UsbHalVersion {}
@@ -1169,8 +1189,9 @@
/**
* Enable/Disable the USB data signaling.
* <p>
- * Enables/Disables USB data path in all the USB ports.
+ * Enables/Disables USB data path of the first port..
* It will force to stop or restore USB data signaling.
+ * Call UsbPort API if the device has more than one UsbPort.
* </p>
*
* @param enable enable or disable USB data signaling
@@ -1181,11 +1202,11 @@
*/
@RequiresPermission(Manifest.permission.MANAGE_USB)
public boolean enableUsbDataSignal(boolean enable) {
- try {
- return mService.enableUsbDataSignal(enable);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ List<UsbPort> usbPorts = getPorts();
+ if (usbPorts.size() == 1) {
+ return usbPorts.get(0).enableUsbData(enable) == UsbPort.ENABLE_USB_DATA_SUCCESS;
}
+ return false;
}
/**
@@ -1271,6 +1292,73 @@
}
/**
+ * Should only be called by {@link UsbPort#enableLimitPowerTransfer}.
+ * <p>
+ * limits or restores power transfer in and out of USB port.
+ *
+ * @param port USB port for which power transfer has to be limited or restored.
+ * @param limit limit power transfer when true.
+ * relax power transfer restrictions when false.
+ * @param operationId operationId for the request.
+ * @param callback callback object to be invoked when the operation is complete.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ void enableLimitPowerTransfer(@NonNull UsbPort port, boolean limit, int operationId,
+ IUsbOperationInternal callback) {
+ Objects.requireNonNull(port, "enableLimitPowerTransfer:port must not be null. opId:"
+ + operationId);
+ try {
+ mService.enableLimitPowerTransfer(port.getId(), limit, operationId, callback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "enableLimitPowerTransfer failed. opId:" + operationId, e);
+ try {
+ callback.onOperationComplete(UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL);
+ } catch (RemoteException r) {
+ Log.e(TAG, "enableLimitPowerTransfer failed to call onOperationComplete. opId:"
+ + operationId, r);
+ }
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Should only be called by {@link UsbPort#enableUsbData}.
+ * <p>
+ * Enables or disables USB data on the specific port.
+ *
+ * @param port USB port for which USB data needs to be enabled or disabled.
+ * @param enable Enable USB data when true.
+ * Disable USB data when false.
+ * @param operationId operationId for the request.
+ * @param callback callback object to be invoked when the operation is complete.
+ * @return True when the operation is asynchronous. The caller must therefore call
+ * {@link UsbOperationInternal#waitForOperationComplete} for processing
+ * the result.
+ * False when the operation is synchronous. Caller can proceed reading the result
+ * through {@link UsbOperationInternal#getStatus}
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ boolean enableUsbData(@NonNull UsbPort port, boolean enable, int operationId,
+ IUsbOperationInternal callback) {
+ Objects.requireNonNull(port, "enableUsbData: port must not be null. opId:" + operationId);
+ try {
+ return mService.enableUsbData(port.getId(), enable, operationId, callback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "enableUsbData: failed. opId:" + operationId, e);
+ try {
+ callback.onOperationComplete(UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL);
+ } catch (RemoteException r) {
+ Log.e(TAG, "enableUsbData: failed to call onOperationComplete. opId:"
+ + operationId, r);
+ }
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Sets the component that will handle USB device connection.
* <p>
* Setting component allows to specify external USB host manager to handle use cases, where
diff --git a/core/java/android/hardware/usb/UsbOperationInternal.java b/core/java/android/hardware/usb/UsbOperationInternal.java
new file mode 100644
index 0000000..9bc2b38
--- /dev/null
+++ b/core/java/android/hardware/usb/UsbOperationInternal.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2022 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.hardware.usb;
+
+import android.annotation.IntDef;
+import android.hardware.usb.IUsbOperationInternal;
+import android.hardware.usb.UsbPort;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.TimeUnit;
+/**
+ * UsbOperationInternal allows UsbPort to support both synchronous and
+ * asynchronous function irrespective of whether the underlying hal
+ * method is synchronous or asynchronous.
+ *
+ * @hide
+ */
+public final class UsbOperationInternal extends IUsbOperationInternal.Stub {
+ private static final String TAG = "UsbPortStatus";
+ private final int mOperationID;
+ // Cached portId.
+ private final String mId;
+ // True implies operation did not timeout.
+ private boolean mOperationComplete;
+ private @UsbOperationStatus int mStatus;
+ final ReentrantLock mLock = new ReentrantLock();
+ final Condition mOperationWait = mLock.newCondition();
+ // Maximum time the caller has to wait for onOperationComplete to be called.
+ private static final int USB_OPERATION_TIMEOUT_MSECS = 5000;
+
+ /**
+ * The requested operation was successfully completed.
+ * Returned in {@link onOperationComplete} and {@link getStatus}.
+ */
+ public static final int USB_OPERATION_SUCCESS = 0;
+
+ /**
+ * The requested operation failed due to internal error.
+ * Returned in {@link onOperationComplete} and {@link getStatus}.
+ */
+ public static final int USB_OPERATION_ERROR_INTERNAL = 1;
+
+ /**
+ * The requested operation failed as it's not supported.
+ * Returned in {@link onOperationComplete} and {@link getStatus}.
+ */
+ public static final int USB_OPERATION_ERROR_NOT_SUPPORTED = 2;
+
+ /**
+ * The requested operation failed as it's not supported.
+ * Returned in {@link onOperationComplete} and {@link getStatus}.
+ */
+ public static final int USB_OPERATION_ERROR_PORT_MISMATCH = 3;
+
+ @IntDef(prefix = { "USB_OPERATION_" }, value = {
+ USB_OPERATION_SUCCESS,
+ USB_OPERATION_ERROR_INTERNAL,
+ USB_OPERATION_ERROR_NOT_SUPPORTED,
+ USB_OPERATION_ERROR_PORT_MISMATCH
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface UsbOperationStatus{}
+
+ UsbOperationInternal(int operationID, String id) {
+ this.mOperationID = operationID;
+ this.mId = id;
+ }
+
+ /**
+ * Hal glue layer would directly call this function when the requested
+ * operation is complete.
+ */
+ @Override
+ public void onOperationComplete(@UsbOperationStatus int status) {
+ mLock.lock();
+ try {
+ mOperationComplete = true;
+ mStatus = status;
+ Log.i(TAG, "Port:" + mId + " opID:" + mOperationID + " status:" + mStatus);
+ mOperationWait.signal();
+ } finally {
+ mLock.unlock();
+ }
+ }
+
+ /**
+ * Caller invokes this function to wait for the operation to be complete.
+ */
+ public void waitForOperationComplete() {
+ mLock.lock();
+ try {
+ long now = System.currentTimeMillis();
+ long deadline = now + USB_OPERATION_TIMEOUT_MSECS;
+ // Wait in loop to overcome spurious wakeups.
+ do {
+ mOperationWait.await(deadline - System.currentTimeMillis(),
+ TimeUnit.MILLISECONDS);
+ } while (!mOperationComplete && System.currentTimeMillis() < deadline);
+ if (!mOperationComplete) {
+ Log.e(TAG, "Port:" + mId + " opID:" + mOperationID
+ + " operationComplete not received in " + USB_OPERATION_TIMEOUT_MSECS
+ + "msecs");
+ }
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Port:" + mId + " opID:" + mOperationID + " operationComplete interrupted");
+ } finally {
+ mLock.unlock();
+ }
+ }
+
+ public @UsbOperationStatus int getStatus() {
+ return mOperationComplete ? mStatus : USB_OPERATION_ERROR_INTERNAL;
+ }
+}
diff --git a/core/java/android/hardware/usb/UsbPort.java b/core/java/android/hardware/usb/UsbPort.java
index 274e23f..e908c24 100644
--- a/core/java/android/hardware/usb/UsbPort.java
+++ b/core/java/android/hardware/usb/UsbPort.java
@@ -16,6 +16,10 @@
package android.hardware.usb;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_NOT_SUPPORTED;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_PORT_MISMATCH;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_SUCCESS;
import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_DETECTED;
import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_DISABLED;
import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_DETECTED;
@@ -34,15 +38,23 @@
import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
import android.Manifest;
+import android.annotation.CheckResult;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.hardware.usb.UsbOperationInternal;
import android.hardware.usb.V1_0.Constants;
+import android.os.Binder;
+import android.util.Log;
import com.android.internal.util.Preconditions;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Represents a physical USB port and describes its characteristics.
@@ -51,6 +63,7 @@
*/
@SystemApi
public final class UsbPort {
+ private static final String TAG = "UsbPort";
private final String mId;
private final int mSupportedModes;
private final UsbManager mUsbManager;
@@ -64,6 +77,83 @@
*/
private static final int POWER_ROLE_OFFSET = Constants.PortPowerRole.NONE;
+ /**
+ * Counter for tracking UsbOperation operations.
+ */
+ private static final AtomicInteger sUsbOperationCount = new AtomicInteger();
+
+ /**
+ * The {@link #enableUsbData} request was successfully completed.
+ */
+ public static final int ENABLE_USB_DATA_SUCCESS = 0;
+
+ /**
+ * The {@link #enableUsbData} request failed due to internal error.
+ */
+ public static final int ENABLE_USB_DATA_ERROR_INTERNAL = 1;
+
+ /**
+ * The {@link #enableUsbData} request failed as it's not supported.
+ */
+ public static final int ENABLE_USB_DATA_ERROR_NOT_SUPPORTED = 2;
+
+ /**
+ * The {@link #enableUsbData} request failed as port id mismatched.
+ */
+ public static final int ENABLE_USB_DATA_ERROR_PORT_MISMATCH = 3;
+
+ /**
+ * The {@link #enableUsbData} request failed due to other reasons.
+ */
+ public static final int ENABLE_USB_DATA_ERROR_OTHER = 4;
+
+ /** @hide */
+ @IntDef(prefix = { "ENABLE_USB_DATA_" }, value = {
+ ENABLE_USB_DATA_SUCCESS,
+ ENABLE_USB_DATA_ERROR_INTERNAL,
+ ENABLE_USB_DATA_ERROR_NOT_SUPPORTED,
+ ENABLE_USB_DATA_ERROR_PORT_MISMATCH,
+ ENABLE_USB_DATA_ERROR_OTHER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface EnableUsbDataStatus{}
+
+ /**
+ * The {@link #enableLimitPowerTransfer} request was successfully completed.
+ */
+ public static final int ENABLE_LIMIT_POWER_TRANSFER_SUCCESS = 0;
+
+ /**
+ * The {@link #enableLimitPowerTransfer} request failed due to internal error.
+ */
+ public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_INTERNAL = 1;
+
+ /**
+ * The {@link #enableLimitPowerTransfer} request failed as it's not supported.
+ */
+ public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_NOT_SUPPORTED = 2;
+
+ /**
+ * The {@link #enableLimitPowerTransfer} request failed as port id mismatched.
+ */
+ public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_PORT_MISMATCH = 3;
+
+ /**
+ * The {@link #enableLimitPowerTransfer} request failed due to other reasons.
+ */
+ public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_OTHER = 4;
+
+ /** @hide */
+ @IntDef(prefix = { "ENABLE_LIMIT_POWER_TRANSFER_" }, value = {
+ ENABLE_LIMIT_POWER_TRANSFER_SUCCESS,
+ ENABLE_LIMIT_POWER_TRANSFER_ERROR_INTERNAL,
+ ENABLE_LIMIT_POWER_TRANSFER_ERROR_NOT_SUPPORTED,
+ ENABLE_LIMIT_POWER_TRANSFER_ERROR_PORT_MISMATCH,
+ ENABLE_LIMIT_POWER_TRANSFER_ERROR_OTHER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface EnableLimitPowerTransferStatus{}
+
/** @hide */
public UsbPort(@NonNull UsbManager usbManager, @NonNull String id, int supportedModes,
int supportedContaminantProtectionModes,
@@ -157,7 +247,7 @@
* {@link UsbPortStatus#isRoleCombinationSupported UsbPortStatus.isRoleCombinationSupported}.
* </p><p>
* Note: This function is asynchronous and may fail silently without applying
- * the requested changes. If this function does cause a status change to occur then
+ * the operationed changes. If this function does cause a status change to occur then
* a {@link UsbManager#ACTION_USB_PORT_CHANGED} broadcast will be sent.
* </p>
*
@@ -177,6 +267,88 @@
}
/**
+ * Enables/Disables Usb data on the port.
+ *
+ * @param enable When true enables USB data if disabled.
+ * When false disables USB data if enabled.
+ * @return {@link #ENABLE_USB_DATA_SUCCESS} when request completes successfully or
+ * {@link #ENABLE_USB_DATA_ERROR_INTERNAL} when request fails due to internal
+ * error or
+ * {@link ENABLE_USB_DATA_ERROR_NOT_SUPPORTED} when not supported or
+ * {@link ENABLE_USB_DATA_ERROR_PORT_MISMATCH} when request fails due to port id
+ * mismatch or
+ * {@link ENABLE_USB_DATA_ERROR_OTHER} when fails due to other reasons.
+ */
+ @CheckResult
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ public @EnableUsbDataStatus int enableUsbData(boolean enable) {
+ // UID is added To minimize operationID overlap between two different packages.
+ int operationId = sUsbOperationCount.incrementAndGet() + Binder.getCallingUid();
+ Log.i(TAG, "enableUsbData opId:" + operationId
+ + " callingUid:" + Binder.getCallingUid());
+ UsbOperationInternal opCallback =
+ new UsbOperationInternal(operationId, mId);
+ if (mUsbManager.enableUsbData(this, enable, operationId, opCallback) == true) {
+ opCallback.waitForOperationComplete();
+ }
+
+ int result = opCallback.getStatus();
+ switch (result) {
+ case USB_OPERATION_SUCCESS:
+ return ENABLE_USB_DATA_SUCCESS;
+ case USB_OPERATION_ERROR_INTERNAL:
+ return ENABLE_USB_DATA_ERROR_INTERNAL;
+ case USB_OPERATION_ERROR_NOT_SUPPORTED:
+ return ENABLE_USB_DATA_ERROR_NOT_SUPPORTED;
+ case USB_OPERATION_ERROR_PORT_MISMATCH:
+ return ENABLE_USB_DATA_ERROR_PORT_MISMATCH;
+ default:
+ return ENABLE_USB_DATA_ERROR_OTHER;
+ }
+ }
+
+ /**
+ * Limits power transfer In and out of the port.
+ * <p>
+ * Disables charging and limits sourcing power(when permitted by the USB spec) until
+ * port disconnect event.
+ * </p>
+ * @param enable limits power transfer when true.
+ * @return {@link #ENABLE_LIMIT_POWER_TRANSFER_SUCCESS} when request completes successfully or
+ * {@link #ENABLE_LIMIT_POWER_TRANSFER_ERROR_INTERNAL} when request fails due to
+ * internal error or
+ * {@link ENABLE_LIMIT_POWER_TRANSFER_ERROR_NOT_SUPPORTED} when not supported or
+ * {@link ENABLE_LIMIT_POWER_TRANSFER_ERROR_PORT_MISMATCH} when request fails due to
+ * port id mismatch or
+ * {@link ENABLE_LIMIT_POWER_TRANSFER_ERROR_OTHER} when fails due to other reasons.
+ */
+ @CheckResult
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ public @EnableLimitPowerTransferStatus int enableLimitPowerTransfer(boolean enable) {
+ // UID is added To minimize operationID overlap between two different packages.
+ int operationId = sUsbOperationCount.incrementAndGet() + Binder.getCallingUid();
+ Log.i(TAG, "enableLimitPowerTransfer opId:" + operationId
+ + " callingUid:" + Binder.getCallingUid());
+ UsbOperationInternal opCallback =
+ new UsbOperationInternal(operationId, mId);
+ mUsbManager.enableLimitPowerTransfer(this, enable, operationId, opCallback);
+ opCallback.waitForOperationComplete();
+ int result = opCallback.getStatus();
+ switch (result) {
+ case USB_OPERATION_SUCCESS:
+ return ENABLE_LIMIT_POWER_TRANSFER_SUCCESS;
+ case USB_OPERATION_ERROR_INTERNAL:
+ return ENABLE_LIMIT_POWER_TRANSFER_ERROR_INTERNAL;
+ case USB_OPERATION_ERROR_NOT_SUPPORTED:
+ return ENABLE_LIMIT_POWER_TRANSFER_ERROR_NOT_SUPPORTED;
+ case USB_OPERATION_ERROR_PORT_MISMATCH:
+ return ENABLE_LIMIT_POWER_TRANSFER_ERROR_PORT_MISMATCH;
+ default:
+ return ENABLE_LIMIT_POWER_TRANSFER_ERROR_OTHER;
+ }
+ }
+
+ /**
* @hide
**/
public void enableContaminantDetection(boolean enable) {
diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java
index bb7aff6..934c506 100644
--- a/core/java/android/hardware/usb/UsbPortStatus.java
+++ b/core/java/android/hardware/usb/UsbPortStatus.java
@@ -19,7 +19,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.hardware.usb.V1_0.Constants;
import android.os.Parcel;
import android.os.Parcelable;
@@ -36,27 +35,30 @@
@Immutable
@SystemApi
public final class UsbPortStatus implements Parcelable {
+ private static final String TAG = "UsbPortStatus";
private final int mCurrentMode;
private final @UsbPowerRole int mCurrentPowerRole;
private final @UsbDataRole int mCurrentDataRole;
private final int mSupportedRoleCombinations;
private final @ContaminantProtectionStatus int mContaminantProtectionStatus;
private final @ContaminantDetectionStatus int mContaminantDetectionStatus;
+ private final boolean mUsbDataEnabled;
+ private final boolean mPowerTransferLimited;
/**
* Power role: This USB port does not have a power role.
*/
- public static final int POWER_ROLE_NONE = Constants.PortPowerRole.NONE;
+ public static final int POWER_ROLE_NONE = 0;
/**
* Power role: This USB port can act as a source (provide power).
*/
- public static final int POWER_ROLE_SOURCE = Constants.PortPowerRole.SOURCE;
+ public static final int POWER_ROLE_SOURCE = 1;
/**
* Power role: This USB port can act as a sink (receive power).
*/
- public static final int POWER_ROLE_SINK = Constants.PortPowerRole.SINK;
+ public static final int POWER_ROLE_SINK = 2;
@IntDef(prefix = { "POWER_ROLE_" }, value = {
POWER_ROLE_NONE,
@@ -69,17 +71,17 @@
/**
* Power role: This USB port does not have a data role.
*/
- public static final int DATA_ROLE_NONE = Constants.PortDataRole.NONE;
+ public static final int DATA_ROLE_NONE = 0;
/**
* Data role: This USB port can act as a host (access data services).
*/
- public static final int DATA_ROLE_HOST = Constants.PortDataRole.HOST;
+ public static final int DATA_ROLE_HOST = 1;
/**
* Data role: This USB port can act as a device (offer data services).
*/
- public static final int DATA_ROLE_DEVICE = Constants.PortDataRole.DEVICE;
+ public static final int DATA_ROLE_DEVICE = 2;
@IntDef(prefix = { "DATA_ROLE_" }, value = {
DATA_ROLE_NONE,
@@ -92,15 +94,7 @@
/**
* There is currently nothing connected to this USB port.
*/
- public static final int MODE_NONE = Constants.PortMode.NONE;
-
- /**
- * This USB port can act as a downstream facing port (host).
- *
- * <p> Implies that the port supports the {@link #POWER_ROLE_SOURCE} and
- * {@link #DATA_ROLE_HOST} combination of roles (and possibly others as well).
- */
- public static final int MODE_DFP = Constants.PortMode.DFP;
+ public static final int MODE_NONE = 0;
/**
* This USB port can act as an upstream facing port (device).
@@ -108,7 +102,15 @@
* <p> Implies that the port supports the {@link #POWER_ROLE_SINK} and
* {@link #DATA_ROLE_DEVICE} combination of roles (and possibly others as well).
*/
- public static final int MODE_UFP = Constants.PortMode.UFP;
+ public static final int MODE_UFP = 1 << 0;
+
+ /**
+ * This USB port can act as a downstream facing port (host).
+ *
+ * <p> Implies that the port supports the {@link #POWER_ROLE_SOURCE} and
+ * {@link #DATA_ROLE_HOST} combination of roles (and possibly others as well).
+ */
+ public static final int MODE_DFP = 1 << 1;
/**
* This USB port can act either as an downstream facing port (host) or as
@@ -120,87 +122,76 @@
*
* @hide
*/
- public static final int MODE_DUAL = Constants.PortMode.DRP;
+ public static final int MODE_DUAL = MODE_UFP | MODE_DFP;
/**
* This USB port can support USB Type-C Audio accessory.
*/
- public static final int MODE_AUDIO_ACCESSORY =
- android.hardware.usb.V1_1.Constants.PortMode_1_1.AUDIO_ACCESSORY;
+ public static final int MODE_AUDIO_ACCESSORY = 1 << 2;
/**
* This USB port can support USB Type-C debug accessory.
*/
- public static final int MODE_DEBUG_ACCESSORY =
- android.hardware.usb.V1_1.Constants.PortMode_1_1.DEBUG_ACCESSORY;
+ public static final int MODE_DEBUG_ACCESSORY = 1 << 3;
/**
* Contaminant presence detection not supported by the device.
* @hide
*/
- public static final int CONTAMINANT_DETECTION_NOT_SUPPORTED =
- android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.NOT_SUPPORTED;
+ public static final int CONTAMINANT_DETECTION_NOT_SUPPORTED = 0;
/**
* Contaminant presence detection supported but disabled.
* @hide
*/
- public static final int CONTAMINANT_DETECTION_DISABLED =
- android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.DISABLED;
+ public static final int CONTAMINANT_DETECTION_DISABLED = 1;
/**
* Contaminant presence enabled but not detected.
* @hide
*/
- public static final int CONTAMINANT_DETECTION_NOT_DETECTED =
- android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.NOT_DETECTED;
+ public static final int CONTAMINANT_DETECTION_NOT_DETECTED = 2;
/**
* Contaminant presence enabled and detected.
* @hide
*/
- public static final int CONTAMINANT_DETECTION_DETECTED =
- android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.DETECTED;
+ public static final int CONTAMINANT_DETECTION_DETECTED = 3;
/**
* Contaminant protection - No action performed upon detection of
* contaminant presence.
* @hide
*/
- public static final int CONTAMINANT_PROTECTION_NONE =
- android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.NONE;
+ public static final int CONTAMINANT_PROTECTION_NONE = 0;
/**
* Contaminant protection - Port is forced to sink upon detection of
* contaminant presence.
* @hide
*/
- public static final int CONTAMINANT_PROTECTION_SINK =
- android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.FORCE_SINK;
+ public static final int CONTAMINANT_PROTECTION_SINK = 1 << 0;
/**
* Contaminant protection - Port is forced to source upon detection of
* contaminant presence.
* @hide
*/
- public static final int CONTAMINANT_PROTECTION_SOURCE =
- android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.FORCE_SOURCE;
+ public static final int CONTAMINANT_PROTECTION_SOURCE = 1 << 1;
/**
* Contaminant protection - Port is disabled upon detection of
* contaminant presence.
* @hide
*/
- public static final int CONTAMINANT_PROTECTION_FORCE_DISABLE =
- android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.FORCE_DISABLE;
+ public static final int CONTAMINANT_PROTECTION_FORCE_DISABLE = 1 << 2;
/**
* Contaminant protection - Port is disabled upon detection of
* contaminant presence.
* @hide
*/
- public static final int CONTAMINANT_PROTECTION_DISABLED =
- android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.DISABLED;
+ public static final int CONTAMINANT_PROTECTION_DISABLED = 1 << 3;
@IntDef(prefix = { "CONTAMINANT_DETECTION_" }, value = {
CONTAMINANT_DETECTION_NOT_SUPPORTED,
@@ -234,6 +225,21 @@
/** @hide */
public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole,
int supportedRoleCombinations, int contaminantProtectionStatus,
+ int contaminantDetectionStatus, boolean usbDataEnabled,
+ boolean powerTransferLimited) {
+ mCurrentMode = currentMode;
+ mCurrentPowerRole = currentPowerRole;
+ mCurrentDataRole = currentDataRole;
+ mSupportedRoleCombinations = supportedRoleCombinations;
+ mContaminantProtectionStatus = contaminantProtectionStatus;
+ mContaminantDetectionStatus = contaminantDetectionStatus;
+ mUsbDataEnabled = usbDataEnabled;
+ mPowerTransferLimited = powerTransferLimited;
+ }
+
+ /** @hide */
+ public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole,
+ int supportedRoleCombinations, int contaminantProtectionStatus,
int contaminantDetectionStatus) {
mCurrentMode = currentMode;
mCurrentPowerRole = currentPowerRole;
@@ -241,6 +247,8 @@
mSupportedRoleCombinations = supportedRoleCombinations;
mContaminantProtectionStatus = contaminantProtectionStatus;
mContaminantDetectionStatus = contaminantDetectionStatus;
+ mUsbDataEnabled = true;
+ mPowerTransferLimited = false;
}
/**
@@ -323,6 +331,25 @@
return mContaminantProtectionStatus;
}
+ /**
+ * Returns UsbData status.
+ *
+ * @hide
+ */
+ public boolean getUsbDataStatus() {
+ return mUsbDataEnabled;
+ }
+
+ /**
+ * Returns whether power transfer is limited.
+ *
+ * @return true when power transfer is limited.
+ * false otherwise.
+ */
+ public boolean isPowerTransferLimited() {
+ return mPowerTransferLimited;
+ }
+
@NonNull
@Override
public String toString() {
@@ -336,6 +363,10 @@
+ getContaminantDetectionStatus()
+ ", contaminantProtectionStatus="
+ getContaminantProtectionStatus()
+ + ", usbDataEnabled="
+ + getUsbDataStatus()
+ + ", isPowerTransferLimited="
+ + isPowerTransferLimited()
+ "}";
}
@@ -352,6 +383,8 @@
dest.writeInt(mSupportedRoleCombinations);
dest.writeInt(mContaminantProtectionStatus);
dest.writeInt(mContaminantDetectionStatus);
+ dest.writeBoolean(mUsbDataEnabled);
+ dest.writeBoolean(mPowerTransferLimited);
}
public static final @NonNull Parcelable.Creator<UsbPortStatus> CREATOR =
@@ -364,9 +397,11 @@
int supportedRoleCombinations = in.readInt();
int contaminantProtectionStatus = in.readInt();
int contaminantDetectionStatus = in.readInt();
+ boolean usbDataEnabled = in.readBoolean();
+ boolean powerTransferLimited = in.readBoolean();
return new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
supportedRoleCombinations, contaminantProtectionStatus,
- contaminantDetectionStatus);
+ contaminantDetectionStatus, usbDataEnabled, powerTransferLimited);
}
@Override
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 6f15588..cc325cd 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -72,7 +72,6 @@
private static final int DO_START_INPUT = 32;
private static final int DO_CREATE_SESSION = 40;
private static final int DO_SET_SESSION_ENABLED = 45;
- private static final int DO_REVOKE_SESSION = 50;
private static final int DO_SHOW_SOFT_INPUT = 60;
private static final int DO_HIDE_SOFT_INPUT = 70;
private static final int DO_CHANGE_INPUTMETHOD_SUBTYPE = 80;
@@ -215,9 +214,6 @@
inputMethod.setSessionEnabled((InputMethodSession)msg.obj,
msg.arg1 != 0);
return;
- case DO_REVOKE_SESSION:
- inputMethod.revokeSession((InputMethodSession)msg.obj);
- return;
case DO_SHOW_SOFT_INPUT: {
final SomeArgs args = (SomeArgs)msg.obj;
inputMethod.showSoftInputWithToken(
@@ -368,22 +364,6 @@
@BinderThread
@Override
- public void revokeSession(IInputMethodSession session) {
- try {
- InputMethodSession ls = ((IInputMethodSessionWrapper)
- session).getInternalInputMethodSession();
- if (ls == null) {
- Log.w(TAG, "Session is already finished: " + session);
- return;
- }
- mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_REVOKE_SESSION, ls));
- } catch (ClassCastException e) {
- Log.w(TAG, "Incoming session not of correct type: " + session, e);
- }
- }
-
- @BinderThread
- @Override
public void showSoftInput(IBinder showInputToken, int flags, ResultReceiver resultReceiver) {
mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_SHOW_SOFT_INPUT,
flags, showInputToken, resultReceiver));
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 4666c5c..2d33817 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -3406,6 +3406,11 @@
public abstract WakeLockStats getWakeLockStats();
/**
+ * Returns aggregated Bluetooth stats.
+ */
+ public abstract BluetoothBatteryStats getBluetoothBatteryStats();
+
+ /**
* Returns Timers tracking the total time of each Resource Power Manager state and voter.
*/
public abstract Map<String, ? extends Timer> getRpmStats();
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index 6339435..2a609b8 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -368,6 +368,21 @@
}
/**
+ * Retrieves accumulated bluetooth stats.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BATTERY_STATS)
+ @NonNull
+ public BluetoothBatteryStats getBluetoothBatteryStats() {
+ try {
+ return mBatteryStats.getBluetoothBatteryStats();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Indicates an app acquiring full wifi lock.
*
* @param ws worksource (to be used for battery blaming).
diff --git a/core/java/android/os/BluetoothBatteryStats.aidl b/core/java/android/os/BluetoothBatteryStats.aidl
new file mode 100644
index 0000000..d0514b6
--- /dev/null
+++ b/core/java/android/os/BluetoothBatteryStats.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 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.os;
+
+/** {@hide} */
+parcelable BluetoothBatteryStats;
diff --git a/core/java/android/os/BluetoothBatteryStats.java b/core/java/android/os/BluetoothBatteryStats.java
new file mode 100644
index 0000000..3d99a08
--- /dev/null
+++ b/core/java/android/os/BluetoothBatteryStats.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2022 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.os;
+
+import android.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Snapshot of Bluetooth battery stats.
+ *
+ * @hide
+ */
+public class BluetoothBatteryStats implements Parcelable {
+
+ /** @hide */
+ public static class UidStats {
+ public final int uid;
+ public final long scanTimeMs;
+ public final long unoptimizedScanTimeMs;
+ public final int scanResultCount;
+ public final long rxTimeMs;
+ public final long txTimeMs;
+
+ public UidStats(int uid, long scanTimeMs, long unoptimizedScanTimeMs, int scanResultCount,
+ long rxTimeMs, long txTimeMs) {
+ this.uid = uid;
+ this.scanTimeMs = scanTimeMs;
+ this.unoptimizedScanTimeMs = unoptimizedScanTimeMs;
+ this.scanResultCount = scanResultCount;
+ this.rxTimeMs = rxTimeMs;
+ this.txTimeMs = txTimeMs;
+ }
+
+ private UidStats(Parcel in) {
+ uid = in.readInt();
+ scanTimeMs = in.readLong();
+ unoptimizedScanTimeMs = in.readLong();
+ scanResultCount = in.readInt();
+ rxTimeMs = in.readLong();
+ txTimeMs = in.readLong();
+ }
+
+ private void writeToParcel(Parcel out) {
+ out.writeInt(uid);
+ out.writeLong(scanTimeMs);
+ out.writeLong(unoptimizedScanTimeMs);
+ out.writeInt(scanResultCount);
+ out.writeLong(rxTimeMs);
+ out.writeLong(txTimeMs);
+ }
+
+ @Override
+ public String toString() {
+ return "UidStats{"
+ + "uid=" + uid
+ + ", scanTimeMs=" + scanTimeMs
+ + ", unoptimizedScanTimeMs=" + unoptimizedScanTimeMs
+ + ", scanResultCount=" + scanResultCount
+ + ", rxTimeMs=" + rxTimeMs
+ + ", txTimeMs=" + txTimeMs
+ + '}';
+ }
+ }
+
+ private final List<UidStats> mUidStats;
+
+ public BluetoothBatteryStats(@NonNull List<UidStats> uidStats) {
+ mUidStats = uidStats;
+ }
+
+ @NonNull
+ public List<UidStats> getUidStats() {
+ return mUidStats;
+ }
+
+ protected BluetoothBatteryStats(Parcel in) {
+ final int size = in.readInt();
+ mUidStats = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ mUidStats.add(new UidStats(in));
+ }
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ final int size = mUidStats.size();
+ out.writeInt(size);
+ for (int i = 0; i < size; i++) {
+ UidStats stats = mUidStats.get(i);
+ stats.writeToParcel(out);
+ }
+ }
+
+ public static final Creator<BluetoothBatteryStats> CREATOR =
+ new Creator<BluetoothBatteryStats>() {
+ @Override
+ public BluetoothBatteryStats createFromParcel(Parcel in) {
+ return new BluetoothBatteryStats(in);
+ }
+
+ @Override
+ public BluetoothBatteryStats[] newArray(int size) {
+ return new BluetoothBatteryStats[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
index 07f4082..22ddbcc 100644
--- a/core/java/android/os/TEST_MAPPING
+++ b/core/java/android/os/TEST_MAPPING
@@ -31,15 +31,6 @@
]
},
{
- "file_patterns": ["Environment\\.java"],
- "name": "FrameworksServicesTests",
- "options": [
- {
- "include-filter": "com.android.server.pm.parsing.PackageInfoUserFieldsTest"
- }
- ]
- },
- {
"file_patterns": [
"BatteryStats[^/]*\\.java",
"BatteryUsageStats[^/]*\\.java",
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 190f5f1..f18c9c9 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -16,6 +16,9 @@
package android.os;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_BADGED_LABEL;
+import static android.app.admin.DevicePolicyResources.Strings.UNDEFINED;
+
import android.Manifest;
import android.accounts.AccountManager;
import android.annotation.ColorInt;
@@ -820,6 +823,20 @@
public static final String DISALLOW_ADD_MANAGED_PROFILE = "no_add_managed_profile";
/**
+ * Specifies if a user is disallowed from creating clone profile.
+ * <p>The default value for an unmanaged user is <code>false</code>.
+ * For users with a device owner set, the default is <code>true</code>.
+ *
+ * <p>Key for user restrictions.
+ * <p>Type: Boolean
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ * @hide
+ */
+ public static final String DISALLOW_ADD_CLONE_PROFILE = "no_add_clone_profile";
+
+ /**
* Specifies if a user is disallowed from disabling application verification. The default
* value is <code>false</code>.
*
@@ -1497,6 +1514,7 @@
DISALLOW_FACTORY_RESET,
DISALLOW_ADD_USER,
DISALLOW_ADD_MANAGED_PROFILE,
+ DISALLOW_ADD_CLONE_PROFILE,
ENSURE_VERIFY_APPS,
DISALLOW_CONFIG_CELL_BROADCASTS,
DISALLOW_CONFIG_MOBILE_NETWORKS,
@@ -4645,6 +4663,18 @@
if (!hasBadge(userId)) {
return label;
}
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(
+ getUpdatableUserBadgedLabelId(userId),
+ () -> getDefaultUserBadgedLabel(label, userId),
+ /* formatArgs= */ label);
+ }
+
+ private String getUpdatableUserBadgedLabelId(int userId) {
+ return isManagedProfile(userId) ? WORK_PROFILE_BADGED_LABEL : UNDEFINED;
+ }
+
+ private String getDefaultUserBadgedLabel(CharSequence label, int userId) {
try {
final int resourceId = mService.getUserBadgeLabelResId(userId);
return Resources.getSystem().getString(resourceId, label);
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index c966b1a..87f4489 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -694,6 +694,15 @@
@SystemApi
public static final String NAMESPACE_UWB = "uwb";
+ /**
+ * Namespace for AmbientContextEventManagerService related features.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE =
+ "ambient_context_manager_service";
+
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index be2e3b2..3f54408 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10602,6 +10602,14 @@
"communal_mode_trusted_networks";
/**
+ * Setting to allow Fast Pair scans to be enabled.
+ * @hide
+ */
+ @SystemApi
+ @Readable
+ public static final String FAST_PAIR_SCAN_ENABLED = "fast_pair_scan_enabled";
+
+ /**
* These entries are considered common between the personal and the managed profile,
* since the managed profile doesn't get to change them.
*/
diff --git a/core/java/android/service/ambientcontext/AmbientContextDetectionService.java b/core/java/android/service/ambientcontext/AmbientContextDetectionService.java
new file mode 100644
index 0000000..dccfe36
--- /dev/null
+++ b/core/java/android/service/ambientcontext/AmbientContextDetectionService.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2022 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.service.ambientcontext;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.ambientcontext.AmbientContextEvent;
+import android.app.ambientcontext.AmbientContextEventRequest;
+import android.app.ambientcontext.AmbientContextEventResponse;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteCallback;
+import android.util.Slog;
+
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * Abstract base class for {@link AmbientContextEvent} detection service.
+ *
+ * <p> A service that provides requested ambient context events to the system.
+ * The system's default AmbientContextDetectionService implementation is configured in
+ * {@code config_defaultAmbientContextDetectionService}. If this config has no value, a stub is
+ * returned.
+ *
+ * See: {@code AmbientContextManagerService}.
+ *
+ * <pre>
+ * {@literal
+ * <service android:name=".YourAmbientContextDetectionService"
+ * android:permission="android.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE">
+ * </service>}
+ * </pre>
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class AmbientContextDetectionService extends Service {
+ private static final String TAG = AmbientContextDetectionService.class.getSimpleName();
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service. To be supported, the
+ * service must also require the
+ * {@link android.Manifest.permission#BIND_AMBIENT_CONTEXT_DETECTION_SERVICE}
+ * permission so that other applications can not abuse it.
+ */
+ public static final String SERVICE_INTERFACE =
+ "android.service.ambientcontext.AmbientContextDetectionService";
+
+ /**
+ * The key for the bundle the parameter of {@code RemoteCallback#sendResult}. Implementation
+ * should set bundle result with this key.
+ *
+ * @hide
+ */
+ public static final String RESPONSE_BUNDLE_KEY =
+ "android.service.ambientcontext.EventResponseKey";
+
+ @Nullable
+ @Override
+ public final IBinder onBind(@NonNull Intent intent) {
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return new IAmbientContextDetectionService.Stub() {
+ /** {@inheritDoc} */
+ @Override
+ public void startDetection(
+ @NonNull AmbientContextEventRequest request, String packageName,
+ RemoteCallback callback) {
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(callback);
+ Consumer<AmbientContextEventResponse> consumer =
+ response -> {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(
+ AmbientContextDetectionService.RESPONSE_BUNDLE_KEY,
+ response);
+ callback.sendResult(bundle);
+ };
+ AmbientContextDetectionService.this.onStartDetection(
+ request, packageName, consumer);
+ Slog.d(TAG, "startDetection " + request);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void stopDetection(String packageName) {
+ Objects.requireNonNull(packageName);
+ AmbientContextDetectionService.this.onStopDetection(packageName);
+ }
+ };
+ }
+ return null;
+ }
+
+ /**
+ * Starts detection and provides detected events to the consumer. The ongoing detection will
+ * keep running, until onStopDetection is called. If there were previously requested
+ * detection from the same package, the previous request will be replaced with the new request.
+ * The implementation should keep track of whether the user consented each requested
+ * AmbientContextEvent for the app. If not consented, the response should set status
+ * STATUS_ACCESS_DENIED and include an action PendingIntent for the app to redirect the user
+ * to the consent screen.
+ *
+ * @param request The request with events to detect, optional detection window and other
+ * options.
+ * @param packageName the requesting app's package name
+ * @param consumer the consumer for the detected event
+ */
+ public abstract void onStartDetection(
+ @NonNull AmbientContextEventRequest request,
+ @NonNull String packageName,
+ @NonNull Consumer<AmbientContextEventResponse> consumer);
+
+ /**
+ * Stops detection of the events. Events that are not being detected will be ignored.
+ *
+ * @param packageName stops detection for the given package.
+ */
+ public abstract void onStopDetection(@NonNull String packageName);
+}
diff --git a/core/java/android/service/ambientcontext/IAmbientContextDetectionService.aidl b/core/java/android/service/ambientcontext/IAmbientContextDetectionService.aidl
new file mode 100644
index 0000000..1c6e25e
--- /dev/null
+++ b/core/java/android/service/ambientcontext/IAmbientContextDetectionService.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 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.service.ambientcontext;
+
+import android.app.ambientcontext.AmbientContextEventRequest;
+import android.os.RemoteCallback;
+
+/**
+ * Interface for a concrete implementation to provide AmbientContextEvents to the framework.
+ *
+ * @hide
+ */
+oneway interface IAmbientContextDetectionService {
+ void startDetection(in AmbientContextEventRequest request, in String packageName,
+ in RemoteCallback callback);
+ void stopDetection(in String packageName);
+}
\ No newline at end of file
diff --git a/core/java/android/service/ambientcontext/OWNERS b/core/java/android/service/ambientcontext/OWNERS
new file mode 100644
index 0000000..a863297
--- /dev/null
+++ b/core/java/android/service/ambientcontext/OWNERS
@@ -0,0 +1,3 @@
+enxun@google.com
+kxchen@google.com
+tgadh@google.com
diff --git a/core/java/android/service/games/GameSession.java b/core/java/android/service/games/GameSession.java
index b6fe067..cb5c19b 100644
--- a/core/java/android/service/games/GameSession.java
+++ b/core/java/android/service/games/GameSession.java
@@ -18,6 +18,7 @@
import android.annotation.Hide;
import android.annotation.IntDef;
+import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.content.Context;
@@ -25,6 +26,7 @@
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.Handler;
+import android.os.Looper;
import android.os.RemoteException;
import android.util.Slog;
import android.view.SurfaceControlViewHost;
@@ -47,21 +49,65 @@
* which is then returned when a game session is created via
* {@link GameSessionService#onNewSession(CreateGameSessionRequest)}.
*
+ * This class exposes various lifecycle methods which are guaranteed to be called in the following
+ * fashion:
+ *
+ * {@link #onCreate()}: Will always be the first lifecycle method to be called, once the game
+ * session is created.
+ *
+ * {@link #onGameTaskFocusChanged(boolean)}: Will be called after {@link #onCreate()} with
+ * focused=true when the game task first comes into focus (if it does). If the game task is focused
+ * when the game session is created, this method will be called immediately after
+ * {@link #onCreate()} with focused=true. After this method is called with focused=true, it will be
+ * called again with focused=false when the task goes out of focus. If this method is ever called
+ * with focused=true, it is guaranteed to be called again with focused=false before
+ * {@link #onDestroy()} is called. If the game task never comes into focus during the session
+ * lifetime, this method will never be called.
+ *
+ * {@link #onDestroy()}: Will always be called after {@link #onCreate()}. If the game task ever
+ * comes into focus before the game session is destroyed, then this method will be called after one
+ * or more pairs of calls to {@link #onGameTaskFocusChanged(boolean)}.
+ *
* @hide
*/
@SystemApi
public abstract class GameSession {
-
private static final String TAG = "GameSession";
+ private static final boolean DEBUG = false;
final IGameSession mInterface = new IGameSession.Stub() {
@Override
- public void destroy() {
+ public void onDestroyed() {
Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
GameSession::doDestroy, GameSession.this));
}
+
+ @Override
+ public void onTaskFocusChanged(boolean focused) {
+ Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+ GameSession::moveToState, GameSession.this,
+ focused ? LifecycleState.TASK_FOCUSED : LifecycleState.TASK_UNFOCUSED));
+ }
};
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public enum LifecycleState {
+ // Initial state; may transition to CREATED.
+ INITIALIZED,
+ // May transition to TASK_FOCUSED or DESTROYED.
+ CREATED,
+ // May transition to TASK_UNFOCUSED.
+ TASK_FOCUSED,
+ // May transition to TASK_FOCUSED or DESTROYED.
+ TASK_UNFOCUSED,
+ // May not transition once reached.
+ DESTROYED
+ }
+
+ private LifecycleState mLifecycleState = LifecycleState.INITIALIZED;
private IGameSessionController mGameSessionController;
private int mTaskId;
private GameSessionRootView mGameSessionRootView;
@@ -87,13 +133,93 @@
@Hide
void doCreate() {
- onCreate();
+ moveToState(LifecycleState.CREATED);
}
@Hide
void doDestroy() {
- onDestroy();
mSurfaceControlViewHost.release();
+ moveToState(LifecycleState.DESTROYED);
+ }
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ @MainThread
+ public void moveToState(LifecycleState newLifecycleState) {
+ if (DEBUG) {
+ Slog.d(TAG, "moveToState: " + mLifecycleState + " -> " + newLifecycleState);
+ }
+
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ throw new RuntimeException("moveToState should be used only from the main thread");
+ }
+
+ if (mLifecycleState == newLifecycleState) {
+ // Nothing to do.
+ return;
+ }
+
+ switch (mLifecycleState) {
+ case INITIALIZED:
+ if (newLifecycleState == LifecycleState.CREATED) {
+ onCreate();
+ } else if (newLifecycleState == LifecycleState.DESTROYED) {
+ onCreate();
+ onDestroy();
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring moveToState: INITIALIZED -> " + newLifecycleState);
+ }
+ return;
+ }
+ break;
+ case CREATED:
+ if (newLifecycleState == LifecycleState.TASK_FOCUSED) {
+ onGameTaskFocusChanged(/*focused=*/ true);
+ } else if (newLifecycleState == LifecycleState.DESTROYED) {
+ onDestroy();
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring moveToState: CREATED -> " + newLifecycleState);
+ }
+ return;
+ }
+ break;
+ case TASK_FOCUSED:
+ if (newLifecycleState == LifecycleState.TASK_UNFOCUSED) {
+ onGameTaskFocusChanged(/*focused=*/ false);
+ } else if (newLifecycleState == LifecycleState.DESTROYED) {
+ onGameTaskFocusChanged(/*focused=*/ false);
+ onDestroy();
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring moveToState: TASK_FOCUSED -> " + newLifecycleState);
+ }
+ return;
+ }
+ break;
+ case TASK_UNFOCUSED:
+ if (newLifecycleState == LifecycleState.TASK_FOCUSED) {
+ onGameTaskFocusChanged(/*focused=*/ true);
+ } else if (newLifecycleState == LifecycleState.DESTROYED) {
+ onDestroy();
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring moveToState: TASK_UNFOCUSED -> " + newLifecycleState);
+ }
+ return;
+ }
+ break;
+ case DESTROYED:
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring moveToState: DESTROYED -> " + newLifecycleState);
+ }
+ return;
+ }
+
+ mLifecycleState = newLifecycleState;
}
/**
@@ -105,13 +231,27 @@
}
/**
- * Finalizer called when the game session is ending.
+ * Finalizer called when the game session is ending. This method will always be called after a
+ * call to {@link #onCreate()}. If the game task is ever in focus, this method will be called
+ * after one or more pairs of calls to {@link #onGameTaskFocusChanged(boolean)}.
*
* This should be used to perform any cleanup before the game session is destroyed.
*/
public void onDestroy() {
}
+ /**
+ * Called when the game task for this session is or unfocused. The initial call to this method
+ * will always come after a call to {@link #onCreate()} with focused=true (when the game task
+ * first comes into focus after the session is created, or immediately after the session is
+ * created if the game task is already focused).
+ *
+ * This should be used to perform any setup required when the game task comes into focus or any
+ * cleanup that is required when the game task goes out of focus.
+ *
+ * @param focused True if the game task is focused, false if the game task is unfocused.
+ */
+ public void onGameTaskFocusChanged(boolean focused) {}
/**
* Sets the task overlay content to an explicit view. This view is placed directly into the game
diff --git a/core/java/android/service/games/IGameSession.aidl b/core/java/android/service/games/IGameSession.aidl
index b2e9f1d..71da630 100644
--- a/core/java/android/service/games/IGameSession.aidl
+++ b/core/java/android/service/games/IGameSession.aidl
@@ -20,5 +20,6 @@
* @hide
*/
oneway interface IGameSession {
- void destroy();
+ void onDestroyed();
+ void onTaskFocusChanged(boolean focused);
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 0084054..dd4355d 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -278,6 +278,7 @@
private Display mDisplay;
private Context mDisplayContext;
private int mDisplayState;
+ private @Surface.Rotation int mDisplayInstallOrientation;
private float mWallpaperDimAmount = 0.05f;
private float mPreviousWallpaperDimAmount = mWallpaperDimAmount;
private float mDefaultDimAmount = mWallpaperDimAmount;
@@ -1122,6 +1123,11 @@
mWindow, mLayout, mWidth, mHeight,
View.VISIBLE, 0, -1, mWinFrames, mMergedConfiguration, mSurfaceControl,
mInsetsState, mTempControls, mSurfaceSize);
+
+ final int transformHint = SurfaceControl.rotationToBufferTransform(
+ (mDisplayInstallOrientation + mDisplay.getRotation()) % 4);
+ mSurfaceControl.setTransformHint(transformHint);
+
if (mSurfaceControl.isValid()) {
if (mBbqSurfaceControl == null) {
mBbqSurfaceControl = new SurfaceControl.Builder()
@@ -1135,9 +1141,9 @@
.build();
updateSurfaceDimming();
}
- // Propagate transform hint from WM so we can use the right hint for the
+ // Propagate transform hint from WM, so we can use the right hint for the
// first frame.
- mBbqSurfaceControl.setTransformHint(mSurfaceControl.getTransformHint());
+ mBbqSurfaceControl.setTransformHint(transformHint);
Surface blastSurface = getOrCreateBLASTSurface(mSurfaceSize.x,
mSurfaceSize.y, mFormat);
// If blastSurface == null that means it hasn't changed since the last
@@ -1377,6 +1383,7 @@
mWallpaperDimAmount = mDefaultDimAmount;
mPreviousWallpaperDimAmount = mWallpaperDimAmount;
mDisplayState = mDisplay.getState();
+ mDisplayInstallOrientation = mDisplay.getInstallOrientation();
if (DEBUG) Log.v(TAG, "onCreate(): " + this);
onCreate(mSurfaceHolder);
diff --git a/core/java/android/service/wallpapereffectsgeneration/OWNERS b/core/java/android/service/wallpapereffectsgeneration/OWNERS
new file mode 100644
index 0000000..d2d3e2c0
--- /dev/null
+++ b/core/java/android/service/wallpapereffectsgeneration/OWNERS
@@ -0,0 +1,4 @@
+susharon@google.com
+shanh@google.com
+huiwu@google.com
+srazdan@google.com
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 52f1fae..96a1fc6 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -81,6 +81,7 @@
DEFAULT_FLAGS.put(SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES, "true");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_SECURITY_HUB, "true");
DEFAULT_FLAGS.put(SETTINGS_SUPPORT_LARGE_SCREEN, "true");
+ DEFAULT_FLAGS.put("settings_search_always_expand", "false");
DEFAULT_FLAGS.put(SETTINGS_APP_LANGUAGE_SELECTION, "false");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS, "true");
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 70266c1..d6e074f 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -928,6 +928,18 @@
}
/**
+ * Returns the install orientation of the display.
+ * @hide
+ */
+ @Surface.Rotation
+ public int getInstallOrientation() {
+ synchronized (mLock) {
+ updateDisplayInfoLocked();
+ return mDisplayInfo.installOrientation;
+ }
+ }
+
+ /**
* @deprecated use {@link #getRotation}
* @return orientation of this display.
*/
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 678c80a..6917d66 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -318,6 +318,12 @@
@Nullable
public RoundedCorners roundedCorners;
+ /**
+ * Install orientation of the display relative to its natural orientation.
+ */
+ @Surface.Rotation
+ public int installOrientation;
+
public static final @android.annotation.NonNull Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() {
@Override
public DisplayInfo createFromParcel(Parcel source) {
@@ -389,7 +395,8 @@
&& brightnessMaximum == other.brightnessMaximum
&& brightnessDefault == other.brightnessDefault
&& Objects.equals(roundedCorners, other.roundedCorners)
- && shouldConstrainMetricsForLauncher == other.shouldConstrainMetricsForLauncher;
+ && shouldConstrainMetricsForLauncher == other.shouldConstrainMetricsForLauncher
+ && installOrientation == other.installOrientation;
}
@Override
@@ -441,6 +448,7 @@
brightnessDefault = other.brightnessDefault;
roundedCorners = other.roundedCorners;
shouldConstrainMetricsForLauncher = other.shouldConstrainMetricsForLauncher;
+ installOrientation = other.installOrientation;
}
public void readFromParcel(Parcel source) {
@@ -498,6 +506,7 @@
userDisabledHdrTypes[i] = source.readInt();
}
shouldConstrainMetricsForLauncher = source.readBoolean();
+ installOrientation = source.readInt();
}
@Override
@@ -553,6 +562,7 @@
dest.writeInt(userDisabledHdrTypes[i]);
}
dest.writeBoolean(shouldConstrainMetricsForLauncher);
+ dest.writeInt(installOrientation);
}
@Override
@@ -809,6 +819,8 @@
sb.append(brightnessDefault);
sb.append(", shouldConstrainMetricsForLauncher ");
sb.append(shouldConstrainMetricsForLauncher);
+ sb.append(", installOrientation ");
+ sb.append(Surface.rotationToString(installOrientation));
sb.append("}");
return sb.toString();
}
diff --git a/core/java/android/view/GestureExclusionTracker.java b/core/java/android/view/GestureExclusionTracker.java
deleted file mode 100644
index fffb323e..0000000
--- a/core/java/android/view/GestureExclusionTracker.java
+++ /dev/null
@@ -1,142 +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 android.view;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.Rect;
-
-import com.android.internal.util.Preconditions;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * Used by {@link ViewRootImpl} to track system gesture exclusion rects reported by views.
- */
-class GestureExclusionTracker {
- private boolean mGestureExclusionViewsChanged = false;
- private boolean mRootGestureExclusionRectsChanged = false;
- private List<Rect> mRootGestureExclusionRects = Collections.emptyList();
- private List<GestureExclusionViewInfo> mGestureExclusionViewInfos = new ArrayList<>();
- private List<Rect> mGestureExclusionRects = Collections.emptyList();
-
- public void updateRectsForView(@NonNull View view) {
- boolean found = false;
- final Iterator<GestureExclusionViewInfo> i = mGestureExclusionViewInfos.iterator();
- while (i.hasNext()) {
- final GestureExclusionViewInfo info = i.next();
- final View v = info.getView();
- if (v == null || !v.isAttachedToWindow() || !v.isAggregatedVisible()) {
- mGestureExclusionViewsChanged = true;
- i.remove();
- continue;
- }
- if (v == view) {
- found = true;
- info.mDirty = true;
- break;
- }
- }
- if (!found && view.isAttachedToWindow()) {
- mGestureExclusionViewInfos.add(new GestureExclusionViewInfo(view));
- mGestureExclusionViewsChanged = true;
- }
- }
-
- @Nullable
- public List<Rect> computeChangedRects() {
- boolean changed = mRootGestureExclusionRectsChanged;
- final Iterator<GestureExclusionViewInfo> i = mGestureExclusionViewInfos.iterator();
- final List<Rect> rects = new ArrayList<>(mRootGestureExclusionRects);
- while (i.hasNext()) {
- final GestureExclusionViewInfo info = i.next();
- switch (info.update()) {
- case GestureExclusionViewInfo.CHANGED:
- changed = true;
- // Deliberate fall-through
- case GestureExclusionViewInfo.UNCHANGED:
- rects.addAll(info.mExclusionRects);
- break;
- case GestureExclusionViewInfo.GONE:
- mGestureExclusionViewsChanged = true;
- i.remove();
- break;
- }
- }
- if (changed || mGestureExclusionViewsChanged) {
- mGestureExclusionViewsChanged = false;
- mRootGestureExclusionRectsChanged = false;
- if (!mGestureExclusionRects.equals(rects)) {
- mGestureExclusionRects = rects;
- return rects;
- }
- }
- return null;
- }
-
- public void setRootSystemGestureExclusionRects(@NonNull List<Rect> rects) {
- Preconditions.checkNotNull(rects, "rects must not be null");
- mRootGestureExclusionRects = rects;
- mRootGestureExclusionRectsChanged = true;
- }
-
- @NonNull
- public List<Rect> getRootSystemGestureExclusionRects() {
- return mRootGestureExclusionRects;
- }
-
- private static class GestureExclusionViewInfo {
- public static final int CHANGED = 0;
- public static final int UNCHANGED = 1;
- public static final int GONE = 2;
-
- private final WeakReference<View> mView;
- boolean mDirty = true;
- List<Rect> mExclusionRects = Collections.emptyList();
-
- GestureExclusionViewInfo(View view) {
- mView = new WeakReference<>(view);
- }
-
- public View getView() {
- return mView.get();
- }
-
- public int update() {
- final View excludedView = getView();
- if (excludedView == null || !excludedView.isAttachedToWindow()
- || !excludedView.isAggregatedVisible()) return GONE;
- final List<Rect> localRects = excludedView.getSystemGestureExclusionRects();
- final List<Rect> newRects = new ArrayList<>(localRects.size());
- for (Rect src : localRects) {
- Rect mappedRect = new Rect(src);
- ViewParent p = excludedView.getParent();
- if (p != null && p.getChildVisibleRect(excludedView, mappedRect, null)) {
- newRects.add(mappedRect);
- }
- }
-
- if (mExclusionRects.equals(localRects)) return UNCHANGED;
- mExclusionRects = newRects;
- return CHANGED;
- }
- }
-}
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index 8524ac84..097d1d0 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Rect;
-import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import com.android.internal.annotations.VisibleForTesting;
@@ -75,11 +74,6 @@
@VisibleForTesting
public WeakReference<View> mConnectedView = null;
- /** The editor bound reported by the connected View. */
- @Nullable
- @VisibleForTesting
- public Rect mEditorBound = null;
-
/**
* When InputConnection restarts for a View, View#onInputConnectionCreatedInternal
* might be called before View#onInputConnectionClosedInternal, so we need to count the input
@@ -174,9 +168,8 @@
* @param view the view that created the current InputConnection.
* @see #onInputConnectionClosed(View)
*/
- public void onInputConnectionCreated(@NonNull View view, @NonNull EditorInfo editorInfo) {
+ public void onInputConnectionCreated(@NonNull View view) {
final View connectedView = getConnectedView();
-// updateEditorBound(editorInfo.getInitialEditorBound());
if (connectedView == view) {
++mConnectionCount;
} else {
@@ -198,33 +191,15 @@
--mConnectionCount;
if (mConnectionCount == 0) {
mConnectedView = null;
- mEditorBound = null;
}
} else {
// Unexpected branch, set mConnectedView to null to avoid further problem.
mConnectedView = null;
- mEditorBound = null;
mConnectionCount = 0;
}
}
/**
- * Notify the HandwritingInitiator that editor bound of the connected view(the view with
- * active InputConnection) has be updated.
- * @param editorBound new the editor bounds of the connected view.
- */
- public void updateEditorBound(@NonNull Rect editorBound) {
- if (mEditorBound == null) {
- mEditorBound = new Rect(editorBound);
- } else {
- mEditorBound.left = editorBound.left;
- mEditorBound.top = editorBound.top;
- mEditorBound.right = editorBound.right;
- mEditorBound.bottom = editorBound.bottom;
- }
- }
-
- /**
* Try to initiate handwriting. For this method to successfully send startHandwriting signal,
* the following 3 conditions should meet:
* a) The stylus movement exceeds the touchSlop.
@@ -240,18 +215,19 @@
return;
}
final View connectedView = getConnectedView();
- if (connectedView == null || mEditorBound == null) {
+ if (connectedView == null) {
return;
}
final ViewParent viewParent = connectedView.getParent();
// Do a final check before startHandwriting.
if (viewParent != null && connectedView.isAttachedToWindow()) {
- final Rect editorBounds = new Rect(mEditorBound);
+ final Rect editorBounds =
+ new Rect(0, 0, connectedView.getWidth(), connectedView.getHeight());
if (viewParent.getChildVisibleRect(connectedView, editorBounds, null)) {
final int roundedInitX = Math.round(mState.mStylusDownX);
final int roundedInitY = Math.round(mState.mStylusDownY);
if (editorBounds.contains(roundedInitX, roundedInitY)) {
- startHandwriting(mConnectedView.get());
+ startHandwriting(connectedView);
}
}
}
@@ -261,7 +237,7 @@
/** For test only. */
@VisibleForTesting
public void startHandwriting(View view) {
- // mImm.startHandwriting(view);
+ mImm.startStylusHandwriting(view);
}
private boolean largerThanTouchSlop(float x1, float y1, float x2, float y2) {
diff --git a/core/java/android/view/IDisplayWindowListener.aidl b/core/java/android/view/IDisplayWindowListener.aidl
index f95d6b3..449e9b3 100644
--- a/core/java/android/view/IDisplayWindowListener.aidl
+++ b/core/java/android/view/IDisplayWindowListener.aidl
@@ -16,8 +16,11 @@
package android.view;
+import android.graphics.Rect;
import android.content.res.Configuration;
+import java.util.List;
+
/**
* Interface to listen for changes to display window-containers.
*
@@ -56,4 +59,9 @@
* Called when the previous fixed rotation on a display is finished.
*/
void onFixedRotationFinished(int displayId);
+
+ /**
+ * Called when the keep clear ares on a display have changed.
+ */
+ void onKeepClearAreasChanged(int displayId, in List<Rect> keepClearAreas);
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 4d24965..fb848ad 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -25,7 +25,6 @@
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.GraphicBuffer;
-import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
@@ -485,16 +484,6 @@
void getStableInsets(int displayId, out Rect outInsets);
/**
- * Set the forwarded insets on the display.
- * <p>
- * This is only used in case a virtual display is displayed on another display that has insets,
- * and the bounds of the virtual display is overlapping with the insets from the host display.
- * In that case, the contents on the virtual display won't be placed over the forwarded insets.
- * Only the owner of the display is permitted to set the forwarded insets on it.
- */
- void setForwardedInsets(int displayId, in Insets insets);
-
- /**
* Register shortcut key. Shortcut code is packed as:
* (MetaState << Integer.SIZE) | KeyCode
* @hide
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 921ce53..6226566 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -37,6 +37,7 @@
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.window.ClientWindowFrames;
+import android.window.IOnBackInvokedCallback;
import java.util.List;
@@ -290,6 +291,11 @@
oneway void reportSystemGestureExclusionChanged(IWindow window, in List<Rect> exclusionRects);
/**
+ * Called when the keep-clear areas for this window have changed.
+ */
+ oneway void reportKeepClearAreasChanged(IWindow window, in List<Rect> keepClearRects);
+
+ /**
* Request the server to call setInputWindowInfo on a given Surface, and return
* an input channel where the client can receive input.
*/
@@ -328,4 +334,12 @@
*/
oneway void generateDisplayHash(IWindow window, in Rect boundsInWindow,
in String hashAlgorithm, in RemoteCallback callback);
+
+ /**
+ * Sets the {@link IOnBackInvokedCallback} to be invoked for a window when back is triggered.
+ *
+ * @param window The token for the window to set the callback to.
+ * @param callback The {@link IOnBackInvokedCallback} to set.
+ */
+ oneway void setOnBackInvokedCallback(IWindow window, IOnBackInvokedCallback callback);
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index b7f9be7..e751720b 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -1831,13 +1831,15 @@
public float density;
public boolean secure;
public DeviceProductInfo deviceProductInfo;
+ public @Surface.Rotation int installOrientation;
@Override
public String toString() {
return "StaticDisplayInfo{isInternal=" + isInternal
+ ", density=" + density
+ ", secure=" + secure
- + ", deviceProductInfo=" + deviceProductInfo + "}";
+ + ", deviceProductInfo=" + deviceProductInfo
+ + ", installOrientation=" + installOrientation + "}";
}
@Override
@@ -1848,12 +1850,13 @@
return isInternal == that.isInternal
&& density == that.density
&& secure == that.secure
- && Objects.equals(deviceProductInfo, that.deviceProductInfo);
+ && Objects.equals(deviceProductInfo, that.deviceProductInfo)
+ && installOrientation == that.installOrientation;
}
@Override
public int hashCode() {
- return Objects.hash(isInternal, density, secure, deviceProductInfo);
+ return Objects.hash(isInternal, density, secure, deviceProductInfo, installOrientation);
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 2126fd5..93fdee0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4734,15 +4734,18 @@
WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback;
/**
- * This lives here since it's only valid for interactive views. This list is null until the
- * first use.
+ * This lives here since it's only valid for interactive views. This list is null
+ * until its first use.
*/
private List<Rect> mSystemGestureExclusionRects = null;
+ private List<Rect> mKeepClearRects = null;
+ private boolean mPreferKeepClear = false;
/**
- * Used to track {@link #mSystemGestureExclusionRects}
+ * Used to track {@link #mSystemGestureExclusionRects} and {@link #mKeepClearRects}
*/
public RenderNode.PositionUpdateListener mPositionUpdateListener;
+ private Runnable mPositionChangedUpdate;
/**
* Allows the application to implement custom scroll capture support.
@@ -6028,6 +6031,9 @@
case R.styleable.View_clipToOutline:
setClipToOutline(a.getBoolean(attr, false));
break;
+ case R.styleable.View_preferKeepClear:
+ setPreferKeepClear(a.getBoolean(attr, false));
+ break;
}
}
@@ -11665,37 +11671,49 @@
} else {
info.mSystemGestureExclusionRects = new ArrayList<>(rects);
}
- if (rects.isEmpty()) {
+
+ updatePositionUpdateListener();
+ postUpdate(this::updateSystemGestureExclusionRects);
+ }
+
+ private void updatePositionUpdateListener() {
+ final ListenerInfo info = getListenerInfo();
+ if (getSystemGestureExclusionRects().isEmpty()
+ && collectPreferKeepClearRects().isEmpty()) {
if (info.mPositionUpdateListener != null) {
mRenderNode.removePositionUpdateListener(info.mPositionUpdateListener);
+ info.mPositionChangedUpdate = null;
}
} else {
if (info.mPositionUpdateListener == null) {
+ info.mPositionChangedUpdate = () -> {
+ updateSystemGestureExclusionRects();
+ updateKeepClearRects();
+ };
info.mPositionUpdateListener = new RenderNode.PositionUpdateListener() {
@Override
public void positionChanged(long n, int l, int t, int r, int b) {
- postUpdateSystemGestureExclusionRects();
+ postUpdate(info.mPositionChangedUpdate);
}
@Override
public void positionLost(long frameNumber) {
- postUpdateSystemGestureExclusionRects();
+ postUpdate(info.mPositionChangedUpdate);
}
};
mRenderNode.addPositionUpdateListener(info.mPositionUpdateListener);
}
}
- postUpdateSystemGestureExclusionRects();
}
/**
* WARNING: this can be called by a hwui worker thread, not just the UI thread!
*/
- void postUpdateSystemGestureExclusionRects() {
+ private void postUpdate(Runnable r) {
// Potentially racey from a background thread. It's ok if it's not perfect.
final Handler h = getHandler();
if (h != null) {
- h.postAtFrontOfQueue(this::updateSystemGestureExclusionRects);
+ h.postAtFrontOfQueue(r);
}
}
@@ -11727,6 +11745,106 @@
}
/**
+ * Set a preference to keep the bounds of this view clear from floating windows above this
+ * view's window. This informs the system that the view is considered a vital area for the
+ * user and that ideally it should not be covered. Setting this is only appropriate for UI
+ * where the user would likely take action to uncover it.
+ * <p>
+ * The system will try to respect this, but when not possible will ignore it.
+ * <p>
+ * @see #setPreferKeepClearRects
+ * @see #isPreferKeepClear
+ * @attr ref android.R.styleable#View_preferKeepClear
+ */
+ public final void setPreferKeepClear(boolean preferKeepClear) {
+ getListenerInfo().mPreferKeepClear = preferKeepClear;
+ updatePositionUpdateListener();
+ postUpdate(this::updateKeepClearRects);
+ }
+
+ /**
+ * Retrieve the preference for this view to be kept clear. This is set either by
+ * {@link #setPreferKeepClear} or via the attribute android.R.styleable#View_preferKeepClear.
+ * <p>
+ * If this is {@code true}, the system will ignore the Rects set by
+ * {@link #setPreferKeepClearRects} and try to keep the whole view clear.
+ * <p>
+ * @see #setPreferKeepClear
+ * @attr ref android.R.styleable#View_preferKeepClear
+ */
+ public final boolean isPreferKeepClear() {
+ return mListenerInfo != null && mListenerInfo.mPreferKeepClear;
+ }
+
+ /**
+ * Set a preference to keep the provided rects clear from floating windows above this
+ * view's window. This informs the system that these rects are considered vital areas for the
+ * user and that ideally they should not be covered. Setting this is only appropriate for UI
+ * where the user would likely take action to uncover it.
+ * <p>
+ * If the whole view is preferred to be clear ({@link #isPreferKeepClear}), the rects set here
+ * will be ignored.
+ * <p>
+ * The system will try to respect this preference, but when not possible will ignore it.
+ * <p>
+ * @see #setPreferKeepClear
+ * @see #getPreferKeepClearRects
+ */
+ public final void setPreferKeepClearRects(@NonNull List<Rect> rects) {
+ final ListenerInfo info = getListenerInfo();
+ if (info.mKeepClearRects != null) {
+ info.mKeepClearRects.clear();
+ info.mKeepClearRects.addAll(rects);
+ } else {
+ info.mKeepClearRects = new ArrayList<>(rects);
+ }
+ updatePositionUpdateListener();
+ postUpdate(this::updateKeepClearRects);
+ }
+
+ /**
+ * @return the list of rects, set by {@link #setPreferKeepClearRects}.
+ *
+ * @see #setPreferKeepClearRects
+ */
+ @NonNull
+ public final List<Rect> getPreferKeepClearRects() {
+ final ListenerInfo info = mListenerInfo;
+ if (info != null && info.mKeepClearRects != null) {
+ return new ArrayList(info.mKeepClearRects);
+ }
+
+ return Collections.emptyList();
+ }
+
+ void updateKeepClearRects() {
+ final AttachInfo ai = mAttachInfo;
+ if (ai != null) {
+ ai.mViewRootImpl.updateKeepClearRectsForView(this);
+ }
+ }
+
+ /**
+ * Retrieve the list of areas within this view's post-layout coordinate space which the
+ * system will try to not cover with other floating elements, like the pip window.
+ */
+ @NonNull
+ List<Rect> collectPreferKeepClearRects() {
+ final ListenerInfo info = mListenerInfo;
+ if (info != null) {
+ final List<Rect> list = new ArrayList();
+ if (info.mPreferKeepClear) {
+ list.add(new Rect(0, 0, getWidth(), getHeight()));
+ } else if (info.mKeepClearRects != null) {
+ list.addAll(info.mKeepClearRects);
+ }
+ return list;
+ }
+
+ return Collections.emptyList();
+ }
+
+ /**
* Compute the view's coordinate within the surface.
*
* <p>Computes the coordinates of this view in its surface. The argument
@@ -15120,7 +15238,11 @@
notifyAppearedOrDisappearedForContentCaptureIfNeeded(isVisible);
if (!getSystemGestureExclusionRects().isEmpty()) {
- postUpdateSystemGestureExclusionRects();
+ postUpdate(this::updateSystemGestureExclusionRects);
+ }
+
+ if (!collectPreferKeepClearRects().isEmpty()) {
+ postUpdate(this::updateKeepClearRects);
}
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index cec8d4c..97b5a31 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -398,6 +398,8 @@
final DisplayManager mDisplayManager;
final String mBasePackageName;
+ private @Surface.Rotation int mDisplayInstallOrientation;
+
final int[] mTmpLocation = new int[2];
final TypedValue mTmpValue = new TypedValue();
@@ -749,7 +751,10 @@
return mImeFocusController;
}
- private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker();
+ private final ViewRootRectTracker mGestureExclusionTracker =
+ new ViewRootRectTracker(v -> v.getSystemGestureExclusionRects());
+ private final ViewRootRectTracker mKeepClearRectsTracker =
+ new ViewRootRectTracker(v -> v.collectPreferKeepClearRects());
private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
@@ -1025,6 +1030,7 @@
mView = view;
mAttachInfo.mDisplayState = mDisplay.getState();
+ mDisplayInstallOrientation = mDisplay.getInstallOrientation();
mViewLayoutDirectionInitial = mView.getRawLayoutDirection();
mFallbackEventHandler.setView(view);
mWindowAttributes.copyFrom(attrs);
@@ -4767,7 +4773,7 @@
* the root's view hierarchy.
*/
public void setRootSystemGestureExclusionRects(@NonNull List<Rect> rects) {
- mGestureExclusionTracker.setRootSystemGestureExclusionRects(rects);
+ mGestureExclusionTracker.setRootRects(rects);
mHandler.sendEmptyMessage(MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED);
}
@@ -4777,7 +4783,26 @@
*/
@NonNull
public List<Rect> getRootSystemGestureExclusionRects() {
- return mGestureExclusionTracker.getRootSystemGestureExclusionRects();
+ return mGestureExclusionTracker.getRootRects();
+ }
+
+ /**
+ * Called from View when the position listener is triggered
+ */
+ void updateKeepClearRectsForView(View view) {
+ mKeepClearRectsTracker.updateRectsForView(view);
+ mHandler.sendEmptyMessage(MSG_KEEP_CLEAR_RECTS_CHANGED);
+ }
+
+ void keepClearRectsChanged() {
+ final List<Rect> rectsForWindowManager = mKeepClearRectsTracker.computeChangedRects();
+ if (rectsForWindowManager != null && mView != null) {
+ try {
+ mWindowSession.reportKeepClearAreasChanged(mWindow, rectsForWindowManager);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
/**
@@ -5273,6 +5298,7 @@
private static final int MSG_HIDE_INSETS = 35;
private static final int MSG_REQUEST_SCROLL_CAPTURE = 36;
private static final int MSG_WINDOW_TOUCH_MODE_CHANGED = 37;
+ private static final int MSG_KEEP_CLEAR_RECTS_CHANGED = 38;
final class ViewRootHandler extends Handler {
@@ -5341,6 +5367,8 @@
return "MSG_HIDE_INSETS";
case MSG_WINDOW_TOUCH_MODE_CHANGED:
return "MSG_WINDOW_TOUCH_MODE_CHANGED";
+ case MSG_KEEP_CLEAR_RECTS_CHANGED:
+ return "MSG_KEEP_CLEAR_RECTS_CHANGED";
}
return super.getMessageName(message);
}
@@ -5564,7 +5592,10 @@
} break;
case MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED: {
systemGestureExclusionChanged();
- } break;
+ } break;
+ case MSG_KEEP_CLEAR_RECTS_CHANGED: {
+ keepClearRectsChanged();
+ } break;
case MSG_REQUEST_SCROLL_CAPTURE:
handleScrollCaptureRequest((IScrollCaptureResponseListener) msg.obj);
break;
@@ -7909,6 +7940,10 @@
mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
mTempControls, mSurfaceSize);
+ final int transformHint = SurfaceControl.rotationToBufferTransform(
+ (mDisplayInstallOrientation + mDisplay.getRotation()) % 4);
+ mSurfaceControl.setTransformHint(transformHint);
+
if (mAttachInfo.mContentCaptureManager != null) {
MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager
.getMainContentCaptureSession();
@@ -7927,7 +7962,6 @@
mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl);
mAttachInfo.mThreadedRenderer.setBlastBufferQueue(mBlastBufferQueue);
}
- int transformHint = mSurfaceControl.getTransformHint();
if (mPreviousTransformHint != transformHint) {
mPreviousTransformHint = transformHint;
dispatchTransformHintChanged(transformHint);
diff --git a/core/java/android/view/ViewRootRectTracker.java b/core/java/android/view/ViewRootRectTracker.java
new file mode 100644
index 0000000..fd9cc19
--- /dev/null
+++ b/core/java/android/view/ViewRootRectTracker.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Rect;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Function;
+
+/**
+ * Abstract class to track a collection of rects reported by the views under the same
+ * {@link ViewRootImpl}.
+ */
+class ViewRootRectTracker {
+ private final Function<View, List<Rect>> mRectCollector;
+ private boolean mViewsChanged = false;
+ private boolean mRootRectsChanged = false;
+ private List<Rect> mRootRects = Collections.emptyList();
+ private List<ViewInfo> mViewInfos = new ArrayList<>();
+ private List<Rect> mRects = Collections.emptyList();
+
+ /**
+ * @param rectCollector given a view returns a list of the rects of interest for this
+ * ViewRootRectTracker
+ */
+ ViewRootRectTracker(Function<View, List<Rect>> rectCollector) {
+ mRectCollector = rectCollector;
+ }
+
+ public void updateRectsForView(@NonNull View view) {
+ boolean found = false;
+ final Iterator<ViewInfo> i = mViewInfos.iterator();
+ while (i.hasNext()) {
+ final ViewInfo info = i.next();
+ final View v = info.getView();
+ if (v == null || !v.isAttachedToWindow() || !v.isAggregatedVisible()) {
+ mViewsChanged = true;
+ i.remove();
+ continue;
+ }
+ if (v == view) {
+ found = true;
+ info.mDirty = true;
+ break;
+ }
+ }
+ if (!found && view.isAttachedToWindow()) {
+ mViewInfos.add(new ViewInfo(view));
+ mViewsChanged = true;
+ }
+ }
+
+ /**
+ * @return all visible rects from all views in the global (root) coordinate system
+ */
+ @Nullable
+ public List<Rect> computeChangedRects() {
+ boolean changed = mRootRectsChanged;
+ final Iterator<ViewInfo> i = mViewInfos.iterator();
+ final List<Rect> rects = new ArrayList<>(mRootRects);
+ while (i.hasNext()) {
+ final ViewInfo info = i.next();
+ switch (info.update()) {
+ case ViewInfo.CHANGED:
+ changed = true;
+ // Deliberate fall-through
+ case ViewInfo.UNCHANGED:
+ rects.addAll(info.mRects);
+ break;
+ case ViewInfo.GONE:
+ mViewsChanged = true;
+ i.remove();
+ break;
+ }
+ }
+ if (changed || mViewsChanged) {
+ mViewsChanged = false;
+ mRootRectsChanged = false;
+ if (!mRects.equals(rects)) {
+ mRects = rects;
+ return rects;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Sets rects defined in the global (root) coordinate system, i.e. not for a specific view.
+ */
+ public void setRootRects(@NonNull List<Rect> rects) {
+ Preconditions.checkNotNull(rects, "rects must not be null");
+ mRootRects = rects;
+ mRootRectsChanged = true;
+ }
+
+ @NonNull
+ public List<Rect> getRootRects() {
+ return mRootRects;
+ }
+
+ @NonNull
+ private List<Rect> getTrackedRectsForView(@NonNull View v) {
+ final List<Rect> rects = mRectCollector.apply(v);
+ return rects == null ? Collections.emptyList() : rects;
+ }
+
+ private class ViewInfo {
+ public static final int CHANGED = 0;
+ public static final int UNCHANGED = 1;
+ public static final int GONE = 2;
+
+ private final WeakReference<View> mView;
+ boolean mDirty = true;
+ List<Rect> mRects = Collections.emptyList();
+
+ ViewInfo(View view) {
+ mView = new WeakReference<>(view);
+ }
+
+ public View getView() {
+ return mView.get();
+ }
+
+ public int update() {
+ final View view = getView();
+ if (view == null || !view.isAttachedToWindow()
+ || !view.isAggregatedVisible()) return GONE;
+ final List<Rect> localRects = getTrackedRectsForView(view);
+ final List<Rect> newRects = new ArrayList<>(localRects.size());
+ for (Rect src : localRects) {
+ Rect mappedRect = new Rect(src);
+ ViewParent p = view.getParent();
+ if (p != null && p.getChildVisibleRect(view, mappedRect, null)) {
+ newRects.add(mappedRect);
+ }
+ }
+
+ if (mRects.equals(localRects)) return UNCHANGED;
+ mRects = newRects;
+ return CHANGED;
+ }
+ }
+}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 5176f9b..998498b 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -28,6 +28,7 @@
import android.util.Log;
import android.util.MergedConfiguration;
import android.window.ClientWindowFrames;
+import android.window.IOnBackInvokedCallback;
import java.util.HashMap;
import java.util.Objects;
@@ -459,6 +460,11 @@
}
@Override
+ public void reportKeepClearAreasChanged(android.view.IWindow window,
+ java.util.List<android.graphics.Rect> exclusionRects) {
+ }
+
+ @Override
public void grantInputChannel(int displayId, SurfaceControl surface, IWindow window,
IBinder hostInputToken, int flags, int privateFlags, int type,
InputChannel outInputChannel) {
@@ -496,6 +502,10 @@
}
@Override
+ public void setOnBackInvokedCallback(IWindow iWindow,
+ IOnBackInvokedCallback iOnBackInvokedCallback) throws RemoteException { }
+
+ @Override
public boolean dropForAccessibility(IWindow window, int x, int y) {
return false;
}
diff --git a/core/java/android/view/accessibility/CaptioningManager.java b/core/java/android/view/accessibility/CaptioningManager.java
index 05c74f2..4f9781b 100644
--- a/core/java/android/view/accessibility/CaptioningManager.java
+++ b/core/java/android/view/accessibility/CaptioningManager.java
@@ -254,7 +254,13 @@
* Returns true if system wide call captioning is enabled for this device.
*/
public boolean isCallCaptioningEnabled() {
- return mResources.getBoolean(R.bool.config_systemCaptionsServiceCallsEnabled);
+ try {
+ return mResources.getBoolean(
+ R.bool.config_systemCaptionsServiceCallsEnabled);
+ } catch (Resources.NotFoundException e) {
+ // The resource may not be defined, return false in that case
+ return false;
+ }
}
private void notifyEnabledChanged() {
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 6fc246e..6a22023 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2139,7 +2139,7 @@
view.onInputConnectionOpenedInternal(ic, tba, icHandler);
final ViewRootImpl viewRoot = view.getViewRootImpl();
if (viewRoot != null) {
- viewRoot.getHandwritingInitiator().onInputConnectionCreated(view, tba);
+ viewRoot.getHandwritingInitiator().onInputConnectionCreated(view);
}
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index ac28c31..dd70d69 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -17,6 +17,7 @@
package android.widget;
import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
+import static android.widget.TextView.ACCESSIBILITY_ACTION_SMART_START_ID;
import android.R;
import android.animation.ValueAnimator;
@@ -84,6 +85,7 @@
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.Pair;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.ActionMode;
@@ -112,6 +114,7 @@
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.animation.LinearInterpolator;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.CursorAnchorInfo;
@@ -449,11 +452,14 @@
private int mLineChangeSlopMax;
private int mLineChangeSlopMin;
+ private final AccessibilitySmartActions mA11ySmartActions;
+
Editor(TextView textView) {
mTextView = textView;
// Synchronize the filter list, which places the undo input filter at the end.
mTextView.setFilters(mTextView.getFilters());
mProcessTextIntentActionsHandler = new ProcessTextIntentActionsHandler(this);
+ mA11ySmartActions = new AccessibilitySmartActions(mTextView);
mHapticTextHandleEnabled = mTextView.getContext().getResources().getBoolean(
com.android.internal.R.bool.config_enableHapticTextHandle);
@@ -4381,6 +4387,7 @@
item.setShowAsAction(showAsAction);
mAssistClickHandlers.put(item,
TextClassification.createIntentOnClickListener(action.getActionIntent()));
+ mA11ySmartActions.addAction(action);
return item;
}
@@ -4394,6 +4401,7 @@
}
i++;
}
+ mA11ySmartActions.reset();
}
private boolean hasLegacyAssistItem(TextClassification classification) {
@@ -7656,7 +7664,7 @@
private final PackageManager mPackageManager;
private final String mPackageName;
private final SparseArray<Intent> mAccessibilityIntents = new SparseArray<>();
- private final SparseArray<AccessibilityNodeInfo.AccessibilityAction> mAccessibilityActions =
+ private final SparseArray<AccessibilityAction> mAccessibilityActions =
new SparseArray<>();
private final List<ResolveInfo> mSupportedActivities = new ArrayList<>();
@@ -7706,8 +7714,7 @@
int actionId = TextView.ACCESSIBILITY_ACTION_PROCESS_TEXT_START_ID + i++;
mAccessibilityActions.put(
actionId,
- new AccessibilityNodeInfo.AccessibilityAction(
- actionId, getLabel(resolveInfo)));
+ new AccessibilityAction(actionId, getLabel(resolveInfo)));
mAccessibilityIntents.put(
actionId, createProcessTextIntentForResolveInfo(resolveInfo));
}
@@ -7786,6 +7793,65 @@
}
}
+ /**
+ * Accessibility helper for "smart" (i.e. textAssist) actions.
+ * Helps ensure that "smart" actions are shown in the accessibility menu.
+ * NOTE that these actions are only available when an action mode is live.
+ *
+ * @hide
+ */
+ private static final class AccessibilitySmartActions {
+
+ private final TextView mTextView;
+ private final SparseArray<Pair<AccessibilityAction, RemoteAction>> mActions =
+ new SparseArray<>();
+
+ private AccessibilitySmartActions(TextView textView) {
+ mTextView = Objects.requireNonNull(textView);
+ }
+
+ private void addAction(RemoteAction action) {
+ final int actionId = ACCESSIBILITY_ACTION_SMART_START_ID + mActions.size();
+ mActions.put(actionId,
+ new Pair(new AccessibilityAction(actionId, action.getTitle()), action));
+ }
+
+ private void reset() {
+ mActions.clear();
+ }
+
+ void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo nodeInfo) {
+ for (int i = 0; i < mActions.size(); i++) {
+ nodeInfo.addAction(mActions.valueAt(i).first);
+ }
+ }
+
+ boolean performAccessibilityAction(int actionId) {
+ final Pair<AccessibilityAction, RemoteAction> pair = mActions.get(actionId);
+ if (pair != null) {
+ TextClassification.createIntentOnClickListener(pair.second.getActionIntent())
+ .onClick(mTextView);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Initializes the nodeInfo with smart actions.
+ */
+ void onInitializeSmartActionsAccessibilityNodeInfo(AccessibilityNodeInfo nodeInfo) {
+ mA11ySmartActions.onInitializeAccessibilityNodeInfo(nodeInfo);
+ }
+
+ /**
+ * Handles the accessibility action if it is an active smart action.
+ * Return false if this method does not hanle the action.
+ */
+ boolean performSmartActionsAccessibilityAction(int actionId) {
+ return mA11ySmartActions.performAccessibilityAction(actionId);
+ }
+
static void logCursor(String location, @Nullable String msgFormat, Object ... msgArgs) {
if (msgFormat == null) {
Log.d(TAG, location);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0fe06be..7161730 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -442,6 +442,9 @@
// Accessibility action start id for "process text" actions.
static final int ACCESSIBILITY_ACTION_PROCESS_TEXT_START_ID = 0x10000100;
+ /** Accessibility action start id for "smart" actions. @hide */
+ static final int ACCESSIBILITY_ACTION_SMART_START_ID = 0x10001000;
+
/**
* @hide
*/
@@ -12223,6 +12226,7 @@
}
if (canProcessText()) { // also implies mEditor is not null.
mEditor.mProcessTextIntentActionsHandler.onInitializeAccessibilityNodeInfo(info);
+ mEditor.onInitializeSmartActionsAccessibilityNodeInfo(info);
}
}
@@ -12426,9 +12430,11 @@
*/
@Override
public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
- if (mEditor != null
- && mEditor.mProcessTextIntentActionsHandler.performAccessibilityAction(action)) {
- return true;
+ if (mEditor != null) {
+ if (mEditor.mProcessTextIntentActionsHandler.performAccessibilityAction(action)
+ || mEditor.performSmartActionsAccessibilityAction(action)) {
+ return true;
+ }
}
switch (action) {
case AccessibilityNodeInfo.ACTION_CLICK: {
diff --git a/core/java/android/window/IOnBackInvokedCallback.aidl b/core/java/android/window/IOnBackInvokedCallback.aidl
new file mode 100644
index 0000000..a42863c
--- /dev/null
+++ b/core/java/android/window/IOnBackInvokedCallback.aidl
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 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.window;
+
+/**
+ * Interface that wraps a {@link OnBackInvokedCallback} object, to be stored in window manager
+ * and called from back handling process when back is invoked.
+ *
+ * @hide
+ */
+oneway interface IOnBackInvokedCallback {
+ /**
+ * Called when a back gesture has been started, or back button has been pressed down.
+ * Wraps {@link OnBackInvokedCallback#onBackStarted()}.
+ */
+ void onBackStarted();
+
+ /**
+ * Called on back gesture progress.
+ * Wraps {@link OnBackInvokedCallback#onBackProgressed()}.
+ *
+ * @param touchX Absolute X location of the touch point.
+ * @param touchY Absolute Y location of the touch point.
+ * @param progress Value between 0 and 1 on how far along the back gesture is.
+ */
+ void onBackProgressed(int touchX, int touchY, float progress);
+
+ /**
+ * Called when a back gesture or back button press has been cancelled.
+ * Wraps {@link OnBackInvokedCallback#onBackCancelled()}.
+ */
+ void onBackCancelled();
+
+ /**
+ * Called when a back gesture has been completed and committed, or back button pressed
+ * has been released and committed.
+ * Wraps {@link OnBackInvokedCallback#onBackInvoked()}.
+ */
+ void onBackInvoked();
+}
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 53734eb..2978604 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -17,11 +17,19 @@
package android.window;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
import android.view.IWindow;
import android.view.IWindowSession;
import android.view.OnBackInvokedCallback;
import android.view.OnBackInvokedDispatcher;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.TreeMap;
+
/**
* Provides window based implementation of {@link OnBackInvokedDispatcher}.
*
@@ -39,6 +47,18 @@
public class WindowOnBackInvokedDispatcher extends OnBackInvokedDispatcher {
private IWindowSession mWindowSession;
private IWindow mWindow;
+ private static final String TAG = "WindowOnBackDispatcher";
+ private static final boolean DEBUG = false;
+
+ /** The currently most prioritized callback. */
+ @Nullable
+ private OnBackInvokedCallbackWrapper mTopCallback;
+
+ /** Convenience hashmap to quickly decide if a callback has been added. */
+ private final HashMap<OnBackInvokedCallback, Integer> mAllCallbacks = new HashMap<>();
+ /** Holds all callbacks by priorities. */
+ private final TreeMap<Integer, ArrayList<OnBackInvokedCallback>>
+ mOnBackInvokedCallbacks = new TreeMap<>();
/**
* Sends the pending top callback (if one exists) to WM when the view root
@@ -47,7 +67,9 @@
public void attachToWindow(@NonNull IWindowSession windowSession, @NonNull IWindow window) {
mWindowSession = windowSession;
mWindow = window;
- // TODO(b/209867448): Send the top callback to WM (if one exists).
+ if (mTopCallback != null) {
+ setTopOnBackInvokedCallback(mTopCallback);
+ }
}
/** Detaches the dispatcher instance from its window. */
@@ -56,20 +78,124 @@
mWindowSession = null;
}
+ // TODO: Take an Executor for the callback to run on.
@Override
public void registerOnBackInvokedCallback(
@NonNull OnBackInvokedCallback callback, @Priority int priority) {
- // TODO(b/209867448): To be implemented.
+ if (!mOnBackInvokedCallbacks.containsKey(priority)) {
+ mOnBackInvokedCallbacks.put(priority, new ArrayList<>());
+ }
+ ArrayList<OnBackInvokedCallback> callbacks = mOnBackInvokedCallbacks.get(priority);
+
+ // If callback has already been added, remove it and re-add it.
+ if (mAllCallbacks.containsKey(callback)) {
+ if (DEBUG) {
+ Log.i(TAG, "Callback already added. Removing and re-adding it.");
+ }
+ Integer prevPriority = mAllCallbacks.get(callback);
+ mOnBackInvokedCallbacks.get(prevPriority).remove(callback);
+ }
+
+ callbacks.add(callback);
+ mAllCallbacks.put(callback, priority);
+ if (mTopCallback == null || (mTopCallback.getCallback() != callback
+ && mAllCallbacks.get(mTopCallback.getCallback()) <= priority)) {
+ setTopOnBackInvokedCallback(new OnBackInvokedCallbackWrapper(callback, priority));
+ }
}
@Override
- public void unregisterOnBackInvokedCallback(
- @NonNull OnBackInvokedCallback callback) {
- // TODO(b/209867448): To be implemented.
+ public void unregisterOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) {
+ if (!mAllCallbacks.containsKey(callback)) {
+ if (DEBUG) {
+ Log.i(TAG, "Callback not found. returning...");
+ }
+ return;
+ }
+ Integer priority = mAllCallbacks.get(callback);
+ mOnBackInvokedCallbacks.get(priority).remove(callback);
+ mAllCallbacks.remove(callback);
+ if (mTopCallback != null && mTopCallback.getCallback() == callback) {
+ findAndSetTopOnBackInvokedCallback();
+ }
}
/** Clears all registered callbacks on the instance. */
public void clear() {
- // TODO(b/209867448): To be implemented.
+ mAllCallbacks.clear();
+ mTopCallback = null;
+ mOnBackInvokedCallbacks.clear();
+ }
+
+ /**
+ * Iterates through all callbacks to find the most prioritized one and pushes it to
+ * window manager.
+ */
+ private void findAndSetTopOnBackInvokedCallback() {
+ if (mAllCallbacks.isEmpty()) {
+ setTopOnBackInvokedCallback(null);
+ return;
+ }
+
+ for (Integer priority : mOnBackInvokedCallbacks.descendingKeySet()) {
+ ArrayList<OnBackInvokedCallback> callbacks = mOnBackInvokedCallbacks.get(priority);
+ if (!callbacks.isEmpty()) {
+ OnBackInvokedCallbackWrapper callback = new OnBackInvokedCallbackWrapper(
+ callbacks.get(callbacks.size() - 1), priority);
+ setTopOnBackInvokedCallback(callback);
+ return;
+ }
+ }
+ setTopOnBackInvokedCallback(null);
+ }
+
+ // Pushes the top priority callback to window manager.
+ private void setTopOnBackInvokedCallback(@Nullable OnBackInvokedCallbackWrapper callback) {
+ mTopCallback = callback;
+ if (mWindowSession == null || mWindow == null) {
+ return;
+ }
+ try {
+ mWindowSession.setOnBackInvokedCallback(mWindow, mTopCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to set OnBackInvokedCallback to WM. Error: " + e);
+ }
+ }
+
+ private class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
+ private final OnBackInvokedCallback mCallback;
+ private final @Priority int mPriority;
+
+ OnBackInvokedCallbackWrapper(
+ @NonNull OnBackInvokedCallback callback, @Priority int priority) {
+ mCallback = callback;
+ mPriority = priority;
+ }
+
+ @NonNull
+ public OnBackInvokedCallback getCallback() {
+ return mCallback;
+ }
+
+ @Override
+ public void onBackStarted() throws RemoteException {
+ Handler.getMain().post(() -> mCallback.onBackStarted());
+ }
+
+ @Override
+ public void onBackProgressed(int touchX, int touchY, float progress)
+ throws RemoteException {
+ Handler.getMain().post(() -> mCallback.onBackProgressed(touchX, touchY, progress));
+ }
+
+ @Override
+ public void onBackCancelled() throws RemoteException {
+ Handler.getMain().post(() -> mCallback.onBackCancelled());
+ }
+
+ @Override
+ public void onBackInvoked() throws RemoteException {
+ Handler.getMain().post(() -> mCallback.onBackInvoked());
+ }
}
}
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 14fd4c2..40ca9fb 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -90,6 +90,14 @@
public static final ComponentName ACCESSIBILITY_BUTTON_COMPONENT_NAME =
new ComponentName("com.android.server.accessibility", "AccessibilityButton");
+ public static final ComponentName COLOR_INVERSION_TILE_COMPONENT_NAME =
+ new ComponentName("com.android.server.accessibility", "ColorInversionTile");
+ public static final ComponentName DALTONIZER_TILE_COMPONENT_NAME =
+ new ComponentName("com.android.server.accessibility", "ColorCorrectionTile");
+ public static final ComponentName ONE_HANDED_TILE_COMPONENT_NAME =
+ new ComponentName("com.android.server.accessibility", "OneHandedModeTile");
+ public static final ComponentName REDUCE_BRIGHT_COLORS_TILE_SERVICE_COMPONENT_NAME =
+ new ComponentName("com.android.server.accessibility", "ReduceBrightColorsTile");
private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index b723db2..4ad232a 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -467,8 +467,21 @@
}
protected void showEmptyState(ResolverListAdapter activeListAdapter,
+ @DrawableRes int iconRes, String title, String subtitle) {
+ showEmptyState(activeListAdapter, iconRes, title, subtitle, /* buttonOnClick */ null);
+ }
+
+ protected void showEmptyState(ResolverListAdapter activeListAdapter,
@DrawableRes int iconRes, @StringRes int titleRes, @StringRes int subtitleRes,
View.OnClickListener buttonOnClick) {
+ String title = titleRes == 0 ? null : mContext.getString(titleRes);
+ String subtitle = subtitleRes == 0 ? null : mContext.getString(subtitleRes);
+ showEmptyState(activeListAdapter, iconRes, title, subtitle, buttonOnClick);
+ }
+
+ protected void showEmptyState(ResolverListAdapter activeListAdapter,
+ @DrawableRes int iconRes, String title, String subtitle,
+ View.OnClickListener buttonOnClick) {
ProfileDescriptor descriptor = getItem(
userHandleToPageIndex(activeListAdapter.getUserHandle()));
descriptor.rootView.findViewById(R.id.resolver_list).setVisibility(View.GONE);
@@ -479,15 +492,15 @@
View container = emptyStateView.findViewById(R.id.resolver_empty_state_container);
setupContainerPadding(container);
- TextView title = emptyStateView.findViewById(R.id.resolver_empty_state_title);
- title.setText(titleRes);
+ TextView titleView = emptyStateView.findViewById(R.id.resolver_empty_state_title);
+ titleView.setText(title);
- TextView subtitle = emptyStateView.findViewById(R.id.resolver_empty_state_subtitle);
- if (subtitleRes != 0) {
- subtitle.setVisibility(View.VISIBLE);
- subtitle.setText(subtitleRes);
+ TextView subtitleView = emptyStateView.findViewById(R.id.resolver_empty_state_subtitle);
+ if (subtitle != null) {
+ subtitleView.setVisibility(View.VISIBLE);
+ subtitleView.setText(subtitle);
} else {
- subtitle.setVisibility(View.GONE);
+ subtitleView.setVisibility(View.GONE);
}
Button button = emptyStateView.findViewById(R.id.resolver_empty_state_button);
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index 3b6a877..393bff4 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -16,7 +16,17 @@
package com.android.internal.app;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_PERSONAL_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_WORK_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PAUSED_TITLE;
+
import android.annotation.Nullable;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.os.UserHandle;
import android.view.LayoutInflater;
@@ -184,8 +194,8 @@
View.OnClickListener listener) {
showEmptyState(activeListAdapter,
R.drawable.ic_work_apps_off,
- R.string.resolver_turn_on_work_apps,
- /* subtitleRes */ 0,
+ getWorkAppPausedTitle(),
+ /* subtitle = */ null,
listener);
}
@@ -194,13 +204,13 @@
if (mIsSendAction) {
showEmptyState(activeListAdapter,
R.drawable.ic_sharing_disabled,
- R.string.resolver_cross_profile_blocked,
- R.string.resolver_cant_share_with_work_apps_explanation);
+ getCrossProfileBlockedTitle(),
+ getCantShareWithWorkMessage());
} else {
showEmptyState(activeListAdapter,
R.drawable.ic_sharing_disabled,
- R.string.resolver_cross_profile_blocked,
- R.string.resolver_cant_access_work_apps_explanation);
+ getCrossProfileBlockedTitle(),
+ getCantAccessWorkMessage());
}
}
@@ -209,13 +219,13 @@
if (mIsSendAction) {
showEmptyState(activeListAdapter,
R.drawable.ic_sharing_disabled,
- R.string.resolver_cross_profile_blocked,
- R.string.resolver_cant_share_with_personal_apps_explanation);
+ getCrossProfileBlockedTitle(),
+ getCantShareWithPersonalMessage());
} else {
showEmptyState(activeListAdapter,
R.drawable.ic_sharing_disabled,
- R.string.resolver_cross_profile_blocked,
- R.string.resolver_cant_access_personal_apps_explanation);
+ getCrossProfileBlockedTitle(),
+ getCantAccessPersonalMessage());
}
}
@@ -223,8 +233,8 @@
protected void showNoPersonalAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
showEmptyState(listAdapter,
R.drawable.ic_no_apps,
- R.string.resolver_no_personal_apps_available,
- /* subtitleRes */ 0);
+ getNoPersonalAppsAvailableMessage(),
+ /* subtitle= */ null);
}
@@ -232,10 +242,65 @@
protected void showNoWorkAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
showEmptyState(listAdapter,
R.drawable.ic_no_apps,
- R.string.resolver_no_work_apps_available,
- /* subtitleRes */ 0);
+ getNoWorkAppsAvailableMessage(),
+ /* subtitle = */ null);
}
+ private String getWorkAppPausedTitle() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_WORK_PAUSED_TITLE,
+ () -> getContext().getString(R.string.resolver_turn_on_work_apps));
+ }
+
+ private String getCrossProfileBlockedTitle() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE,
+ () -> getContext().getString(R.string.resolver_cross_profile_blocked));
+ }
+
+ private String getCantShareWithWorkMessage() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_CANT_SHARE_WITH_WORK,
+ () -> getContext().getString(
+ R.string.resolver_cant_share_with_work_apps_explanation));
+ }
+
+ private String getCantShareWithPersonalMessage() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_CANT_SHARE_WITH_PERSONAL,
+ () -> getContext().getString(
+ R.string.resolver_cant_share_with_personal_apps_explanation));
+ }
+
+ private String getCantAccessWorkMessage() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_CANT_ACCESS_WORK,
+ () -> getContext().getString(
+ R.string.resolver_cant_access_work_apps_explanation));
+ }
+
+ private String getCantAccessPersonalMessage() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_CANT_ACCESS_PERSONAL,
+ () -> getContext().getString(
+ R.string.resolver_cant_access_personal_apps_explanation));
+ }
+
+ private String getNoWorkAppsAvailableMessage() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_NO_WORK_APPS,
+ () -> getContext().getString(
+ R.string.resolver_no_work_apps_available));
+ }
+
+ private String getNoPersonalAppsAvailableMessage() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_NO_PERSONAL_APPS,
+ () -> getContext().getString(
+ R.string.resolver_no_personal_apps_available));
+ }
+
+
void setEmptyStateBottomOffset(int bottomOffset) {
mBottomOffset = bottomOffset;
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 587876d..9648008 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -21,6 +21,7 @@
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
+import android.os.BluetoothBatteryStats;
import android.os.ParcelFileDescriptor;
import android.os.WakeLockStats;
import android.os.WorkSource;
@@ -162,6 +163,10 @@
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BATTERY_STATS)")
WakeLockStats getWakeLockStats();
+ /** {@hide} */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BATTERY_STATS)")
+ BluetoothBatteryStats getBluetoothBatteryStats();
+
HealthStatsParceler takeUidSnapshot(int uid);
HealthStatsParceler[] takeUidSnapshots(in int[] uid);
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 0f37dc5..25b8dba 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -16,13 +16,14 @@
package com.android.internal.app;
+import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_WORK;
import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
import static com.android.internal.app.ResolverActivity.EXTRA_CALLING_USER;
import static com.android.internal.app.ResolverActivity.EXTRA_SELECTED_PROFILE;
import android.annotation.Nullable;
-import android.annotation.StringRes;
import android.app.Activity;
import android.app.ActivityThread;
import android.app.AppGlobals;
@@ -101,16 +102,16 @@
Intent intentReceived = getIntent();
String className = intentReceived.getComponent().getClassName();
final int targetUserId;
- final int userMessageId;
+ final String userMessage;
if (className.equals(FORWARD_INTENT_TO_PARENT)) {
- userMessageId = com.android.internal.R.string.forward_intent_to_owner;
+ userMessage = getForwardToPersonalMessage();
targetUserId = getProfileParent();
getMetricsLogger().write(
new LogMaker(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE)
.setSubtype(MetricsEvent.PARENT_PROFILE));
} else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
- userMessageId = com.android.internal.R.string.forward_intent_to_work;
+ userMessage = getForwardToWorkMessage();
targetUserId = getManagedProfile();
getMetricsLogger().write(
@@ -118,7 +119,7 @@
.setSubtype(MetricsEvent.MANAGED_PROFILE));
} else {
Slog.wtf(TAG, IntentForwarderActivity.class.getName() + " cannot be called directly");
- userMessageId = -1;
+ userMessage = null;
targetUserId = UserHandle.USER_NULL;
}
if (targetUserId == UserHandle.USER_NULL) {
@@ -156,11 +157,23 @@
return targetResolveInfo;
}, mExecutorService)
.thenAcceptAsync(result -> {
- maybeShowDisclosure(intentReceived, result, userMessageId);
+ maybeShowDisclosure(intentReceived, result, userMessage);
finish();
}, getApplicationContext().getMainExecutor());
}
+ private String getForwardToPersonalMessage() {
+ return getSystemService(DevicePolicyManager.class).getString(
+ FORWARD_INTENT_TO_PERSONAL,
+ () -> getString(com.android.internal.R.string.forward_intent_to_owner));
+ }
+
+ private String getForwardToWorkMessage() {
+ return getSystemService(DevicePolicyManager.class).getString(
+ FORWARD_INTENT_TO_WORK,
+ () -> getString(com.android.internal.R.string.forward_intent_to_work));
+ }
+
private boolean isIntentForwarderResolveInfo(ResolveInfo resolveInfo) {
if (resolveInfo == null) {
return false;
@@ -183,9 +196,9 @@
}
private void maybeShowDisclosure(
- Intent intentReceived, ResolveInfo resolveInfo, int messageId) {
- if (shouldShowDisclosure(resolveInfo, intentReceived)) {
- mInjector.showToast(messageId, Toast.LENGTH_LONG);
+ Intent intentReceived, ResolveInfo resolveInfo, @Nullable String message) {
+ if (shouldShowDisclosure(resolveInfo, intentReceived) && message != null) {
+ mInjector.showToast(message, Toast.LENGTH_LONG);
}
}
@@ -405,8 +418,8 @@
}
@Override
- public void showToast(int messageId, int duration) {
- Toast.makeText(IntentForwarderActivity.this, getString(messageId), duration).show();
+ public void showToast(String message, int duration) {
+ Toast.makeText(IntentForwarderActivity.this, message, duration).show();
}
}
@@ -419,6 +432,6 @@
CompletableFuture<ResolveInfo> resolveActivityAsUser(Intent intent, int flags, int userId);
- void showToast(@StringRes int messageId, int duration);
+ void showToast(String message, int duration);
}
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index f9a8c7b..347153c 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -17,6 +17,13 @@
package com.android.internal.app;
import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
+import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_PERSONAL_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_PERSONAL_TAB_ACCESSIBILITY;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PROFILE_NOT_SUPPORTED;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_TAB_ACCESSIBILITY;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.PermissionChecker.PID_UNKNOWN;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
@@ -32,6 +39,7 @@
import android.app.VoiceInteractor.PickOptionRequest.Option;
import android.app.VoiceInteractor.Prompt;
import android.app.admin.DevicePolicyEventLogger;
+import android.app.admin.DevicePolicyManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -123,7 +131,7 @@
protected View mProfileView;
private int mLastSelected = AbsListView.INVALID_POSITION;
private boolean mResolvingHome = false;
- private int mProfileSwitchMessageId = -1;
+ private String mProfileSwitchMessage;
private int mLayoutId;
@VisibleForTesting
protected final ArrayList<Intent> mIntents = new ArrayList<>();
@@ -441,7 +449,7 @@
// Determine whether we should show that intent is forwarded
// from managed profile to owner or other way around.
- setProfileSwitchMessageId(intent.getContentUserHint());
+ setProfileSwitchMessage(intent.getContentUserHint());
mLaunchedFromUid = getLaunchedFromUid();
if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) {
@@ -674,7 +682,7 @@
}
// Do not show the profile switch message anymore.
- mProfileSwitchMessageId = -1;
+ mProfileSwitchMessage = null;
onTargetSelected(dri, false);
if (!mAwaitingDelegateResponse) {
@@ -828,7 +836,7 @@
}
}
- private void setProfileSwitchMessageId(int contentUserHint) {
+ private void setProfileSwitchMessage(int contentUserHint) {
if (contentUserHint != UserHandle.USER_CURRENT &&
contentUserHint != UserHandle.myUserId()) {
UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
@@ -837,13 +845,25 @@
: false;
boolean targetIsManaged = userManager.isManagedProfile();
if (originIsManaged && !targetIsManaged) {
- mProfileSwitchMessageId = com.android.internal.R.string.forward_intent_to_owner;
+ mProfileSwitchMessage = getForwardToPersonalMsg();
} else if (!originIsManaged && targetIsManaged) {
- mProfileSwitchMessageId = com.android.internal.R.string.forward_intent_to_work;
+ mProfileSwitchMessage = getForwardToWorkMsg();
}
}
}
+ private String getForwardToPersonalMsg() {
+ return getSystemService(DevicePolicyManager.class).getString(
+ FORWARD_INTENT_TO_PERSONAL,
+ () -> getString(com.android.internal.R.string.forward_intent_to_owner));
+ }
+
+ private String getForwardToWorkMsg() {
+ return getSystemService(DevicePolicyManager.class).getString(
+ FORWARD_INTENT_TO_WORK,
+ () -> getString(com.android.internal.R.string.forward_intent_to_work));
+ }
+
/**
* Turn on launch mode that is safe to use when forwarding intents received from
* applications and running in system processes. This mode uses Activity.startActivityAsCaller
@@ -1095,9 +1115,9 @@
ResolveInfo ri = mMultiProfilePagerAdapter.getActiveListAdapter()
.resolveInfoForPosition(which, hasIndexBeenFiltered);
if (mResolvingHome && hasManagedProfile() && !supportsManagedProfiles(ri)) {
- Toast.makeText(this, String.format(getResources().getString(
- com.android.internal.R.string.activity_resolver_work_profiles_support),
- ri.activityInfo.loadLabel(getPackageManager()).toString()),
+ Toast.makeText(this,
+ getWorkProfileNotSupportedMsg(
+ ri.activityInfo.loadLabel(getPackageManager()).toString()),
Toast.LENGTH_LONG).show();
return;
}
@@ -1128,6 +1148,15 @@
}
}
+ private String getWorkProfileNotSupportedMsg(String launcherName) {
+ return getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_WORK_PROFILE_NOT_SUPPORTED,
+ () -> getString(
+ com.android.internal.R.string.activity_resolver_work_profiles_support,
+ launcherName),
+ launcherName);
+ }
+
/**
* Replace me in subclasses!
*/
@@ -1394,8 +1423,8 @@
}
// If needed, show that intent is forwarded
// from managed profile to owner or other way around.
- if (mProfileSwitchMessageId != -1) {
- Toast.makeText(this, getString(mProfileSwitchMessageId), Toast.LENGTH_LONG).show();
+ if (mProfileSwitchMessage != null) {
+ Toast.makeText(this, mProfileSwitchMessage, Toast.LENGTH_LONG).show();
}
if (!mSafeForwardingMode) {
if (cti.startAsUser(this, null, user)) {
@@ -1742,12 +1771,12 @@
viewPager.setSaveEnabled(false);
TabHost.TabSpec tabSpec = tabHost.newTabSpec(TAB_TAG_PERSONAL)
.setContent(R.id.profile_pager)
- .setIndicator(getString(R.string.resolver_personal_tab));
+ .setIndicator(getPersonalTabLabel());
tabHost.addTab(tabSpec);
tabSpec = tabHost.newTabSpec(TAB_TAG_WORK)
.setContent(R.id.profile_pager)
- .setIndicator(getString(R.string.resolver_work_tab));
+ .setIndicator(getWorkTabLabel());
tabHost.addTab(tabSpec);
TabWidget tabWidget = tabHost.getTabWidget();
@@ -1799,6 +1828,16 @@
findViewById(R.id.resolver_tab_divider).setVisibility(View.VISIBLE);
}
+ private String getPersonalTabLabel() {
+ return getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_PERSONAL_TAB, () -> getString(R.string.resolver_personal_tab));
+ }
+
+ private String getWorkTabLabel() {
+ return getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_WORK_TAB, () -> getString(R.string.resolver_work_tab));
+ }
+
void onHorizontalSwipeStateChanged(int state) {}
private void maybeHideDivider() {
@@ -1830,8 +1869,6 @@
}
private void resetTabsHeaderStyle(TabWidget tabWidget) {
- String workContentDescription = getString(R.string.resolver_work_tab_accessibility);
- String personalContentDescription = getString(R.string.resolver_personal_tab_accessibility);
for (int i = 0; i < tabWidget.getChildCount(); i++) {
View tabView = tabWidget.getChildAt(i);
TextView title = tabView.findViewById(android.R.id.title);
@@ -1839,14 +1876,26 @@
title.setTextColor(getAttrColor(this, android.R.attr.textColorTertiary));
title.setTextSize(TypedValue.COMPLEX_UNIT_PX,
getResources().getDimension(R.dimen.resolver_tab_text_size));
- if (title.getText().equals(getString(R.string.resolver_personal_tab))) {
- tabView.setContentDescription(personalContentDescription);
- } else if (title.getText().equals(getString(R.string.resolver_work_tab))) {
- tabView.setContentDescription(workContentDescription);
+ if (title.getText().equals(getPersonalTabLabel())) {
+ tabView.setContentDescription(getPersonalTabAccessibilityLabel());
+ } else if (title.getText().equals(getWorkTabLabel())) {
+ tabView.setContentDescription(getWorkTabAccessibilityLabel());
}
}
}
+ private String getPersonalTabAccessibilityLabel() {
+ return getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_PERSONAL_TAB_ACCESSIBILITY,
+ () -> getString(R.string.resolver_personal_tab_accessibility));
+ }
+
+ private String getWorkTabAccessibilityLabel() {
+ return getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_WORK_TAB_ACCESSIBILITY,
+ () -> getString(R.string.resolver_work_tab_accessibility));
+ }
+
private static int getAttrColor(Context context, int attr) {
TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
int colorAccent = ta.getColor(0, 0);
diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
index 622f166..4da59a3 100644
--- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
@@ -16,7 +16,15 @@
package com.android.internal.app;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_PERSONAL_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_WORK_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PAUSED_TITLE;
+
import android.annotation.Nullable;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.res.Resources;
import android.os.UserHandle;
@@ -196,8 +204,8 @@
View.OnClickListener listener) {
showEmptyState(activeListAdapter,
R.drawable.ic_work_apps_off,
- R.string.resolver_turn_on_work_apps,
- /* subtitleRes */ 0,
+ getWorkAppPausedTitle(),
+ /* subtitle = */ null,
listener);
}
@@ -205,32 +213,72 @@
protected void showNoPersonalToWorkIntentsEmptyState(ResolverListAdapter activeListAdapter) {
showEmptyState(activeListAdapter,
R.drawable.ic_sharing_disabled,
- R.string.resolver_cross_profile_blocked,
- R.string.resolver_cant_access_work_apps_explanation);
+ getCrossProfileBlockedTitle(),
+ getCantAccessWorkMessage());
}
@Override
protected void showNoWorkToPersonalIntentsEmptyState(ResolverListAdapter activeListAdapter) {
showEmptyState(activeListAdapter,
R.drawable.ic_sharing_disabled,
- R.string.resolver_cross_profile_blocked,
- R.string.resolver_cant_access_personal_apps_explanation);
+ getCrossProfileBlockedTitle(),
+ getCantAccessPersonalMessage());
}
@Override
protected void showNoPersonalAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
showEmptyState(listAdapter,
R.drawable.ic_no_apps,
- R.string.resolver_no_personal_apps_available,
- /* subtitleRes */ 0);
+ getNoPersonalAppsAvailableMessage(),
+ /* subtitle = */ null);
}
@Override
protected void showNoWorkAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
showEmptyState(listAdapter,
R.drawable.ic_no_apps,
- R.string.resolver_no_work_apps_available,
- /* subtitleRes */ 0);
+ getNoWorkAppsAvailableMessage(),
+ /* subtitle= */ null);
+ }
+
+ private String getWorkAppPausedTitle() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_WORK_PAUSED_TITLE,
+ () -> getContext().getString(R.string.resolver_turn_on_work_apps));
+ }
+
+ private String getCrossProfileBlockedTitle() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE,
+ () -> getContext().getString(R.string.resolver_cross_profile_blocked));
+ }
+
+ private String getCantAccessWorkMessage() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_CANT_ACCESS_WORK,
+ () -> getContext().getString(
+ R.string.resolver_cant_access_work_apps_explanation));
+ }
+
+ private String getCantAccessPersonalMessage() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_CANT_ACCESS_PERSONAL,
+ () -> getContext().getString(
+ R.string.resolver_cant_access_personal_apps_explanation));
+ }
+
+ private String getNoWorkAppsAvailableMessage() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_NO_WORK_APPS,
+ () -> getContext().getString(
+ R.string.resolver_no_work_apps_available));
+ }
+
+ private String getNoPersonalAppsAvailableMessage() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_NO_PERSONAL_APPS,
+ () -> getContext().getString(
+ R.string.resolver_no_personal_apps_available));
}
void setUseLayoutWithDefault(boolean useLayoutWithDefault) {
diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
index ca0856238..3531fad 100644
--- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java
+++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
@@ -16,11 +16,14 @@
package com.android.internal.app;
+import static android.app.admin.DevicePolicyResources.Strings.Core.UNLAUNCHABLE_APP_WORK_PAUSED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.UNLAUNCHABLE_APP_WORK_PAUSED_TITLE;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import android.app.Activity;
import android.app.AlertDialog;
+import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.Intent;
@@ -70,8 +73,8 @@
String dialogTitle;
String dialogMessage = null;
if (mReason == UNLAUNCHABLE_REASON_QUIET_MODE) {
- dialogTitle = getResources().getString(R.string.work_mode_off_title);
- dialogMessage = getResources().getString(R.string.work_mode_off_message);
+ dialogTitle = getDialogTitle();
+ dialogMessage = getDialogMessage();
} else {
Log.wtf(TAG, "Invalid unlaunchable type: " + mReason);
finish();
@@ -91,6 +94,17 @@
builder.show();
}
+ private String getDialogTitle() {
+ return getSystemService(DevicePolicyManager.class).getString(
+ UNLAUNCHABLE_APP_WORK_PAUSED_TITLE, () -> getString(R.string.work_mode_off_title));
+ }
+
+ private String getDialogMessage() {
+ return getSystemService(DevicePolicyManager.class).getString(
+ UNLAUNCHABLE_APP_WORK_PAUSED_MESSAGE,
+ () -> getString(R.string.work_mode_off_message));
+ }
+
@Override
public void onDismiss(DialogInterface dialog) {
finish();
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index 2f40d3b..3b6f8f6 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -14,10 +14,13 @@
package com.android.internal.notification;
+import static android.app.admin.DevicePolicyResources.Strings.Core.NOTIFICATION_CHANNEL_DEVICE_ADMIN;
+
import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.media.AudioAttributes;
@@ -143,7 +146,7 @@
final NotificationChannel deviceAdmin = new NotificationChannel(
DEVICE_ADMIN,
- context.getString(R.string.notification_channel_device_admin),
+ getDeviceAdminNotificationChannelName(context),
NotificationManager.IMPORTANCE_HIGH);
channelsList.add(deviceAdmin);
@@ -209,6 +212,12 @@
nm.createNotificationChannels(channelsList);
}
+ private static String getDeviceAdminNotificationChannelName(Context context) {
+ DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(NOTIFICATION_CHANNEL_DEVICE_ADMIN,
+ () -> context.getString(R.string.notification_channel_device_admin));
+ }
+
/** Remove notification channels which are no longer used */
public static void removeDeprecated(Context context) {
final NotificationManager nm = context.getSystemService(NotificationManager.class);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 7c203fb..8213c86 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -45,6 +45,7 @@
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Binder;
+import android.os.BluetoothBatteryStats;
import android.os.Build;
import android.os.Handler;
import android.os.IBatteryPropertiesRegistrar;
@@ -1196,6 +1197,48 @@
return new WakeLockStats(uidWakeLockStats);
}
+ @Override
+ @GuardedBy("this")
+ public BluetoothBatteryStats getBluetoothBatteryStats() {
+ final long elapsedRealtimeUs = mClock.elapsedRealtime() * 1000;
+ ArrayList<BluetoothBatteryStats.UidStats> uidStats = new ArrayList<>();
+ for (int i = mUidStats.size() - 1; i >= 0; i--) {
+ final Uid uid = mUidStats.valueAt(i);
+ final Timer scanTimer = uid.getBluetoothScanTimer();
+ final long scanTimeMs =
+ scanTimer != null ? scanTimer.getTotalTimeLocked(
+ elapsedRealtimeUs, STATS_SINCE_CHARGED) / 1000 : 0;
+
+ final Timer unoptimizedScanTimer = uid.getBluetoothUnoptimizedScanTimer();
+ final long unoptimizedScanTimeMs =
+ unoptimizedScanTimer != null ? unoptimizedScanTimer.getTotalTimeLocked(
+ elapsedRealtimeUs, STATS_SINCE_CHARGED) / 1000 : 0;
+
+ final Counter scanResultCounter = uid.getBluetoothScanResultCounter();
+ final int scanResultCount =
+ scanResultCounter != null ? scanResultCounter.getCountLocked(
+ STATS_SINCE_CHARGED) : 0;
+
+ final ControllerActivityCounter counter = uid.getBluetoothControllerActivity();
+ final long rxTimeMs = counter != null ? counter.getRxTimeCounter().getCountLocked(
+ STATS_SINCE_CHARGED) : 0;
+ final long txTimeMs = counter != null ? counter.getTxTimeCounters()[0].getCountLocked(
+ STATS_SINCE_CHARGED) : 0;
+
+ if (scanTimeMs != 0 || unoptimizedScanTimeMs != 0 || scanResultCount != 0
+ || rxTimeMs != 0 || txTimeMs != 0) {
+ uidStats.add(new BluetoothBatteryStats.UidStats(uid.getUid(),
+ scanTimeMs,
+ unoptimizedScanTimeMs,
+ scanResultCount,
+ rxTimeMs,
+ txTimeMs));
+ }
+ }
+
+ return new BluetoothBatteryStats(uidStats);
+ }
+
String mLastWakeupReason = null;
long mLastWakeupUptimeMs = 0;
private final HashMap<String, SamplingTimer> mWakeupReasonStats = new HashMap<>();
@@ -12763,60 +12806,58 @@
long totalTxPackets = 0;
long totalRxPackets = 0;
if (delta != null) {
- NetworkStats.Entry entry = new NetworkStats.Entry();
- final int size = delta.size();
- for (int i = 0; i < size; i++) {
- entry = delta.getValues(i, entry);
-
+ for (NetworkStats.Entry entry : delta) {
if (DEBUG_ENERGY) {
- Slog.d(TAG, "Wifi uid " + entry.uid + ": delta rx=" + entry.rxBytes
- + " tx=" + entry.txBytes + " rxPackets=" + entry.rxPackets
- + " txPackets=" + entry.txPackets);
+ Slog.d(TAG, "Wifi uid " + entry.getUid()
+ + ": delta rx=" + entry.getRxBytes()
+ + " tx=" + entry.getTxBytes()
+ + " rxPackets=" + entry.getRxPackets()
+ + " txPackets=" + entry.getTxPackets());
}
- if (entry.rxBytes == 0 && entry.txBytes == 0) {
+ if (entry.getRxBytes() == 0 && entry.getTxBytes() == 0) {
// Skip the lookup below since there is no work to do.
continue;
}
- final int uid = mapUid(entry.uid);
+ final int uid = mapUid(entry.getUid());
final Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs);
- if (entry.rxBytes != 0) {
- u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.rxBytes,
- entry.rxPackets);
- if (entry.set == NetworkStats.SET_DEFAULT) { // Background transfers
- u.noteNetworkActivityLocked(NETWORK_WIFI_BG_RX_DATA, entry.rxBytes,
- entry.rxPackets);
+ if (entry.getRxBytes() != 0) {
+ u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.getRxBytes(),
+ entry.getRxPackets());
+ if (entry.getSet() == NetworkStats.SET_DEFAULT) { // Background transfers
+ u.noteNetworkActivityLocked(NETWORK_WIFI_BG_RX_DATA, entry.getRxBytes(),
+ entry.getRxPackets());
}
mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
- entry.rxBytes);
+ entry.getRxBytes());
mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
- entry.rxPackets);
+ entry.getRxPackets());
- rxPackets.incrementValue(uid, entry.rxPackets);
+ rxPackets.incrementValue(uid, entry.getRxPackets());
// Sum the total number of packets so that the Rx Power can
// be evenly distributed amongst the apps.
- totalRxPackets += entry.rxPackets;
+ totalRxPackets += entry.getRxPackets();
}
- if (entry.txBytes != 0) {
- u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.txBytes,
- entry.txPackets);
- if (entry.set == NetworkStats.SET_DEFAULT) { // Background transfers
- u.noteNetworkActivityLocked(NETWORK_WIFI_BG_TX_DATA, entry.txBytes,
- entry.txPackets);
+ if (entry.getTxBytes() != 0) {
+ u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.getTxBytes(),
+ entry.getTxPackets());
+ if (entry.getSet() == NetworkStats.SET_DEFAULT) { // Background transfers
+ u.noteNetworkActivityLocked(NETWORK_WIFI_BG_TX_DATA, entry.getTxBytes(),
+ entry.getTxPackets());
}
mNetworkByteActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
- entry.txBytes);
+ entry.getTxBytes());
mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
- entry.txPackets);
+ entry.getTxPackets());
- txPackets.incrementValue(uid, entry.txPackets);
+ txPackets.incrementValue(uid, entry.getTxPackets());
// Sum the total number of packets so that the Tx Power can
// be evenly distributed amongst the apps.
- totalTxPackets += entry.txPackets;
+ totalTxPackets += entry.getTxPackets();
}
// Calculate consumed energy for this uid. Only do so if WifiReporting isn't
@@ -12844,7 +12885,7 @@
uidEstimatedConsumptionMah.incrementValue(u.getUid(),
mWifiPowerCalculator.calcPowerWithoutControllerDataMah(
- entry.rxPackets, entry.txPackets,
+ entry.getRxPackets(), entry.getTxPackets(),
uidRunningMs, uidScanMs, uidBatchScanMs));
}
}
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsStore.java b/core/java/com/android/internal/os/BatteryUsageStatsStore.java
index fd54b32..af82f40 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsStore.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsStore.java
@@ -58,6 +58,7 @@
new BatteryUsageStatsQuery.Builder()
.setMaxStatsAgeMs(0)
.includePowerModels()
+ .includeProcessStateData()
.build());
private static final String BATTERY_USAGE_STATS_DIR = "battery-usage-stats";
private static final String SNAPSHOT_FILE_EXTENSION = ".bus";
diff --git a/core/java/com/android/internal/os/BinderLatencyObserver.java b/core/java/com/android/internal/os/BinderLatencyObserver.java
index 20cf102..e9d55db 100644
--- a/core/java/com/android/internal/os/BinderLatencyObserver.java
+++ b/core/java/com/android/internal/os/BinderLatencyObserver.java
@@ -19,7 +19,6 @@
import android.annotation.Nullable;
import android.os.Binder;
import android.os.Handler;
-import android.os.Looper;
import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.Slog;
@@ -181,7 +180,7 @@
}
public Handler getHandler() {
- return new Handler(Looper.getMainLooper());
+ return BackgroundThread.getHandler();
}
}
diff --git a/core/java/com/android/internal/usb/DumpUtils.java b/core/java/com/android/internal/usb/DumpUtils.java
index 3260136..b06a7f4 100644
--- a/core/java/com/android/internal/usb/DumpUtils.java
+++ b/core/java/com/android/internal/usb/DumpUtils.java
@@ -244,7 +244,10 @@
writeContaminantPresenceStatus(dump, "contaminant_presence_status",
UsbPortStatusProto.CONTAMINANT_PRESENCE_STATUS,
status.getContaminantDetectionStatus());
-
+ dump.write("usb_data_enabled", UsbPortStatusProto.USB_DATA_ENABLED,
+ status.getUsbDataStatus());
+ dump.write("is_power_transfer_limited", UsbPortStatusProto.IS_POWER_TRANSFER_LIMITED,
+ status.isPowerTransferLimited());
dump.end(token);
}
}
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index 402fa64..6a626ee 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -53,8 +53,6 @@
void setSessionEnabled(IInputMethodSession session, boolean enabled);
- void revokeSession(IInputMethodSession session);
-
void showSoftInput(in IBinder showInputToken, int flags, in ResultReceiver resultReceiver);
void hideSoftInput(in IBinder hideInputToken, int flags, in ResultReceiver resultReceiver);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 2b25b8d..430d84e 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -254,7 +254,6 @@
"libandroidicu",
"libbattery",
"libbpf_android",
- "libnetdbpf",
"libnetdutils",
"libmemtrack",
"libandroidfw",
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 4357729..9a460f5 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -76,6 +76,9 @@
per-file AndroidRuntime.cpp = calin@google.com, ngeoffray@google.com, oth@google.com
# Although marked "view" this is mostly graphics stuff
per-file android_view_* = file:/graphics/java/android/graphics/OWNERS
+# File used for Android Studio layoutlib
+per-file LayoutlibLoader.cpp = file:/graphics/java/android/graphics/OWNERS
+per-file LayoutlibLoader.cpp = diegoperez@google.com, jgaillard@google.com
# Verity
per-file com_android_internal_security_Verity* = ebiggers@google.com, victorhsieh@google.com
diff --git a/core/jni/android_text_Hyphenator.cpp b/core/jni/android_text_Hyphenator.cpp
index 3651dbd..571a8e2 100644
--- a/core/jni/android_text_Hyphenator.cpp
+++ b/core/jni/android_text_Hyphenator.cpp
@@ -127,6 +127,7 @@
addHyphenator("or", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Oriya
addHyphenator("pa", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Punjabi
addHyphenator("pt", 2, 3); // Portuguese
+ addHyphenator("ru", 2, 2); // Russian
addHyphenator("sk", 2, 2); // Slovak
addHyphenator("sl", 2, 2); // Slovenian
addHyphenator("sq", 2, 2); // Albanian
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index dd5af04..d5470cc 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -91,6 +91,7 @@
jfieldID density;
jfieldID secure;
jfieldID deviceProductInfo;
+ jfieldID installOrientation;
} gStaticDisplayInfoClassInfo;
static struct {
@@ -1210,6 +1211,8 @@
env->SetBooleanField(object, gStaticDisplayInfoClassInfo.secure, info.secure);
env->SetObjectField(object, gStaticDisplayInfoClassInfo.deviceProductInfo,
convertDeviceProductInfoToJavaObject(env, info.deviceProductInfo));
+ env->SetIntField(object, gStaticDisplayInfoClassInfo.installOrientation,
+ static_cast<uint32_t>(info.installOrientation));
return object;
}
@@ -2152,6 +2155,8 @@
gStaticDisplayInfoClassInfo.deviceProductInfo =
GetFieldIDOrDie(env, infoClazz, "deviceProductInfo",
"Landroid/hardware/display/DeviceProductInfo;");
+ gStaticDisplayInfoClassInfo.installOrientation =
+ GetFieldIDOrDie(env, infoClazz, "installOrientation", "I");
jclass dynamicInfoClazz = FindClassOrDie(env, "android/view/SurfaceControl$DynamicDisplayInfo");
gDynamicDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, dynamicInfoClazz);
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 23453876..11560a5 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -225,6 +225,7 @@
repeated InsetsSourceProviderProto insets_source_providers = 35;
optional bool is_sleeping = 36;
repeated string sleep_tokens = 37;
+ repeated .android.graphics.RectProto keep_clear_areas = 38;
}
@@ -443,6 +444,7 @@
optional bool force_seamless_rotation = 42;
optional bool has_compat_scale = 43;
optional float global_scale = 44;
+ repeated .android.graphics.RectProto keep_clear_areas = 45;
}
message IdentifierProto {
diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto
index 97097ff..b3f54f9 100644
--- a/core/proto/android/service/usb.proto
+++ b/core/proto/android/service/usb.proto
@@ -196,9 +196,19 @@
message UsbPortManagerProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
+ enum HalVersion {
+ V_UNKNOWN = 0;
+ V1_0 = 10;
+ V1_1 = 11;
+ V1_2 = 12;
+ V1_3 = 13;
+ V2 = 20;
+ }
+
optional bool is_simulation_active = 1;
repeated UsbPortInfoProto usb_ports = 2;
optional bool enable_usb_data_signaling = 3;
+ optional HalVersion hal_version = 4;
}
message UsbPortInfoProto {
@@ -254,6 +264,8 @@
optional DataRole data_role = 4;
repeated UsbPortStatusRoleCombinationProto role_combinations = 5;
optional android.service.ContaminantPresenceStatus contaminant_presence_status = 6;
+ optional bool usb_data_enabled = 7;
+ optional bool is_power_transfer_limited = 8;
}
message UsbPortStatusRoleCombinationProto {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 50e9f23..a4d4069 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -6043,10 +6043,10 @@
<permission android:name="android.permission.MANAGE_TOAST_RATE_LIMITING"
android:protectionLevel="signature" />
- <!-- Allows managing the Game Mode
- @hide Used internally. -->
+ <!-- @SystemApi Allows managing the Game Mode
+ @hide -->
<permission android:name="android.permission.MANAGE_GAME_MODE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows accessing the frame rate per second of a given application
@hide -->
@@ -6166,6 +6166,19 @@
<permission android:name="android.permission.MANAGE_SAFETY_CENTER"
android:protectionLevel="internal|installer|role" />
+ <!-- @SystemApi Allows an application to access the AmbientContextEvent service.
+ @hide
+ -->
+ <permission android:name="android.permission.ACCESS_AMBIENT_CONTEXT_EVENT"
+ android:protectionLevel="internal|role"/>
+
+ <!-- @SystemApi Required by a AmbientContextEventDetectionService
+ to ensure that only the service with this permission can bind to it.
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 6a7b4af..a5da1ba 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3347,6 +3347,14 @@
<p>Note that this flag will only be respected if the View's Outline returns true from
{@link android.graphics.Outline#canClip()}. -->
<attr name="clipToOutline" format="boolean" />
+
+ <!-- <p> Sets a preference to keep the bounds of this view clear from floating windows
+ above this view's window. This informs the system that the view is considered a vital
+ area for the user and that ideally it should not be covered. Setting this is only
+ appropriate for UI where the user would likely take action to uncover it.
+ <p>The system will try to respect this, but when not possible will ignore it.
+ See {@link android.view.View#setPreferKeepClear}. -->
+ <attr name="preferKeepClear" format="boolean" />
</declare-styleable>
<!-- Attributes that can be assigned to a tag for a particular View. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d8b3785..c0c8618 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4077,6 +4077,12 @@
-->
<string name="config_defaultRotationResolverService" translatable="false"></string>
+ <!-- The component name for the default system AmbientContextEvent detection service.
+ This service must be trusted, as it can be activated without explicit consent of the user.
+ See android.service.ambientcontext.AmbientContextDetectionService.
+ -->
+ <string name="config_defaultAmbientContextDetectionService" translatable="false"></string>
+
<!-- The component name for the system-wide captions service.
This service must be trusted, as it controls part of the UI of the volume bar.
Example: "com.android.captions/.SystemCaptionsService"
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3a3bb11..42386fc 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3257,6 +3257,7 @@
<public name="localeConfig" />
<public name="showBackground" />
<public name="inheritKeyStoreKeys" />
+ <public name="preferKeepClear" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01de0000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5e88519..f639350 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3652,6 +3652,7 @@
<java-symbol type="string" name="config_defaultRotationResolverService" />
<java-symbol type="string" name="config_defaultSystemCaptionsService" />
<java-symbol type="string" name="config_defaultSystemCaptionsManagerService" />
+ <java-symbol type="string" name="config_defaultAmbientContextDetectionService" />
<java-symbol type="string" name="config_retailDemoPackage" />
<java-symbol type="string" name="config_retailDemoPackageSignature" />
diff --git a/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java b/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java
index 0cfcd8f8..b66642c 100644
--- a/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java
+++ b/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java
@@ -16,11 +16,17 @@
package android.content.pm;
+import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_PERSONAL_LABEL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_WORK_LABEL;
+
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
@@ -58,6 +64,8 @@
@Mock
private UserManager mUserManager;
@Mock
+ private DevicePolicyManager mDevicePolicyManager;
+ @Mock
private ICrossProfileApps mService;
@Mock
private Resources mResources;
@@ -75,6 +83,10 @@
when(mContext.getPackageName()).thenReturn(MY_PACKAGE);
when(mContext.getSystemServiceName(UserManager.class)).thenReturn(Context.USER_SERVICE);
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+ when(mContext.getSystemServiceName(DevicePolicyManager.class)).thenReturn(
+ Context.DEVICE_POLICY_SERVICE);
+ when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(
+ mDevicePolicyManager);
}
@Before
@@ -98,7 +110,7 @@
setValidTargetProfile(MANAGED_PROFILE);
mCrossProfileApps.getProfileSwitchingLabel(MANAGED_PROFILE);
- verify(mResources).getString(R.string.managed_profile_label);
+ verify(mDevicePolicyManager).getString(eq(SWITCH_TO_WORK_LABEL), any());
}
@Test
@@ -106,7 +118,7 @@
setValidTargetProfile(PERSONAL_PROFILE);
mCrossProfileApps.getProfileSwitchingLabel(PERSONAL_PROFILE);
- verify(mResources).getString(R.string.user_owner_label);
+ verify(mDevicePolicyManager).getString(eq(SWITCH_TO_PERSONAL_LABEL), any());
}
@Test(expected = SecurityException.class)
diff --git a/core/tests/coretests/src/android/view/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/HandwritingInitiatorTest.java
index 8f04461..5ea9199 100644
--- a/core/tests/coretests/src/android/view/HandwritingInitiatorTest.java
+++ b/core/tests/coretests/src/android/view/HandwritingInitiatorTest.java
@@ -33,7 +33,6 @@
import android.content.Context;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
-import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -57,7 +56,6 @@
private static final int TOUCH_SLOP = 8;
private static final long TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
private static final Rect sHwArea = new Rect(100, 200, 500, 500);
- private static final EditorInfo sFakeEditorInfo = new EditorInfo();
private HandwritingInitiator mHandwritingInitiator;
private View mTestView;
@@ -72,7 +70,6 @@
InputMethodManager inputMethodManager = context.getSystemService(InputMethodManager.class);
mHandwritingInitiator =
spy(new HandwritingInitiator(viewConfiguration, inputMethodManager));
- mHandwritingInitiator.updateEditorBound(sHwArea);
// mock a parent so that HandwritingInitiator can get
ViewGroup parent = new ViewGroup(context) {
@@ -82,10 +79,7 @@
}
@Override
public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
- r.left = sHwArea.left;
- r.top = sHwArea.top;
- r.right = sHwArea.right;
- r.bottom = sHwArea.bottom;
+ r.set(sHwArea);
return true;
}
};
@@ -97,7 +91,7 @@
@Test
public void onTouchEvent_startHandwriting_when_stylusMoveOnce_withinHWArea() {
- mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ mHandwritingInitiator.onInputConnectionCreated(mTestView);
final int x1 = (sHwArea.left + sHwArea.right) / 2;
final int y1 = (sHwArea.top + sHwArea.bottom) / 2;
MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
@@ -109,13 +103,13 @@
MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0);
mHandwritingInitiator.onTouchEvent(stylusEvent2);
- // Stylus movement win HandwritingArea should trigger IMM.startHandwriting once.
+ // Stylus movement within HandwritingArea should trigger IMM.startHandwriting once.
verify(mHandwritingInitiator, times(1)).startHandwriting(mTestView);
}
@Test
public void onTouchEvent_startHandwritingOnce_when_stylusMoveMultiTimes_withinHWArea() {
- mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ mHandwritingInitiator.onInputConnectionCreated(mTestView);
final int x1 = (sHwArea.left + sHwArea.right) / 2;
final int y1 = (sHwArea.top + sHwArea.bottom) / 2;
MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
@@ -152,14 +146,14 @@
mHandwritingInitiator.onTouchEvent(stylusEvent2);
// InputConnection is created after stylus movement.
- mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ mHandwritingInitiator.onInputConnectionCreated(mTestView);
verify(mHandwritingInitiator, times(1)).startHandwriting(mTestView);
}
@Test
public void onTouchEvent_notStartHandwriting_when_stylusTap_withinHWArea() {
- mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ mHandwritingInitiator.onInputConnectionCreated(mTestView);
final int x1 = 200;
final int y1 = 200;
MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
@@ -175,7 +169,7 @@
@Test
public void onTouchEvent_notStartHandwriting_when_stylusMove_outOfHWArea() {
- mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ mHandwritingInitiator.onInputConnectionCreated(mTestView);
final int x1 = 10;
final int y1 = 10;
MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
@@ -191,7 +185,7 @@
@Test
public void onTouchEvent_notStartHandwriting_when_stylusMove_afterTapTimeOut() {
- mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ mHandwritingInitiator.onInputConnectionCreated(mTestView);
final int x1 = 10;
final int y1 = 10;
final long time1 = 10L;
@@ -210,18 +204,17 @@
@Test
public void onInputConnectionCreated_inputConnectionCreated() {
- mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ mHandwritingInitiator.onInputConnectionCreated(mTestView);
assertThat(mHandwritingInitiator.mConnectedView).isNotNull();
assertThat(mHandwritingInitiator.mConnectedView.get()).isEqualTo(mTestView);
}
@Test
public void onInputConnectionCreated_inputConnectionClosed() {
- mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ mHandwritingInitiator.onInputConnectionCreated(mTestView);
mHandwritingInitiator.onInputConnectionClosed(mTestView);
assertThat(mHandwritingInitiator.mConnectedView).isNull();
- assertThat(mHandwritingInitiator.mEditorBound).isNull();
}
@Test
@@ -229,22 +222,14 @@
// When IMM restarts input connection, View#onInputConnectionCreatedInternal might be
// called before View#onInputConnectionClosedInternal. As a result, we need to handle the
// case where "one view "2 InputConnections".
- mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
- mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ mHandwritingInitiator.onInputConnectionCreated(mTestView);
+ mHandwritingInitiator.onInputConnectionCreated(mTestView);
mHandwritingInitiator.onInputConnectionClosed(mTestView);
assertThat(mHandwritingInitiator.mConnectedView).isNotNull();
assertThat(mHandwritingInitiator.mConnectedView.get()).isEqualTo(mTestView);
}
- @Test
- public void updateEditorBound() {
- Rect rect = new Rect(1, 2, 3, 4);
- mHandwritingInitiator.updateEditorBound(rect);
-
- assertThat(mHandwritingInitiator.mEditorBound).isEqualTo(rect);
- }
-
private MotionEvent createStylusEvent(int action, int x, int y, long eventTime) {
MotionEvent.PointerProperties[] properties = MotionEvent.PointerProperties.createArray(1);
properties[0].toolType = MotionEvent.TOOL_TYPE_STYLUS;
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
new file mode 100644
index 0000000..a1a1e20
--- /dev/null
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2021 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.window;
+
+import static org.mockito.ArgumentMatchers.isNull;
+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.verifyZeroInteractions;
+
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.view.IWindow;
+import android.view.IWindowSession;
+import android.view.OnBackInvokedCallback;
+import android.view.OnBackInvokedDispatcher;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link WindowOnBackInvokedDispatcherTest}
+ *
+ * <p>Build/Install/Run:
+ * atest FrameworksCoreTests:WindowOnBackInvokedDispatcherTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class WindowOnBackInvokedDispatcherTest {
+ @Mock
+ private IWindowSession mWindowSession;
+ @Mock
+ private IWindow mWindow;
+ private WindowOnBackInvokedDispatcher mDispatcher;
+ @Mock
+ private OnBackInvokedCallback mCallback1;
+ @Mock
+ private OnBackInvokedCallback mCallback2;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mDispatcher = new WindowOnBackInvokedDispatcher();
+ mDispatcher.attachToWindow(mWindowSession, mWindow);
+ }
+
+ private void waitForIdle() {
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ @Test
+ public void propagatesTopCallback_samePriority() throws RemoteException {
+ ArgumentCaptor<IOnBackInvokedCallback> captor =
+ ArgumentCaptor.forClass(IOnBackInvokedCallback.class);
+
+ mDispatcher.registerOnBackInvokedCallback(
+ mCallback1, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+ mDispatcher.registerOnBackInvokedCallback(
+ mCallback2, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+
+ verify(mWindowSession, times(2))
+ .setOnBackInvokedCallback(Mockito.eq(mWindow), captor.capture());
+ captor.getAllValues().get(0).onBackStarted();
+ waitForIdle();
+ verify(mCallback1).onBackStarted();
+ verifyZeroInteractions(mCallback2);
+
+ captor.getAllValues().get(1).onBackStarted();
+ waitForIdle();
+ verify(mCallback2).onBackStarted();
+ verifyNoMoreInteractions(mCallback1);
+ }
+
+ @Test
+ public void propagatesTopCallback_differentPriority() throws RemoteException {
+ ArgumentCaptor<IOnBackInvokedCallback> captor =
+ ArgumentCaptor.forClass(IOnBackInvokedCallback.class);
+
+ mDispatcher.registerOnBackInvokedCallback(
+ mCallback1, OnBackInvokedDispatcher.PRIORITY_OVERLAY);
+ mDispatcher.registerOnBackInvokedCallback(
+ mCallback2, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+
+ verify(mWindowSession)
+ .setOnBackInvokedCallback(Mockito.eq(mWindow), captor.capture());
+ verifyNoMoreInteractions(mWindowSession);
+ captor.getValue().onBackStarted();
+ waitForIdle();
+ verify(mCallback1).onBackStarted();
+ }
+
+ @Test
+ public void propagatesTopCallback_withRemoval() throws RemoteException {
+ mDispatcher.registerOnBackInvokedCallback(
+ mCallback1, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+ mDispatcher.registerOnBackInvokedCallback(
+ mCallback2, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+
+ reset(mWindowSession);
+ mDispatcher.unregisterOnBackInvokedCallback(mCallback1);
+ verifyZeroInteractions(mWindowSession);
+
+ mDispatcher.unregisterOnBackInvokedCallback(mCallback2);
+ verify(mWindowSession).setOnBackInvokedCallback(Mockito.eq(mWindow), isNull());
+ }
+
+
+ @Test
+ public void propagatesTopCallback_sameInstanceAddedTwice() throws RemoteException {
+ ArgumentCaptor<IOnBackInvokedCallback> captor =
+ ArgumentCaptor.forClass(IOnBackInvokedCallback.class);
+
+ mDispatcher.registerOnBackInvokedCallback(mCallback1,
+ OnBackInvokedDispatcher.PRIORITY_OVERLAY);
+ mDispatcher.registerOnBackInvokedCallback(
+ mCallback2, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+ mDispatcher.registerOnBackInvokedCallback(
+ mCallback1, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+
+ reset(mWindowSession);
+ mDispatcher.registerOnBackInvokedCallback(
+ mCallback2, OnBackInvokedDispatcher.PRIORITY_OVERLAY);
+ verify(mWindowSession)
+ .setOnBackInvokedCallback(Mockito.eq(mWindow), captor.capture());
+ captor.getValue().onBackStarted();
+ waitForIdle();
+ verify(mCallback2).onBackStarted();
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
index 43590ba..1f6b57e 100644
--- a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
@@ -297,7 +297,7 @@
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector).showToast(anyInt(), anyInt());
+ verify(sInjector).showToast(anyString(), anyInt());
}
@Test
@@ -312,7 +312,7 @@
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -324,7 +324,7 @@
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -336,7 +336,7 @@
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -348,7 +348,7 @@
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -360,7 +360,7 @@
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -372,7 +372,7 @@
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector).showToast(anyInt(), anyInt());
+ verify(sInjector).showToast(anyString(), anyInt());
}
@Test
@@ -386,7 +386,7 @@
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -399,7 +399,7 @@
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -412,7 +412,7 @@
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -425,7 +425,7 @@
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -438,7 +438,7 @@
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -452,7 +452,7 @@
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -466,7 +466,7 @@
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -480,7 +480,7 @@
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -494,7 +494,7 @@
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -507,7 +507,7 @@
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector).showToast(anyInt(), anyInt());
+ verify(sInjector).showToast(anyString(), anyInt());
}
@Test
@@ -521,7 +521,7 @@
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector).showToast(anyInt(), anyInt());
+ verify(sInjector).showToast(anyString(), anyInt());
}
@Test
@@ -535,7 +535,7 @@
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector).showToast(anyInt(), anyInt());
+ verify(sInjector).showToast(anyString(), anyInt());
}
@Test
@@ -551,7 +551,7 @@
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -692,6 +692,6 @@
}
@Override
- public void showToast(int messageId, int duration) {}
+ public void showToast(String message, int duration) {}
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
index 388cf6e..be8045d 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
@@ -37,8 +37,12 @@
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.bluetooth.BluetoothActivityEnergyInfo;
+import android.bluetooth.UidTraffic;
import android.os.BatteryStats;
+import android.os.BluetoothBatteryStats;
import android.os.WakeLockStats;
+import android.os.WorkSource;
import android.util.SparseArray;
import android.view.Display;
@@ -47,6 +51,8 @@
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
+import com.google.common.collect.ImmutableList;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -66,6 +72,8 @@
private KernelCpuUidFreqTimeReader mKernelUidCpuFreqTimeReader;
@Mock
private KernelSingleUidTimeReader mKernelSingleUidTimeReader;
+ @Mock
+ private PowerProfile mPowerProfile;
private final MockClock mMockClock = new MockClock();
private MockBatteryStatsImpl mBatteryStatsImpl;
@@ -79,6 +87,7 @@
when(mKernelUidCpuFreqTimeReader.allUidTimesAvailable()).thenReturn(true);
when(mKernelSingleUidTimeReader.singleUidCpuTimesAvailable()).thenReturn(true);
mBatteryStatsImpl = new MockBatteryStatsImpl(mMockClock)
+ .setPowerProfile(mPowerProfile)
.setKernelCpuUidFreqTimeReader(mKernelUidCpuFreqTimeReader)
.setKernelSingleUidTimeReader(mKernelSingleUidTimeReader);
}
@@ -559,4 +568,38 @@
assertThat(wakeLock2.timeHeldMs).isEqualTo(3000); // 9000-6000
assertThat(wakeLock2.totalTimeHeldMs).isEqualTo(4000); // (5000-4000) + (9000-6000)
}
+
+ @Test
+ public void testGetBluetoothBatteryStats() {
+ when(mPowerProfile.getAveragePower(
+ PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE)).thenReturn(3.0);
+ mBatteryStatsImpl.setOnBatteryInternal(true);
+ mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+
+ final WorkSource ws = new WorkSource(10042);
+ mBatteryStatsImpl.noteBluetoothScanStartedFromSourceLocked(ws, false, 1000, 1000);
+ mBatteryStatsImpl.noteBluetoothScanStoppedFromSourceLocked(ws, false, 5000, 5000);
+ mBatteryStatsImpl.noteBluetoothScanStartedFromSourceLocked(ws, true, 6000, 6000);
+ mBatteryStatsImpl.noteBluetoothScanStoppedFromSourceLocked(ws, true, 9000, 9000);
+ mBatteryStatsImpl.noteBluetoothScanResultsFromSourceLocked(ws, 42, 9000, 9000);
+
+ BluetoothActivityEnergyInfo info = new BluetoothActivityEnergyInfo(1000,
+ BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 9000, 8000, 12000, 0);
+ info.setUidTraffic(ImmutableList.of(
+ new UidTraffic(10042, 3000, 4000),
+ new UidTraffic(10043, 5000, 8000)));
+ mBatteryStatsImpl.updateBluetoothStateLocked(info, -1, 1000, 1000);
+
+ BluetoothBatteryStats stats =
+ mBatteryStatsImpl.getBluetoothBatteryStats();
+ assertThat(stats.getUidStats()).hasSize(2);
+
+ final BluetoothBatteryStats.UidStats uidStats =
+ stats.getUidStats().stream().filter(u -> u.uid == 10042).findFirst().get();
+ assertThat(uidStats.scanTimeMs).isEqualTo(7000); // 4000+3000
+ assertThat(uidStats.unoptimizedScanTimeMs).isEqualTo(3000);
+ assertThat(uidStats.scanResultCount).isEqualTo(42);
+ assertThat(uidStats.rxTimeMs).isEqualTo(7375); // Some scan time is treated as RX
+ assertThat(uidStats.txTimeMs).isEqualTo(7666); // Some scan time is treated as TX
+ }
}
diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml
index 3fdb0da..ddcab6e 100644
--- a/data/etc/com.android.settings.xml
+++ b/data/etc/com.android.settings.xml
@@ -30,6 +30,7 @@
<permission name="android.permission.MANAGE_DEBUGGING"/>
<permission name="android.permission.MANAGE_DEVICE_ADMINS"/>
<permission name="android.permission.MANAGE_FINGERPRINT"/>
+ <permission name="android.permission.MANAGE_GAME_MODE" />
<permission name="android.permission.MANAGE_USB"/>
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.MANAGE_USER_OEM_UNLOCK_STATE" />
@@ -59,5 +60,6 @@
<permission name="android.permission.READ_DREAM_STATE"/>
<permission name="android.permission.READ_DREAM_SUPPRESSION"/>
<permission name="android.permission.RESTART_WIFI_SUBSYSTEM"/>
+ <permission name="android.permission.READ_SAFETY_CENTER_STATUS" />
</privapp-permissions>
</permissions>
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index be189405..d95644a 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -30,6 +30,7 @@
<permission name="android.permission.GET_APP_OPS_STATS"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MANAGE_DEBUGGING"/>
+ <permission name="android.permission.MANAGE_GAME_MODE" />
<permission name="android.permission.MANAGE_SENSOR_PRIVACY"/>
<permission name="android.permission.MANAGE_USB"/>
<permission name="android.permission.MANAGE_USERS"/>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 1068c27..de086df 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -333,6 +333,7 @@
<permission name="android.permission.LOCAL_MAC_ADDRESS"/>
<permission name="android.permission.MANAGE_ACCESSIBILITY"/>
<permission name="android.permission.MANAGE_DEVICE_ADMINS"/>
+ <permission name="android.permission.MANAGE_GAME_MODE"/>
<permission name="android.permission.MANAGE_ROLLBACKS"/>
<permission name="android.permission.MANAGE_USB"/>
<permission name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/>
@@ -574,6 +575,7 @@
<privapp-permissions package="com.android.settings">
<permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/>
<permission name="android.permission.BIND_CELL_BROADCAST_SERVICE"/>
+ <permission name="android.permission.READ_SAFETY_CENTER_STATUS" />
</privapp-permissions>
<privapp-permissions package="com.android.bips">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 9b67cfc..0752329 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -493,12 +493,6 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "-1556507536": {
- "message": "Passing transform hint %d for window %s%s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/WindowManagerService.java"
- },
"-1554521902": {
"message": "showInsets(ime) was requested by different window: %s ",
"level": "WARN",
@@ -2761,6 +2755,12 @@
"group": "WM_SHOW_SURFACE_ALLOC",
"at": "com\/android\/server\/wm\/WindowStateAnimator.java"
},
+ "751854538": {
+ "message": "DisplayArea keep clear rects changed name =%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
+ },
"765395228": {
"message": "onAnimationFinished(): controller=%s reorderMode=%d",
"level": "DEBUG",
@@ -3691,12 +3691,6 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/DisplayAreaPolicyBuilder.java"
},
- "1884961873": {
- "message": "Sleep still need to stop %d activities",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"1891501279": {
"message": "cancelAnimation(): reason=%s",
"level": "DEBUG",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index 7db49f0..e2bc360 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.RemoteException;
import android.util.Slog;
@@ -34,6 +35,7 @@
import com.android.wm.shell.common.annotations.ShellMainThread;
import java.util.ArrayList;
+import java.util.List;
/**
* This module deals with display rotations coming from WM. When WM starts a rotation: after it has
@@ -243,6 +245,19 @@
}
}
+ private void onKeepClearAreasChanged(int displayId, List<Rect> keepClearAreas) {
+ synchronized (mDisplays) {
+ if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) {
+ Slog.w(TAG, "Skipping onKeepClearAreasChanged on unknown"
+ + " display, displayId=" + displayId);
+ return;
+ }
+ for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) {
+ mDisplayChangedListeners.get(i).onKeepClearAreasChanged(displayId, keepClearAreas);
+ }
+ }
+ }
+
private static class DisplayRecord {
private int mDisplayId;
private Context mContext;
@@ -301,6 +316,13 @@
DisplayController.this.onFixedRotationFinished(displayId);
});
}
+
+ @Override
+ public void onKeepClearAreasChanged(int displayId, List<Rect> keepClearAreas) {
+ mMainExecutor.execute(() -> {
+ DisplayController.this.onKeepClearAreasChanged(displayId, keepClearAreas);
+ });
+ }
}
/**
@@ -335,5 +357,10 @@
* Called when fixed rotation on a display is finished.
*/
default void onFixedRotationFinished(int displayId) {}
+
+ /**
+ * Called when keep-clear areas on a display have changed.
+ */
+ default void onKeepClearAreasChanged(int displayId, List<Rect> keepClearAreas) {}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt
index af629cc..f8d14c6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt
@@ -24,6 +24,9 @@
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.helpers.BaseAppHelper
+import org.junit.Assume
+import org.junit.Before
import org.junit.runner.RunWith
import org.junit.Test
import org.junit.runners.Parameterized
@@ -59,6 +62,12 @@
}
}
+ @Before
+ fun setup() {
+ // This test doesn't work in shell transitions because of b/205288792
+ Assume.assumeFalse(BaseAppHelper.isShellTransitionsEnabled())
+ }
+
@Presubmit
@Test
fun testAppIsAlwaysVisible() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
index add11c1..c93c5ad 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
@@ -25,6 +25,9 @@
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.helpers.BaseAppHelper
+import org.junit.Assume
+import org.junit.Before
import org.junit.runner.RunWith
import org.junit.Test
import org.junit.runners.Parameterized
@@ -67,6 +70,12 @@
}
}
+ @Before
+ fun setup() {
+ // This test doesn't work in shell transitions because of b/205288792
+ Assume.assumeFalse(BaseAppHelper.isShellTransitionsEnabled())
+ }
+
@Presubmit
@Test
fun testAppIsAlwaysVisible() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
index 2c08b7f..3a9a070 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
@@ -82,6 +82,11 @@
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 197726610)
+ @Test
+ override fun pipLayerExpands() = super.pipLayerExpands()
+
companion object {
/**
* Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
index e340f4c..03c8929f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
@@ -101,6 +101,11 @@
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 197726610)
+ @Test
+ override fun pipLayerExpands() = super.pipLayerExpands()
+
companion object {
/**
* Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
index 8adebb8..976b7c6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
@@ -90,6 +90,11 @@
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 215869110)
+ @Test
+ override fun focusDoesNotChange() = super.focusDoesNotChange()
+
companion object {
/**
* Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index fe66e22..35e4982 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -19,7 +19,6 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
@@ -111,7 +110,6 @@
private ActivityManager.RunningTaskInfo mHomeTask;
private ActivityManager.RunningTaskInfo mFullscreenAppTask;
private ActivityManager.RunningTaskInfo mNonResizeableFullscreenAppTask;
- private ActivityManager.RunningTaskInfo mSplitPrimaryAppTask;
@Before
public void setUp() throws RemoteException {
@@ -144,8 +142,6 @@
mNonResizeableFullscreenAppTask =
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
mNonResizeableFullscreenAppTask.isResizeable = false;
- mSplitPrimaryAppTask = createTaskInfo(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
- ACTIVITY_TYPE_STANDARD);
setRunningTask(mFullscreenAppTask);
}
diff --git a/media/aidl/android/media/audio/common/AudioOutputFlags.aidl b/media/aidl/android/media/audio/common/AudioOutputFlags.aidl
index a46229d..2556b68 100644
--- a/media/aidl/android/media/audio/common/AudioOutputFlags.aidl
+++ b/media/aidl/android/media/audio/common/AudioOutputFlags.aidl
@@ -26,7 +26,7 @@
*/
@VintfStability
@Backing(type="int")
-enum AudioOutputFlags {
+enum AudioOutputFlags{
/**
* Output must not be altered by the framework, it bypasses software mixers.
*/
@@ -98,7 +98,11 @@
*/
GAPLESS_OFFLOAD = 15,
/**
+ * Output is used for spatial audio.
+ */
+ SPATIALIZER = 16,
+ /**
* Output is used for transmitting ultrasound audio.
*/
- ULTRASOUND = 16,
+ ULTRASOUND = 17,
}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOutputFlags.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOutputFlags.aidl
index e2f286e..4a512a8 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOutputFlags.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOutputFlags.aidl
@@ -51,5 +51,6 @@
VOIP_RX = 13,
INCALL_MUSIC = 14,
GAPLESS_OFFLOAD = 15,
- ULTRASOUND = 16,
+ SPATIALIZER = 16,
+ ULTRASOUND = 17,
}
diff --git a/media/java/android/media/tv/DsmccResponse.java b/media/java/android/media/tv/DsmccResponse.java
index 4d49620..3ca63e3 100644
--- a/media/java/android/media/tv/DsmccResponse.java
+++ b/media/java/android/media/tv/DsmccResponse.java
@@ -17,10 +17,13 @@
package android.media.tv;
import android.annotation.NonNull;
+import android.annotation.StringDef;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -29,6 +32,27 @@
public static final @TvInputManager.BroadcastInfoType int responseType =
TvInputManager.BROADCAST_INFO_TYPE_DSMCC;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(prefix = "BIOP_MESSAGE_TYPE_", value = {
+ BIOP_MESSAGE_TYPE_DIRECTORY,
+ BIOP_MESSAGE_TYPE_FILE,
+ BIOP_MESSAGE_TYPE_STREAM,
+ BIOP_MESSAGE_TYPE_SERVICE_GATEWAY,
+
+ })
+ public @interface BiopMessageType {}
+
+ /** Broadcast Inter-ORB Protocol (BIOP) message types */
+ /** BIOP directory message */
+ public static final String BIOP_MESSAGE_TYPE_DIRECTORY = "directory";
+ /** BIOP file message */
+ public static final String BIOP_MESSAGE_TYPE_FILE = "file";
+ /** BIOP stream message */
+ public static final String BIOP_MESSAGE_TYPE_STREAM = "stream";
+ /** BIOP service gateway message */
+ public static final String BIOP_MESSAGE_TYPE_SERVICE_GATEWAY = "service_gateway";
+
public static final @NonNull Parcelable.Creator<DsmccResponse> CREATOR =
new Parcelable.Creator<DsmccResponse>() {
@Override
@@ -43,39 +67,173 @@
}
};
+ private final @BiopMessageType String mBiopMessageType;
private final ParcelFileDescriptor mFileDescriptor;
- private final boolean mIsDirectory;
- private final List<String> mChildren;
+ private final List<String> mChildList;
+ private final int[] mEventIds;
+ private final String[] mEventNames;
public static DsmccResponse createFromParcelBody(Parcel in) {
return new DsmccResponse(in);
}
+ /**
+ * Constructs a BIOP file message response.
+ */
public DsmccResponse(int requestId, int sequence, @ResponseResult int responseResult,
- ParcelFileDescriptor file, boolean isDirectory, List<String> children) {
+ @NonNull ParcelFileDescriptor file) {
super(responseType, requestId, sequence, responseResult);
+ mBiopMessageType = BIOP_MESSAGE_TYPE_FILE;
mFileDescriptor = file;
- mIsDirectory = isDirectory;
- mChildren = children;
+ mChildList = null;
+ mEventIds = null;
+ mEventNames = null;
}
- protected DsmccResponse(Parcel source) {
+ /**
+ * Constructs a BIOP service gateway or directory message response.
+ */
+ public DsmccResponse(int requestId, int sequence, @ResponseResult int responseResult,
+ boolean isServiceGateway, @NonNull List<String> childList) {
+ super(responseType, requestId, sequence, responseResult);
+ if (isServiceGateway) {
+ mBiopMessageType = BIOP_MESSAGE_TYPE_SERVICE_GATEWAY;
+ } else {
+ mBiopMessageType = BIOP_MESSAGE_TYPE_DIRECTORY;
+ }
+ mFileDescriptor = null;
+ mChildList = childList;
+ mEventIds = null;
+ mEventNames = null;
+ }
+
+ /**
+ * Constructs a BIOP stream message response.
+ *
+ * <p>The current stream message response does not support other stream messages types than
+ * stream event message type.
+ */
+ public DsmccResponse(int requestId, int sequence, @ResponseResult int responseResult,
+ @NonNull int[] eventIds, @NonNull String[] eventNames) {
+ super(responseType, requestId, sequence, responseResult);
+ mBiopMessageType = BIOP_MESSAGE_TYPE_STREAM;
+ mFileDescriptor = null;
+ mChildList = null;
+ mEventIds = eventIds;
+ mEventNames = eventNames;
+ if (mEventIds.length != eventNames.length) {
+ throw new IllegalStateException("The size of eventIds and eventNames must be equal");
+ }
+ }
+
+ private DsmccResponse(@NonNull Parcel source) {
super(responseType, source);
- mFileDescriptor = source.readFileDescriptor();
- mIsDirectory = (source.readInt() == 1);
- mChildren = new ArrayList<>();
- source.readStringList(mChildren);
+
+ mBiopMessageType = source.readString();
+ switch (mBiopMessageType) {
+ case BIOP_MESSAGE_TYPE_SERVICE_GATEWAY:
+ case BIOP_MESSAGE_TYPE_DIRECTORY:
+ int childNum = source.readInt();
+ mChildList = new ArrayList<>();
+ for (int i = 0; i < childNum; i++) {
+ mChildList.add(source.readString());
+ }
+ mFileDescriptor = null;
+ mEventIds = null;
+ mEventNames = null;
+ break;
+ case BIOP_MESSAGE_TYPE_FILE:
+ mFileDescriptor = source.readFileDescriptor();
+ mChildList = null;
+ mEventIds = null;
+ mEventNames = null;
+ break;
+ case BIOP_MESSAGE_TYPE_STREAM:
+ int eventNum = source.readInt();
+ mEventIds = new int[eventNum];
+ mEventNames = new String[eventNum];
+ for (int i = 0; i < eventNum; i++) {
+ mEventIds[i] = source.readInt();
+ mEventNames[i] = source.readString();
+ }
+ mChildList = null;
+ mFileDescriptor = null;
+ break;
+ default:
+ throw new IllegalStateException("unexpected BIOP message type");
+ }
}
+ /** Returns the BIOP message type */
+ @NonNull
+ public @BiopMessageType String getBiopMessageType() {
+ return mBiopMessageType;
+ }
+
+ /** Returns the file descriptor for a given file message response */
+ @NonNull
public ParcelFileDescriptor getFile() {
+ if (!mBiopMessageType.equals(BIOP_MESSAGE_TYPE_FILE)) {
+ throw new IllegalStateException("Not file object");
+ }
return mFileDescriptor;
}
+ /**
+ * Returns a list of subobject names for the given service gateway or directory message
+ * response.
+ */
+ @NonNull
+ public List<String> getChildList() {
+ if (!mBiopMessageType.equals(BIOP_MESSAGE_TYPE_DIRECTORY)
+ && !mBiopMessageType.equals(BIOP_MESSAGE_TYPE_SERVICE_GATEWAY)) {
+ throw new IllegalStateException("Not directory object");
+ }
+ return new ArrayList<String>(mChildList);
+ }
+
+ /** Returns all event IDs carried in a given stream message response. */
+ @NonNull
+ public int[] getStreamEventIds() {
+ if (!mBiopMessageType.equals(BIOP_MESSAGE_TYPE_STREAM)) {
+ throw new IllegalStateException("Not stream event object");
+ }
+ return mEventIds;
+ }
+
+ /** Returns all event names carried in a given stream message response */
+ @NonNull
+ public String[] getStreamEventNames() {
+ if (!mBiopMessageType.equals(BIOP_MESSAGE_TYPE_STREAM)) {
+ throw new IllegalStateException("Not stream event object");
+ }
+ return mEventNames;
+ }
+
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
- mFileDescriptor.writeToParcel(dest, flags);
- dest.writeInt(mIsDirectory ? 1 : 0);
- dest.writeStringList(mChildren);
+ dest.writeString(mBiopMessageType);
+ switch (mBiopMessageType) {
+ case BIOP_MESSAGE_TYPE_SERVICE_GATEWAY:
+ case BIOP_MESSAGE_TYPE_DIRECTORY:
+ dest.writeInt(mChildList.size());
+ for (String child : mChildList) {
+ dest.writeString(child);
+ }
+ break;
+ case BIOP_MESSAGE_TYPE_FILE:
+ dest.writeFileDescriptor(mFileDescriptor.getFileDescriptor());
+ break;
+ case BIOP_MESSAGE_TYPE_STREAM:
+ dest.writeInt(mEventIds.length);
+ for (int i = 0; i < mEventIds.length; i++) {
+ dest.writeInt(mEventIds[i]);
+ dest.writeString(mEventNames[i]);
+ }
+ break;
+ default:
+ throw new IllegalStateException("unexpected BIOP message type");
+ }
}
}
diff --git a/media/java/android/media/tv/StreamEventResponse.java b/media/java/android/media/tv/StreamEventResponse.java
index fd75801..903fab5c2 100644
--- a/media/java/android/media/tv/StreamEventResponse.java
+++ b/media/java/android/media/tv/StreamEventResponse.java
@@ -39,54 +39,53 @@
}
};
- private final String mName;
- private final String mText;
- private final String mData;
- private final String mStatus;
+ private final int mEventId;
+ private final long mNpt;
+ private final byte[] mData;
public static StreamEventResponse createFromParcelBody(Parcel in) {
return new StreamEventResponse(in);
}
public StreamEventResponse(int requestId, int sequence, @ResponseResult int responseResult,
- String name, String text, String data, String status) {
+ int eventId, long npt, @NonNull byte[] data) {
super(responseType, requestId, sequence, responseResult);
- mName = name;
- mText = text;
+ mEventId = eventId;
+ mNpt = npt;
mData = data;
- mStatus = status;
}
- protected StreamEventResponse(Parcel source) {
+ private StreamEventResponse(@NonNull Parcel source) {
super(responseType, source);
- mName = source.readString();
- mText = source.readString();
- mData = source.readString();
- mStatus = source.readString();
+ mEventId = source.readInt();
+ mNpt = source.readLong();
+ int dataLength = source.readInt();
+ mData = new byte[dataLength];
+ source.readByteArray(mData);
}
- public String getName() {
- return mName;
+ /** Returns the event ID */
+ public int getEventId() {
+ return mEventId;
}
- public String getText() {
- return mText;
+ /** Returns the NPT(Normal Play Time) value when the event occurred or will occur */
+ public long getNpt() {
+ return mNpt;
}
- public String getData() {
+ /** Returns the application specific data */
+ @NonNull
+ public byte[] getData() {
return mData;
}
- public String getStatus() {
- return mStatus;
- }
-
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
- dest.writeString(mName);
- dest.writeString(mText);
- dest.writeString(mData);
- dest.writeString(mStatus);
+ dest.writeInt(mEventId);
+ dest.writeLong(mNpt);
+ dest.writeInt(mData.length);
+ dest.writeByteArray(mData);
}
}
diff --git a/media/java/android/media/tv/interactive/AppLinkInfo.aidl b/media/java/android/media/tv/interactive/AppLinkInfo.aidl
new file mode 100644
index 0000000..7c52d01
--- /dev/null
+++ b/media/java/android/media/tv/interactive/AppLinkInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 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.interactive;
+
+parcelable AppLinkInfo;
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/AppLinkInfo.java b/media/java/android/media/tv/interactive/AppLinkInfo.java
new file mode 100644
index 0000000..5cce443
--- /dev/null
+++ b/media/java/android/media/tv/interactive/AppLinkInfo.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2021 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.interactive;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * App link information used by TV interactive app to launch Android apps.
+ * @hide
+ */
+public final class AppLinkInfo implements Parcelable {
+ private @NonNull String mPackageName;
+ private @NonNull String mClassName;
+ private @Nullable String mUriScheme;
+ private @Nullable String mUriHost;
+ private @Nullable String mUriPrefix;
+
+
+ /**
+ * Creates a new AppLinkInfo.
+ */
+ private AppLinkInfo(
+ @NonNull String packageName,
+ @NonNull String className,
+ @Nullable String uriScheme,
+ @Nullable String uriHost,
+ @Nullable String uriPrefix) {
+ this.mPackageName = packageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ this.mClassName = className;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mClassName);
+ this.mUriScheme = uriScheme;
+ this.mUriHost = uriHost;
+ this.mUriPrefix = uriPrefix;
+ }
+
+ /**
+ * Gets package name of the App link.
+ */
+ @NonNull
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * Gets package class of the App link.
+ */
+ @NonNull
+ public String getClassName() {
+ return mClassName;
+ }
+
+ /**
+ * Gets URI scheme of the App link.
+ */
+ @Nullable
+ public String getUriScheme() {
+ return mUriScheme;
+ }
+
+ /**
+ * Gets URI host of the App link.
+ */
+ @Nullable
+ public String getUriHost() {
+ return mUriHost;
+ }
+
+ /**
+ * Gets URI prefix of the App link.
+ */
+ @Nullable
+ public String getUriPrefix() {
+ return mUriPrefix;
+ }
+
+ @Override
+ public String toString() {
+ return "AppLinkInfo { "
+ + "packageName = " + mPackageName + ", "
+ + "className = " + mClassName + ", "
+ + "uriScheme = " + mUriScheme + ", "
+ + "uriHost = " + mUriHost + ", "
+ + "uriPrefix = " + mUriPrefix
+ + " }";
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mPackageName);
+ dest.writeString(mClassName);
+ dest.writeString(mUriScheme);
+ dest.writeString(mUriHost);
+ dest.writeString(mUriPrefix);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /* package-private */ AppLinkInfo(@NonNull Parcel in) {
+ String packageName = in.readString();
+ String className = in.readString();
+ String uriScheme = in.readString();
+ String uriHost = in.readString();
+ String uriPrefix = in.readString();
+
+ this.mPackageName = packageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ this.mClassName = className;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mClassName);
+ this.mUriScheme = uriScheme;
+ this.mUriHost = uriHost;
+ this.mUriPrefix = uriPrefix;
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<AppLinkInfo> CREATOR =
+ new Parcelable.Creator<AppLinkInfo>() {
+ @Override
+ public AppLinkInfo[] newArray(int size) {
+ return new AppLinkInfo[size];
+ }
+
+ @Override
+ public AppLinkInfo createFromParcel(@NonNull Parcel in) {
+ return new AppLinkInfo(in);
+ }
+ };
+
+ /**
+ * A builder for {@link AppLinkInfo}
+ */
+ public static final class Builder {
+ private @NonNull String mPackageName;
+ private @NonNull String mClassName;
+ private @Nullable String mUriScheme;
+ private @Nullable String mUriHost;
+ private @Nullable String mUriPrefix;
+
+ /**
+ * Creates a new Builder.
+ */
+ public Builder(
+ @NonNull String packageName,
+ @NonNull String className) {
+ mPackageName = packageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ mClassName = className;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mClassName);
+ }
+
+ /**
+ * Sets package name of the App link.
+ */
+ @NonNull
+ public Builder setPackageName(@NonNull String value) {
+ mPackageName = value;
+ return this;
+ }
+
+ /**
+ * Sets app name of the App link.
+ */
+ @NonNull
+ public Builder setClassName(@NonNull String value) {
+ mClassName = value;
+ return this;
+ }
+
+ /**
+ * Sets URI scheme of the App link.
+ */
+ @NonNull
+ public Builder setUriScheme(@Nullable String value) {
+ mUriScheme = value;
+ return this;
+ }
+
+ /**
+ * Sets URI host of the App link.
+ */
+ @NonNull
+ public Builder setUriHost(@Nullable String value) {
+ mUriHost = value;
+ return this;
+ }
+
+ /**
+ * Sets URI prefix of the App link.
+ */
+ @NonNull
+ public Builder setUriPrefix(@Nullable String value) {
+ mUriPrefix = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ @NonNull
+ public AppLinkInfo build() {
+ AppLinkInfo o = new AppLinkInfo(
+ mPackageName,
+ mClassName,
+ mUriScheme,
+ mUriHost,
+ mUriPrefix);
+ return o;
+ }
+ }
+}
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
index a8ef095..aaabe34 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
@@ -20,6 +20,7 @@
import android.media.tv.AdResponse;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvTrackInfo;
+import android.media.tv.interactive.AppLinkInfo;
import android.media.tv.interactive.ITvInteractiveAppClient;
import android.media.tv.interactive.ITvInteractiveAppManagerCallback;
import android.media.tv.interactive.TvInteractiveAppInfo;
@@ -34,8 +35,8 @@
interface ITvInteractiveAppManager {
List<TvInteractiveAppInfo> getTvInteractiveAppServiceList(int userId);
void prepare(String tiasId, int type, int userId);
- void registerAppLinkInfo(String tiasId, in Bundle info, int userId);
- void unregisterAppLinkInfo(String tiasId, in Bundle info, int userId);
+ void registerAppLinkInfo(String tiasId, in AppLinkInfo info, int userId);
+ void unregisterAppLinkInfo(String tiasId, in AppLinkInfo info, int userId);
void sendAppLinkCommand(String tiasId, in Bundle command, int userId);
void startInteractiveApp(in IBinder sessionToken, int userId);
void stopInteractiveApp(in IBinder sessionToken, int userId);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl
index 68fae2d..b6d518f 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl
@@ -16,6 +16,7 @@
package android.media.tv.interactive;
+import android.media.tv.interactive.AppLinkInfo;
import android.media.tv.interactive.ITvInteractiveAppServiceCallback;
import android.media.tv.interactive.ITvInteractiveAppSessionCallback;
import android.os.Bundle;
@@ -32,7 +33,7 @@
void createSession(in InputChannel channel, in ITvInteractiveAppSessionCallback callback,
in String iAppServiceId, int type);
void prepare(int type);
- void registerAppLinkInfo(in Bundle info);
- void unregisterAppLinkInfo(in Bundle info);
+ void registerAppLinkInfo(in AppLinkInfo info);
+ void unregisterAppLinkInfo(in AppLinkInfo info);
void sendAppLinkCommand(in Bundle command);
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
index 15a5f823..39be501 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
@@ -757,7 +757,8 @@
* Registers app link info.
* @hide
*/
- public void registerAppLinkInfo(@NonNull String tvIAppServiceId, @NonNull Bundle appLinkInfo) {
+ public void registerAppLinkInfo(
+ @NonNull String tvIAppServiceId, @NonNull AppLinkInfo appLinkInfo) {
try {
mService.registerAppLinkInfo(tvIAppServiceId, appLinkInfo, mUserId);
} catch (RemoteException e) {
@@ -770,7 +771,7 @@
* @hide
*/
public void unregisterAppLinkInfo(
- @NonNull String tvIAppServiceId, @NonNull Bundle appLinkInfo) {
+ @NonNull String tvIAppServiceId, @NonNull AppLinkInfo appLinkInfo) {
try {
mService.unregisterAppLinkInfo(tvIAppServiceId, appLinkInfo, mUserId);
} catch (RemoteException e) {
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index 094aabd..d599d0a 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -131,6 +131,13 @@
/** @hide */
public static final String COMMAND_PARAMETER_KEY_TRACK_SELECT_MODE =
"command_track_select_mode";
+ /**
+ * Command to quiet channel change. No channel banner or channel info is shown.
+ * <p>Refer to HbbTV Spec 2.0.4 chapter A.2.4.3.
+ * @hide
+ */
+ public static final String COMMAND_PARAMETER_KEY_CHANGE_CHANNEL_QUIETLY =
+ "command_change_channel_quietly";
private final Handler mServiceHandler = new ServiceHandler();
private final RemoteCallbackList<ITvInteractiveAppServiceCallback> mCallbacks =
@@ -175,12 +182,12 @@
}
@Override
- public void registerAppLinkInfo(Bundle appLinkInfo) {
+ public void registerAppLinkInfo(AppLinkInfo appLinkInfo) {
onRegisterAppLinkInfo(appLinkInfo);
}
@Override
- public void unregisterAppLinkInfo(Bundle appLinkInfo) {
+ public void unregisterAppLinkInfo(AppLinkInfo appLinkInfo) {
onUnregisterAppLinkInfo(appLinkInfo);
}
@@ -203,7 +210,7 @@
* Registers App link info.
* @hide
*/
- public void onRegisterAppLinkInfo(Bundle appLinkInfo) {
+ public void onRegisterAppLinkInfo(AppLinkInfo appLinkInfo) {
// TODO: make it abstract when unhide
}
@@ -211,7 +218,7 @@
* Unregisters App link info.
* @hide
*/
- public void onUnregisterAppLinkInfo(Bundle appLinkInfo) {
+ public void onUnregisterAppLinkInfo(AppLinkInfo appLinkInfo) {
// TODO: make it abstract when unhide
}
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index 9590d5d..12e2199 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
+import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.media.tv.TvInputManager;
@@ -237,6 +238,7 @@
// The surface view's content should be treated as secure all the time.
mSurfaceView.setSecure(true);
mSurfaceView.getHolder().addCallback(mSurfaceHolderCallback);
+ mSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
addView(mSurfaceView);
}
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettings.java b/media/java/android/media/tv/tuner/filter/SectionSettings.java
index f123675..83ed8e8 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettings.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettings.java
@@ -82,7 +82,7 @@
* The section filter uses this for CRC (Cyclic redundancy check) checking when
* {@link #isCrcEnabled()} is {@code true}.
*/
- public int getBitWidthOfLengthField() {
+ public int getLengthFieldBitWidth() {
return mBitWidthOfLengthField;
}
diff --git a/packages/ConnectivityT/framework-t/Android.bp b/packages/ConnectivityT/framework-t/Android.bp
index d3d8bba..223bdcdd 100644
--- a/packages/ConnectivityT/framework-t/Android.bp
+++ b/packages/ConnectivityT/framework-t/Android.bp
@@ -129,6 +129,11 @@
"src/android/net/EthernetNetworkSpecifier.java",
"src/android/net/IEthernetManager.aidl",
"src/android/net/IEthernetServiceListener.aidl",
+ "src/android/net/IInternalNetworkManagementListener.aidl",
+ "src/android/net/InternalNetworkUpdateRequest.java",
+ "src/android/net/InternalNetworkUpdateRequest.aidl",
+ "src/android/net/InternalNetworkManagementException.java",
+ "src/android/net/InternalNetworkManagementException.aidl",
"src/android/net/ITetheredInterfaceCallback.aidl",
],
path: "src",
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
index 683678a..8813f98 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
@@ -157,6 +157,11 @@
setAugmentWithSubscriptionPlan(true);
}
+ /** @hide */
+ public INetworkStatsService getBinder() {
+ return mService;
+ }
+
/**
* Set poll on open flag to indicate the poll is needed before service gets statistics
* result. This is default enabled. However, for any non-privileged caller, the poll might
diff --git a/core/java/android/net/IInternalNetworkManagementListener.aidl b/packages/ConnectivityT/framework-t/src/android/net/IInternalNetworkManagementListener.aidl
similarity index 100%
rename from core/java/android/net/IInternalNetworkManagementListener.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/IInternalNetworkManagementListener.aidl
diff --git a/core/java/android/net/InternalNetworkManagementException.aidl b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.aidl
similarity index 100%
rename from core/java/android/net/InternalNetworkManagementException.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.aidl
diff --git a/core/java/android/net/InternalNetworkManagementException.java b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java
similarity index 100%
rename from core/java/android/net/InternalNetworkManagementException.java
rename to packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java
diff --git a/core/java/android/net/InternalNetworkUpdateRequest.aidl b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.aidl
similarity index 100%
rename from core/java/android/net/InternalNetworkUpdateRequest.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.aidl
diff --git a/core/java/android/net/InternalNetworkUpdateRequest.java b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java
similarity index 100%
rename from core/java/android/net/InternalNetworkUpdateRequest.java
rename to packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
index 9b9d38a..d3d5a08 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
@@ -16,6 +16,8 @@
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.NetworkTemplate.NETWORK_TYPE_ALL;
@@ -23,6 +25,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.service.NetworkIdentityProto;
@@ -30,6 +33,7 @@
import android.telephony.TelephonyManager;
import android.util.proto.ProtoOutputStream;
+import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.NetworkCapabilitiesUtils;
import com.android.net.module.util.NetworkIdentityUtils;
@@ -44,8 +48,8 @@
*
* @hide
*/
-// @SystemApi(client = MODULE_LIBRARIES)
-public class NetworkIdentity implements Comparable<NetworkIdentity> {
+@SystemApi(client = MODULE_LIBRARIES)
+public class NetworkIdentity {
private static final String TAG = "NetworkIdentity";
/** @hide */
@@ -55,7 +59,7 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "OEM_MANAGED_" }, value = {
+ @IntDef(prefix = { "OEM_MANAGED_" }, flag = true, value = {
NetworkTemplate.OEM_MANAGED_NO,
NetworkTemplate.OEM_MANAGED_PAID,
NetworkTemplate.OEM_MANAGED_PRIVATE
@@ -71,12 +75,14 @@
* Network has {@link NetworkCapabilities#NET_CAPABILITY_OEM_PAID}.
* @hide
*/
- public static final int OEM_PAID = 0x1;
+ public static final int OEM_PAID = 1 << 0;
/**
* Network has {@link NetworkCapabilities#NET_CAPABILITY_OEM_PRIVATE}.
* @hide
*/
- public static final int OEM_PRIVATE = 0x2;
+ public static final int OEM_PRIVATE = 1 << 1;
+
+ private static final long SUPPORTED_OEM_MANAGED_TYPES = OEM_PAID | OEM_PRIVATE;
final int mType;
final int mRatType;
@@ -206,7 +212,7 @@
return mSubscriberId;
}
- /** Get the Wifi Network Key of this instance. See {@link WifiInfo#getCurrentNetworkKey()}. */
+ /** Get the Wifi Network Key of this instance. See {@link WifiInfo#getNetworkKey()}. */
@Nullable
public String getWifiNetworkKey() {
return mWifiNetworkKey;
@@ -218,7 +224,7 @@
return mRoaming;
}
- /** Return the roaming status of this instance. */
+ /** Return whether this network is roaming. */
public boolean isRoaming() {
return mRoaming;
}
@@ -229,7 +235,7 @@
return mMetered;
}
- /** Return the meteredness of this instance. */
+ /** Return whether this network is metered. */
public boolean isMetered() {
return mMetered;
}
@@ -240,7 +246,7 @@
return mDefaultNetwork;
}
- /** Return the default network status of this instance. */
+ /** Return whether this network is the default network. */
public boolean isDefaultNetwork() {
return mDefaultNetwork;
}
@@ -262,7 +268,7 @@
* {@link TelephonyManager#NETWORK_TYPE_UNKNOWN} if not applicable.
* See {@code TelephonyManager.NETWORK_TYPE_*}.
* @hide
- * @deprecated See {@link NetworkIdentity#Builder}.
+ * @deprecated See {@link NetworkIdentity.Builder}.
*/
// TODO: Remove this after all callers are migrated to use new Api.
@Deprecated
@@ -270,8 +276,12 @@
public static NetworkIdentity buildNetworkIdentity(Context context,
@NonNull NetworkStateSnapshot snapshot,
boolean defaultNetwork, @Annotation.NetworkType int ratType) {
- return new NetworkIdentity.Builder().setNetworkStateSnapshot(snapshot)
- .setDefaultNetwork(defaultNetwork).setRatType(ratType).build();
+ final NetworkIdentity.Builder builder = new NetworkIdentity.Builder()
+ .setNetworkStateSnapshot(snapshot).setDefaultNetwork(defaultNetwork);
+ if (snapshot.getLegacyType() == TYPE_MOBILE && ratType != NETWORK_TYPE_ALL) {
+ builder.setRatType(ratType);
+ }
+ return builder.build();
}
/**
@@ -291,30 +301,30 @@
return oemManaged;
}
- @Override
- public int compareTo(@NonNull NetworkIdentity another) {
- Objects.requireNonNull(another);
- int res = Integer.compare(mType, another.mType);
+ /** @hide */
+ public static int compare(@NonNull NetworkIdentity left, @NonNull NetworkIdentity right) {
+ Objects.requireNonNull(right);
+ int res = Integer.compare(left.mType, right.mType);
if (res == 0) {
- res = Integer.compare(mRatType, another.mRatType);
+ res = Integer.compare(left.mRatType, right.mRatType);
}
- if (res == 0 && mSubscriberId != null && another.mSubscriberId != null) {
- res = mSubscriberId.compareTo(another.mSubscriberId);
+ if (res == 0 && left.mSubscriberId != null && right.mSubscriberId != null) {
+ res = left.mSubscriberId.compareTo(right.mSubscriberId);
}
- if (res == 0 && mWifiNetworkKey != null && another.mWifiNetworkKey != null) {
- res = mWifiNetworkKey.compareTo(another.mWifiNetworkKey);
+ if (res == 0 && left.mWifiNetworkKey != null && right.mWifiNetworkKey != null) {
+ res = left.mWifiNetworkKey.compareTo(right.mWifiNetworkKey);
}
if (res == 0) {
- res = Boolean.compare(mRoaming, another.mRoaming);
+ res = Boolean.compare(left.mRoaming, right.mRoaming);
}
if (res == 0) {
- res = Boolean.compare(mMetered, another.mMetered);
+ res = Boolean.compare(left.mMetered, right.mMetered);
}
if (res == 0) {
- res = Boolean.compare(mDefaultNetwork, another.mDefaultNetwork);
+ res = Boolean.compare(left.mDefaultNetwork, right.mDefaultNetwork);
}
if (res == 0) {
- res = Integer.compare(mOemManaged, another.mOemManaged);
+ res = Integer.compare(left.mOemManaged, right.mOemManaged);
}
return res;
}
@@ -323,6 +333,11 @@
* Builder class for {@link NetworkIdentity}.
*/
public static final class Builder {
+ // Need to be synchronized with ConnectivityManager.
+ // TODO: Use {@link ConnectivityManager#MAX_NETWORK_TYPE} when this file is in the module.
+ private static final int MAX_NETWORK_TYPE = 18; // TYPE_TEST
+ private static final int MIN_NETWORK_TYPE = TYPE_MOBILE;
+
private int mType;
private int mRatType;
private String mSubscriberId;
@@ -349,7 +364,14 @@
/**
* Add an {@link NetworkStateSnapshot} into the {@link NetworkIdentity} instance.
- * This is to read roaming, metered, wifikey... from the snapshot for convenience.
+ * This is a useful shorthand that will read from the snapshot and set the
+ * following fields, if they are set in the snapshot :
+ * - type
+ * - subscriberId
+ * - roaming
+ * - metered
+ * - oemManaged
+ * - wifiNetworkKey
*
* @param snapshot The target {@link NetworkStateSnapshot} object.
* @return The builder object.
@@ -374,9 +396,7 @@
.getTransportInfo();
if (transportInfo instanceof WifiInfo) {
final WifiInfo info = (WifiInfo) transportInfo;
- if (info != null) {
- setWifiNetworkKey(info.getCurrentNetworkKey());
- }
+ setWifiNetworkKey(info.getNetworkKey());
}
}
return this;
@@ -391,6 +411,12 @@
*/
@NonNull
public Builder setType(int type) {
+ // Include TYPE_NONE for compatibility, type field might not be filled by some
+ // networks such as test networks.
+ if ((type < MIN_NETWORK_TYPE || MAX_NETWORK_TYPE < type)
+ && type != ConnectivityManager.TYPE_NONE) {
+ throw new IllegalArgumentException("Invalid network type: " + type);
+ }
mType = type;
return this;
}
@@ -398,6 +424,8 @@
/**
* Set the Radio Access Technology(RAT) type of the network.
*
+ * No RAT type is specified by default. Call clearRatType to reset.
+ *
* @param ratType the Radio Access Technology(RAT) type if applicable. See
* {@code TelephonyManager.NETWORK_TYPE_*}.
*
@@ -405,6 +433,10 @@
*/
@NonNull
public Builder setRatType(@Annotation.NetworkType int ratType) {
+ if (!CollectionUtils.contains(TelephonyManager.getAllNetworkTypes(), ratType)
+ && ratType != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
+ throw new IllegalArgumentException("Invalid ratType " + ratType);
+ }
mRatType = ratType;
return this;
}
@@ -436,7 +468,7 @@
* Set the Wifi Network Key.
*
* @param wifiNetworkKey Wifi Network Key of the network,
- * see {@link WifiInfo#getCurrentNetworkKey()}.
+ * see {@link WifiInfo#getNetworkKey()}.
* Or null if not applicable.
* @return this builder.
*/
@@ -447,7 +479,9 @@
}
/**
- * Set the roaming.
+ * Set whether this network is roaming.
+ *
+ * This field is false by default. Call with false to reset.
*
* @param roaming the roaming status of the network.
* @return this builder.
@@ -459,7 +493,9 @@
}
/**
- * Set the meteredness.
+ * Set whether this network is metered.
+ *
+ * This field is false by default. Call with false to reset.
*
* @param metered the meteredness of the network.
* @return this builder.
@@ -471,7 +507,9 @@
}
/**
- * Set the default network status.
+ * Set whether this network is the default network.
+ *
+ * This field is false by default. Call with false to reset.
*
* @param defaultNetwork the default network status of the network.
* @return this builder.
@@ -491,10 +529,27 @@
*/
@NonNull
public Builder setOemManaged(@OemManaged int oemManaged) {
+ // Assert input does not contain illegal oemManage bits.
+ if ((~SUPPORTED_OEM_MANAGED_TYPES & oemManaged) != 0) {
+ throw new IllegalArgumentException("Invalid value for OemManaged : " + oemManaged);
+ }
mOemManaged = oemManaged;
return this;
}
+ private void ensureValidParameters() {
+ // Assert non-mobile network cannot have a ratType.
+ if (mType != TYPE_MOBILE && mRatType != NetworkTemplate.NETWORK_TYPE_ALL) {
+ throw new IllegalArgumentException(
+ "Invalid ratType " + mRatType + " for type " + mType);
+ }
+
+ // Assert non-wifi network cannot have a wifi network key.
+ if (mType != TYPE_WIFI && mWifiNetworkKey != null) {
+ throw new IllegalArgumentException("Invalid wifi network key for type " + mType);
+ }
+ }
+
/**
* Builds the instance of the {@link NetworkIdentity}.
*
@@ -502,6 +557,7 @@
*/
@NonNull
public NetworkIdentity build() {
+ ensureValidParameters();
return new NetworkIdentity(mType, mRatType, mSubscriberId, mWifiNetworkKey,
mRoaming, mMetered, mDefaultNetwork, mOemManaged);
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
index 041f070..dfa347f 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
@@ -27,6 +27,7 @@
import java.io.IOException;
import java.util.HashSet;
import java.util.Objects;
+import java.util.Set;
/**
* Identity of a {@code iface}, defined by the set of {@link NetworkIdentity}
@@ -34,9 +35,7 @@
*
* @hide
*/
-// @SystemApi(client = MODULE_LIBRARIES)
-public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements
- Comparable<NetworkIdentitySet> {
+public class NetworkIdentitySet extends HashSet<NetworkIdentity> {
private static final int VERSION_INIT = 1;
private static final int VERSION_ADD_ROAMING = 2;
private static final int VERSION_ADD_NETWORK_ID = 3;
@@ -52,6 +51,11 @@
}
/** @hide */
+ public NetworkIdentitySet(@NonNull Set<NetworkIdentity> ident) {
+ super(ident);
+ }
+
+ /** @hide */
public NetworkIdentitySet(DataInput in) throws IOException {
final int version = in.readInt();
final int size = in.readInt();
@@ -189,15 +193,15 @@
}
}
- @Override
- public int compareTo(@NonNull NetworkIdentitySet another) {
- Objects.requireNonNull(another);
- if (isEmpty()) return -1;
- if (another.isEmpty()) return 1;
+ public static int compare(@NonNull NetworkIdentitySet left, @NonNull NetworkIdentitySet right) {
+ Objects.requireNonNull(left);
+ Objects.requireNonNull(right);
+ if (left.isEmpty()) return -1;
+ if (right.isEmpty()) return 1;
- final NetworkIdentity ident = iterator().next();
- final NetworkIdentity anotherIdent = another.iterator().next();
- return ident.compareTo(anotherIdent);
+ final NetworkIdentity leftIdent = left.iterator().next();
+ final NetworkIdentity rightIdent = right.iterator().next();
+ return NetworkIdentity.compare(leftIdent, rightIdent);
}
/**
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
index f169fed..58ca21f 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
@@ -72,6 +72,7 @@
import java.util.HashMap;
import java.util.Iterator;
import java.util.Objects;
+import java.util.Set;
/**
* Collection of {@link NetworkStatsHistory}, stored based on combined key of
@@ -702,7 +703,7 @@
private ArrayList<Key> getSortedKeys() {
final ArrayList<Key> keys = new ArrayList<>();
keys.addAll(mStats.keySet());
- Collections.sort(keys);
+ Collections.sort(keys, (left, right) -> Key.compare(left, right));
return keys;
}
@@ -812,7 +813,7 @@
* the identifier that associate with the {@link NetworkStatsHistory} object to identify
* a certain record in the {@link NetworkStatsCollection} object.
*/
- public static class Key implements Comparable<Key> {
+ public static class Key {
/** @hide */
public final NetworkIdentitySet ident;
/** @hide */
@@ -832,6 +833,11 @@
* @param set Set of the record, see {@code NetworkStats#SET_*}.
* @param tag Tag of the record, see {@link TrafficStats#setThreadStatsTag(int)}.
*/
+ public Key(@NonNull Set<NetworkIdentity> ident, int uid, int set, int tag) {
+ this(new NetworkIdentitySet(Objects.requireNonNull(ident)), uid, set, tag);
+ }
+
+ /** @hide */
public Key(@NonNull NetworkIdentitySet ident, int uid, int set, int tag) {
this.ident = Objects.requireNonNull(ident);
this.uid = uid;
@@ -855,21 +861,22 @@
return false;
}
- @Override
- public int compareTo(@NonNull Key another) {
- Objects.requireNonNull(another);
+ /** @hide */
+ public static int compare(@NonNull Key left, @NonNull Key right) {
+ Objects.requireNonNull(left);
+ Objects.requireNonNull(right);
int res = 0;
- if (ident != null && another.ident != null) {
- res = ident.compareTo(another.ident);
+ if (left.ident != null && right.ident != null) {
+ res = NetworkIdentitySet.compare(left.ident, right.ident);
}
if (res == 0) {
- res = Integer.compare(uid, another.uid);
+ res = Integer.compare(left.uid, right.uid);
}
if (res == 0) {
- res = Integer.compare(set, another.set);
+ res = Integer.compare(left.set, right.set);
}
if (res == 0) {
- res = Integer.compare(tag, another.tag);
+ res = Integer.compare(left.tag, right.tag);
}
return res;
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
index 90054c6..78c1370 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
@@ -16,6 +16,7 @@
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.TAG_NONE;
@@ -31,6 +32,7 @@
import static com.android.net.module.util.NetworkStatsUtils.multiplySafeByRational;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
@@ -51,7 +53,9 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ProtocolException;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Random;
/**
@@ -65,7 +69,7 @@
*
* @hide
*/
-// @SystemApi(client = MODULE_LIBRARIES)
+@SystemApi(client = MODULE_LIBRARIES)
public final class NetworkStatsHistory implements Parcelable {
private static final int VERSION_INIT = 1;
private static final int VERSION_ADD_PACKETS = 2;
@@ -97,23 +101,157 @@
private long[] operations;
private long totalBytes;
- public static class Entry {
+ /** @hide */
+ public NetworkStatsHistory(long bucketDuration, long[] bucketStart, long[] activeTime,
+ long[] rxBytes, long[] rxPackets, long[] txBytes, long[] txPackets,
+ long[] operations, int bucketCount, long totalBytes) {
+ this.bucketDuration = bucketDuration;
+ this.bucketStart = bucketStart;
+ this.activeTime = activeTime;
+ this.rxBytes = rxBytes;
+ this.rxPackets = rxPackets;
+ this.txBytes = txBytes;
+ this.txPackets = txPackets;
+ this.operations = operations;
+ this.bucketCount = bucketCount;
+ this.totalBytes = totalBytes;
+ }
+
+ /**
+ * An instance to represent a single record in a {@link NetworkStatsHistory} object.
+ */
+ public static final class Entry {
+ /** @hide */
public static final long UNKNOWN = -1;
+ /** @hide */
+ // TODO: Migrate all callers to get duration from the history object and remove this field.
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public long bucketDuration;
+ /** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public long bucketStart;
+ /** @hide */
public long activeTime;
+ /** @hide */
@UnsupportedAppUsage
public long rxBytes;
+ /** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public long rxPackets;
+ /** @hide */
@UnsupportedAppUsage
public long txBytes;
+ /** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public long txPackets;
+ /** @hide */
public long operations;
+ /** @hide */
+ Entry() {}
+
+ /**
+ * Construct a {@link Entry} instance to represent a single record in a
+ * {@link NetworkStatsHistory} object.
+ *
+ * @param bucketStart Start of period for this {@link Entry}, in milliseconds since the
+ * Unix epoch, see {@link java.lang.System#currentTimeMillis}.
+ * @param activeTime Active time for this {@link Entry}, in milliseconds.
+ * @param rxBytes Number of bytes received for this {@link Entry}. Statistics should
+ * represent the contents of IP packets, including IP headers.
+ * @param rxPackets Number of packets received for this {@link Entry}. Statistics should
+ * represent the contents of IP packets, including IP headers.
+ * @param txBytes Number of bytes transmitted for this {@link Entry}. Statistics should
+ * represent the contents of IP packets, including IP headers.
+ * @param txPackets Number of bytes transmitted for this {@link Entry}. Statistics should
+ * represent the contents of IP packets, including IP headers.
+ * @param operations count of network operations performed for this {@link Entry}. This can
+ * be used to derive bytes-per-operation.
+ */
+ public Entry(long bucketStart, long activeTime, long rxBytes,
+ long rxPackets, long txBytes, long txPackets, long operations) {
+ this.bucketStart = bucketStart;
+ this.activeTime = activeTime;
+ this.rxBytes = rxBytes;
+ this.rxPackets = rxPackets;
+ this.txBytes = txBytes;
+ this.txPackets = txPackets;
+ this.operations = operations;
+ }
+
+ /**
+ * Get start timestamp of the bucket's time interval, in milliseconds since the Unix epoch.
+ */
+ public long getBucketStart() {
+ return bucketStart;
+ }
+
+ /**
+ * Get active time of the bucket's time interval, in milliseconds.
+ */
+ public long getActiveTime() {
+ return activeTime;
+ }
+
+ /** Get number of bytes received for this {@link Entry}. */
+ public long getRxBytes() {
+ return rxBytes;
+ }
+
+ /** Get number of packets received for this {@link Entry}. */
+ public long getRxPackets() {
+ return rxPackets;
+ }
+
+ /** Get number of bytes transmitted for this {@link Entry}. */
+ public long getTxBytes() {
+ return txBytes;
+ }
+
+ /** Get number of packets transmitted for this {@link Entry}. */
+ public long getTxPackets() {
+ return txPackets;
+ }
+
+ /** Get count of network operations performed for this {@link Entry}. */
+ public long getOperations() {
+ return operations;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o.getClass() != getClass()) return false;
+ Entry entry = (Entry) o;
+ return bucketStart == entry.bucketStart
+ && activeTime == entry.activeTime && rxBytes == entry.rxBytes
+ && rxPackets == entry.rxPackets && txBytes == entry.txBytes
+ && txPackets == entry.txPackets && operations == entry.operations;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) (bucketStart * 2
+ + activeTime * 3
+ + rxBytes * 5
+ + rxPackets * 7
+ + txBytes * 11
+ + txPackets * 13
+ + operations * 17);
+ }
+
+ @Override
+ public String toString() {
+ return "Entry{"
+ + "bucketStart=" + bucketStart
+ + ", activeTime=" + activeTime
+ + ", rxBytes=" + rxBytes
+ + ", rxPackets=" + rxPackets
+ + ", txBytes=" + txBytes
+ + ", txPackets=" + txPackets
+ + ", operations=" + operations
+ + "}";
+ }
}
/** @hide */
@@ -324,6 +462,22 @@
return entry;
}
+ /**
+ * Get List of {@link Entry} of the {@link NetworkStatsHistory} instance.
+ *
+ * @return
+ */
+ @NonNull
+ public List<Entry> getEntries() {
+ // TODO: Return a wrapper that uses this list instead, to prevent the returned result
+ // from being changed.
+ final ArrayList<Entry> ret = new ArrayList<>(size());
+ for (int i = 0; i < size(); i++) {
+ ret.add(getValues(i, null /* recycle */));
+ }
+ return ret;
+ }
+
/** @hide */
public void setValues(int i, Entry entry) {
// Unwind old values
@@ -928,4 +1082,80 @@
}
}
+ /**
+ * Builder class for {@link NetworkStatsHistory}.
+ */
+ public static final class Builder {
+ private final long mBucketDuration;
+ private final List<Long> mBucketStart;
+ private final List<Long> mActiveTime;
+ private final List<Long> mRxBytes;
+ private final List<Long> mRxPackets;
+ private final List<Long> mTxBytes;
+ private final List<Long> mTxPackets;
+ private final List<Long> mOperations;
+
+ /**
+ * Creates a new Builder with given bucket duration and initial capacity to construct
+ * {@link NetworkStatsHistory} objects.
+ *
+ * @param bucketDuration Duration of the buckets of the object, in milliseconds.
+ * @param initialCapacity Estimated number of records.
+ */
+ public Builder(long bucketDuration, int initialCapacity) {
+ mBucketDuration = bucketDuration;
+ mBucketStart = new ArrayList<>(initialCapacity);
+ mActiveTime = new ArrayList<>(initialCapacity);
+ mRxBytes = new ArrayList<>(initialCapacity);
+ mRxPackets = new ArrayList<>(initialCapacity);
+ mTxBytes = new ArrayList<>(initialCapacity);
+ mTxPackets = new ArrayList<>(initialCapacity);
+ mOperations = new ArrayList<>(initialCapacity);
+ }
+
+ /**
+ * Add an {@link Entry} into the {@link NetworkStatsHistory} instance.
+ *
+ * @param entry The target {@link Entry} object.
+ * @return The builder object.
+ */
+ @NonNull
+ public Builder addEntry(@NonNull Entry entry) {
+ mBucketStart.add(entry.bucketStart);
+ mActiveTime.add(entry.activeTime);
+ mRxBytes.add(entry.rxBytes);
+ mRxPackets.add(entry.rxPackets);
+ mTxBytes.add(entry.txBytes);
+ mTxPackets.add(entry.txPackets);
+ mOperations.add(entry.operations);
+ return this;
+ }
+
+ private static long sum(@NonNull List<Long> list) {
+ long sum = 0;
+ for (long entry : list) {
+ sum += entry;
+ }
+ return sum;
+ }
+
+ /**
+ * Builds the instance of the {@link NetworkStatsHistory}.
+ *
+ * @return the built instance of {@link NetworkStatsHistory}.
+ */
+ @NonNull
+ public NetworkStatsHistory build() {
+ return new NetworkStatsHistory(mBucketDuration,
+ CollectionUtils.toLongArray(mBucketStart),
+ CollectionUtils.toLongArray(mActiveTime),
+ CollectionUtils.toLongArray(mRxBytes),
+ CollectionUtils.toLongArray(mRxPackets),
+ CollectionUtils.toLongArray(mTxBytes),
+ CollectionUtils.toLongArray(mTxPackets),
+ CollectionUtils.toLongArray(mOperations),
+ mBucketStart.size(),
+ sum(mRxBytes) + sum(mTxBytes));
+ }
+ }
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
index a7e48d4..cad8075 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
@@ -263,7 +263,7 @@
* Template to match {@link ConnectivityManager#TYPE_WIFI} networks with the
* given key of the wifi network.
*
- * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+ * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getNetworkKey()}
* to know details about the key.
* @hide
*/
@@ -283,7 +283,7 @@
* Call with {@link #WIFI_NETWORK_KEY_ALL} for {@code wifiNetworkKey} to get result regardless
* of key of the wifi network.
*
- * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+ * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getNetworkKey()}
* to know details about the key.
* @param subscriberId the IMSI associated to this wifi network.
*
@@ -593,7 +593,7 @@
/**
* Get the set of Wifi Network Keys of the template.
- * See {@link WifiInfo#getCurrentNetworkKey()}.
+ * See {@link WifiInfo#getNetworkKey()}.
*/
@NonNull
public Set<String> getWifiNetworkKeys() {
@@ -729,7 +729,7 @@
* Returns true when the key matches, or when {@code mMatchWifiNetworkKeys} is
* empty.
*
- * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+ * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getNetworkKey()}
* to know details about the key.
*/
private boolean matchesWifiNetworkKey(@NonNull String wifiNetworkKey) {
@@ -1059,9 +1059,9 @@
* the intention of matching any Wifi Network Key.
*
* @param wifiNetworkKeys the list of Wifi Network Key,
- * see {@link WifiInfo#getCurrentNetworkKey()}.
+ * see {@link WifiInfo#getNetworkKey()}.
* Or an empty list to match all networks.
- * Note that {@code getCurrentNetworkKey()} might get null key
+ * Note that {@code getNetworkKey()} might get null key
* when wifi disconnects. However, the caller should never invoke
* this function with a null Wifi Network Key since such statistics
* never exists.
diff --git a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
index 1af32bf..c803a72 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
@@ -17,7 +17,6 @@
package android.net;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -27,8 +26,8 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.media.MediaPlayer;
+import android.os.Binder;
import android.os.Build;
-import android.os.IBinder;
import android.os.RemoteException;
import com.android.server.NetworkManagementSocketTagger;
@@ -37,8 +36,6 @@
import java.io.FileDescriptor;
import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import java.net.DatagramSocket;
import java.net.Socket;
import java.net.SocketException;
@@ -177,25 +174,12 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
private synchronized static INetworkStatsService getStatsService() {
if (sStatsService == null) {
- sStatsService = getStatsBinder();
+ throw new IllegalStateException("TrafficStats not initialized, uid="
+ + Binder.getCallingUid());
}
return sStatsService;
}
- @Nullable
- private static INetworkStatsService getStatsBinder() {
- try {
- final Method getServiceMethod = Class.forName("android.os.ServiceManager")
- .getDeclaredMethod("getService", new Class[]{String.class});
- final IBinder binder = (IBinder) getServiceMethod.invoke(
- null, Context.NETWORK_STATS_SERVICE);
- return INetworkStatsService.Stub.asInterface(binder);
- } catch (NoSuchMethodException | ClassNotFoundException | IllegalAccessException
- | InvocationTargetException e) {
- throw new NullPointerException("Cannot get INetworkStatsService: " + e);
- }
- }
-
/**
* Snapshot of {@link NetworkStats} when the currently active profiling
* session started, or {@code null} if no session active.
@@ -210,6 +194,26 @@
private static final String LOOPBACK_IFACE = "lo";
/**
+ * Initialization {@link TrafficStats} with the context, to
+ * allow {@link TrafficStats} to fetch the needed binder.
+ *
+ * @param context a long-lived context, such as the application context or system
+ * server context.
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("VisiblySynchronized")
+ public static synchronized void init(@NonNull final Context context) {
+ if (sStatsService != null) {
+ throw new IllegalStateException("TrafficStats is already initialized, uid="
+ + Binder.getCallingUid());
+ }
+ final NetworkStatsManager statsManager =
+ context.getSystemService(NetworkStatsManager.class);
+ sStatsService = statsManager.getBinder();
+ }
+
+ /**
* Set active tag to use when accounting {@link Socket} traffic originating
* from the current thread. Only one active tag per thread is supported.
* <p>
diff --git a/packages/ConnectivityT/service/Android.bp b/packages/ConnectivityT/service/Android.bp
index b261e16..36dd200 100644
--- a/packages/ConnectivityT/service/Android.bp
+++ b/packages/ConnectivityT/service/Android.bp
@@ -66,6 +66,7 @@
filegroup {
name: "services.connectivity-ethernet-sources",
srcs: [
+ "src/com/android/server/net/DelayedDiskWrite.java",
"src/com/android/server/net/IpConfigStore.java",
],
path: "src",
diff --git a/services/core/java/com/android/server/net/DelayedDiskWrite.java b/packages/ConnectivityT/service/src/com/android/server/net/DelayedDiskWrite.java
similarity index 82%
rename from services/core/java/com/android/server/net/DelayedDiskWrite.java
rename to packages/ConnectivityT/service/src/com/android/server/net/DelayedDiskWrite.java
index 8f09eb7..35dc455 100644
--- a/services/core/java/com/android/server/net/DelayedDiskWrite.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/DelayedDiskWrite.java
@@ -26,21 +26,37 @@
import java.io.FileOutputStream;
import java.io.IOException;
+/**
+ * This class provides APIs to do a delayed data write to a given {@link OutputStream}.
+ */
public class DelayedDiskWrite {
+ private static final String TAG = "DelayedDiskWrite";
+
private HandlerThread mDiskWriteHandlerThread;
private Handler mDiskWriteHandler;
/* Tracks multiple writes on the same thread */
private int mWriteSequence = 0;
- private final String TAG = "DelayedDiskWrite";
+ /**
+ * Used to do a delayed data write to a given {@link OutputStream}.
+ */
public interface Writer {
- public void onWriteCalled(DataOutputStream out) throws IOException;
+ /**
+ * write data to a given {@link OutputStream}.
+ */
+ void onWriteCalled(DataOutputStream out) throws IOException;
}
+ /**
+ * Do a delayed data write to a given output stream opened from filePath.
+ */
public void write(final String filePath, final Writer w) {
write(filePath, w, true);
}
+ /**
+ * Do a delayed data write to a given output stream opened from filePath.
+ */
public void write(final String filePath, final Writer w, final boolean open) {
if (TextUtils.isEmpty(filePath)) {
throw new IllegalArgumentException("empty file path");
@@ -77,7 +93,7 @@
if (out != null) {
try {
out.close();
- } catch (Exception e) {}
+ } catch (Exception e) { }
}
// Quit if no more writes sent
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index 9b90f3b..1105de3 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -106,7 +106,6 @@
import android.os.DropBoxManager;
import android.os.Environment;
import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
@@ -450,7 +449,7 @@
handlerThread.start();
mHandler = new NetworkStatsHandler(handlerThread.getLooper());
mNetworkStatsSubscriptionsMonitor = deps.makeSubscriptionsMonitor(mContext,
- new HandlerExecutor(mHandler), this);
+ (command) -> mHandler.post(command) , this);
mContentResolver = mContext.getContentResolver();
mContentObserver = mDeps.makeContentObserver(mHandler, mSettings,
mNetworkStatsSubscriptionsMonitor);
@@ -557,7 +556,7 @@
// watch for tethering changes
final TetheringManager tetheringManager = mContext.getSystemService(TetheringManager.class);
tetheringManager.registerTetheringEventCallback(
- new HandlerExecutor(mHandler), mTetherListener);
+ (command) -> mHandler.post(command), mTetherListener);
// listen for periodic polling events
final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 2c862e685..389892e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -170,18 +170,6 @@
}
@VisibleForTesting
- void registerIntentReceiver() {
- mContext.registerReceiverAsUser(mBroadcastReceiver, mUserHandle, mAdapterIntentFilter,
- null, mReceiverHandler);
- }
-
- @VisibleForTesting
- void registerProfileIntentReceiverForTest() {
- mContext.registerReceiverAsUser(mProfileBroadcastReceiver, mUserHandle,
- mProfileIntentFilter, null, mReceiverHandler);
- }
-
- @VisibleForTesting
void addProfileHandler(String action, Handler handler) {
mHandlerMap.put(action, handler);
mProfileIntentFilter.addAction(action);
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index 3c444f2..7168f3c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -402,7 +402,10 @@
if (dreamInfo == null || dreamInfo.settingsComponentName == null) {
return;
}
- uiContext.startActivity(new Intent().setComponent(dreamInfo.settingsComponentName));
+ final Intent intent = new Intent()
+ .setComponent(dreamInfo.settingsComponentName)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ uiContext.startActivity(intent);
}
public void preview(DreamInfo dreamInfo) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index bee466d..852ac5c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -129,7 +129,6 @@
@Test
public void intentWithExtraState_audioStateChangedShouldDispatchToRegisterCallback() {
mBluetoothEventManager.registerCallback(mBluetoothCallback);
- mBluetoothEventManager.registerIntentReceiver();
mIntent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
mContext.sendBroadcast(mIntent);
@@ -143,7 +142,6 @@
@Test
public void intentWithExtraState_phoneStateChangedShouldDispatchToRegisterCallback() {
mBluetoothEventManager.registerCallback(mBluetoothCallback);
- mBluetoothEventManager.registerIntentReceiver();
mIntent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
mContext.sendBroadcast(mIntent);
@@ -169,7 +167,6 @@
@Test
public void dispatchAclConnectionStateChanged_aclDisconnected_shouldDispatchCallback() {
mBluetoothEventManager.registerCallback(mBluetoothCallback);
- mBluetoothEventManager.registerIntentReceiver();
mIntent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
@@ -182,7 +179,6 @@
@Test
public void dispatchAclConnectionStateChanged_aclConnected_shouldDispatchCallback() {
mBluetoothEventManager.registerCallback(mBluetoothCallback);
- mBluetoothEventManager.registerIntentReceiver();
mIntent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
@@ -196,7 +192,6 @@
public void dispatchAclConnectionStateChanged_aclDisconnected_shouldNotCallbackSubDevice() {
when(mCachedDeviceManager.isSubDevice(mBluetoothDevice)).thenReturn(true);
mBluetoothEventManager.registerCallback(mBluetoothCallback);
- mBluetoothEventManager.registerIntentReceiver();
mIntent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
@@ -210,7 +205,6 @@
public void dispatchAclConnectionStateChanged_aclConnected_shouldNotCallbackSubDevice() {
when(mCachedDeviceManager.isSubDevice(mBluetoothDevice)).thenReturn(true);
mBluetoothEventManager.registerCallback(mBluetoothCallback);
- mBluetoothEventManager.registerIntentReceiver();
mIntent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
@@ -224,7 +218,6 @@
public void dispatchAclConnectionStateChanged_findDeviceReturnNull_shouldNotDispatchCallback() {
when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(null);
mBluetoothEventManager.registerCallback(mBluetoothCallback);
- mBluetoothEventManager.registerIntentReceiver();
mIntent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
@@ -361,7 +354,6 @@
@Test
public void showUnbondMessage_reasonAuthTimeout_showCorrectedErrorCode() {
- mBluetoothEventManager.registerIntentReceiver();
mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
@@ -377,7 +369,6 @@
@Test
public void showUnbondMessage_reasonRemoteDeviceDown_showCorrectedErrorCode() {
- mBluetoothEventManager.registerIntentReceiver();
mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
@@ -394,7 +385,6 @@
@Test
public void showUnbondMessage_reasonAuthRejected_showCorrectedErrorCode() {
- mBluetoothEventManager.registerIntentReceiver();
mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
@@ -410,7 +400,6 @@
@Test
public void showUnbondMessage_reasonAuthFailed_showCorrectedErrorCode() {
- mBluetoothEventManager.registerIntentReceiver();
mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
index 09540d1..4f8fa2f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
@@ -40,7 +40,6 @@
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -85,7 +84,6 @@
when(mCachedBluetoothDevice.getDevice()).thenReturn(mDevice);
mProfileManager = new LocalBluetoothProfileManager(mContext, mLocalBluetoothAdapter,
mDeviceManager, mEventManager);
- mEventManager.registerProfileIntentReceiverForTest();
}
/**
@@ -152,7 +150,6 @@
* profile connection state changed callback
*/
@Test
- @Ignore
public void stateChangedHandler_receiveA2dpConnectionStateChanged_shouldDispatchCallback() {
mShadowBluetoothAdapter.setSupportedProfiles(generateList(
new int[] {BluetoothProfile.A2DP}));
@@ -174,7 +171,6 @@
* profile connection state changed callback
*/
@Test
- @Ignore
public void stateChangedHandler_receiveHeadsetConnectionStateChanged_shouldDispatchCallback() {
mShadowBluetoothAdapter.setSupportedProfiles(generateList(
new int[] {BluetoothProfile.HEADSET}));
@@ -196,7 +192,6 @@
* CachedBluetoothDeviceManager method
*/
@Test
- @Ignore
public void stateChangedHandler_receiveHAPConnectionStateChanged_shouldDispatchDeviceManager() {
mShadowBluetoothAdapter.setSupportedProfiles(generateList(
new int[] {BluetoothProfile.HEARING_AID}));
@@ -219,7 +214,6 @@
* profile connection state changed callback
*/
@Test
- @Ignore
public void stateChangedHandler_receivePanConnectionStateChanged_shouldNotDispatchCallback() {
mShadowBluetoothAdapter.setSupportedProfiles(generateList(
new int[] {BluetoothProfile.PAN}));
@@ -261,7 +255,6 @@
* handler and refresh CachedBluetoothDevice
*/
@Test
- @Ignore
public void stateChangedHandler_receivePanConnectionStateChangedWithProfile_shouldRefresh() {
mShadowBluetoothAdapter.setSupportedProfiles(generateList(
new int[] {BluetoothProfile.PAN}));
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 a31f24a..30267f7 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
@@ -19,14 +19,17 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
+import android.net.wifi.WifiManager;
+
+import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -37,7 +40,7 @@
@RunWith(RobolectricTestRunner.class)
public class AccessPointPreferenceTest {
- private Context mContext = RuntimeEnvironment.application;
+ private Context mContext;
@Mock
private AccessPoint mockAccessPoint;
@@ -54,12 +57,13 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mock(WifiManager.class));
when(mockIconInjector.getIcon(anyInt())).thenReturn(new ColorDrawable());
}
@Test
- @Ignore
public void refresh_openNetwork_updateContentDescription() {
final String ssid = "ssid";
final String summary = "connected";
@@ -90,7 +94,6 @@
}
@Test
- @Ignore
public void refresh_setTitle_shouldUseSsidString() {
final String ssid = "ssid";
final String summary = "connected";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
index 5d7f8ba..e7b3fe9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
@@ -20,6 +20,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -32,6 +33,7 @@
import android.net.WifiKey;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
import android.net.wifi.WifiNetworkScoreCache;
import android.os.Bundle;
import android.os.Parcelable;
@@ -44,7 +46,6 @@
import com.android.settingslib.R;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -75,10 +76,10 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
+ when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mock(WifiManager.class));
}
@Test
- @Ignore
public void testVerboseSummaryString_showsScanResultSpeedLabel() {
WifiTracker.sVerboseLogging = true;
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index f20057d..5f549fd 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -325,6 +325,7 @@
return true;
});
VALIDATORS.put(Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.FAST_PAIR_SCAN_ENABLED, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index e1da744..3ae85e7 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -11,8 +11,10 @@
awickham@google.com
beverlyt@google.com
brockman@google.com
+brzezinski@google.com
brycelee@google.com
ccassidy@google.com
+chrisgollner@google.com
cinek@google.com
cwren@google.com
dupin@google.com
@@ -43,6 +45,8 @@
mrcasey@google.com
mrenouf@google.com
nesciosquid@google.com
+nickchameyev@google.com
+nicomazz@google.com
ogunwale@google.com
peanutbutter@google.com
pinyaoting@google.com
diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions.xml b/packages/SystemUI/res-keyguard/layout/footer_actions.xml
index dfc3e63..ecb3cb3 100644
--- a/packages/SystemUI/res-keyguard/layout/footer_actions.xml
+++ b/packages/SystemUI/res-keyguard/layout/footer_actions.xml
@@ -22,21 +22,6 @@
android:layout_height="48dp"
android:gravity="center_vertical">
- <com.android.systemui.statusbar.AlphaOptimizedImageView
- android:id="@android:id/edit"
- android:layout_width="0dp"
- android:layout_height="@dimen/qs_footer_action_button_size"
- android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
- android:layout_weight="1"
- android:background="@drawable/qs_footer_action_chip_background"
- android:clickable="true"
- android:clipToPadding="false"
- android:contentDescription="@string/accessibility_quick_settings_edit"
- android:focusable="true"
- android:padding="@dimen/qs_footer_icon_padding"
- android:src="@*android:drawable/ic_mode_edit"
- android:tint="?android:attr/textColorPrimary" />
-
<com.android.systemui.statusbar.phone.MultiUserSwitch
android:id="@+id/multi_user_switch"
android:layout_width="0dp"
diff --git a/packages/SystemUI/res/drawable/qs_customizer_background_transition.xml b/packages/SystemUI/res/drawable/qs_customizer_background_transition.xml
index ed8f61a..6fa9eac 100644
--- a/packages/SystemUI/res/drawable/qs_customizer_background_transition.xml
+++ b/packages/SystemUI/res/drawable/qs_customizer_background_transition.xml
@@ -15,7 +15,7 @@
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android">
<shape>
- <solid android:color="@color/qs_detail_transition"/>
+ <solid android:color="@android:color/transparent"/>
<corners android:radius="?android:attr/dialogCornerRadius" />
</shape>
</inset>
diff --git a/packages/SystemUI/res/drawable/qs_detail_background.xml b/packages/SystemUI/res/drawable/qs_detail_background.xml
index e5c7352..c23649d 100644
--- a/packages/SystemUI/res/drawable/qs_detail_background.xml
+++ b/packages/SystemUI/res/drawable/qs_detail_background.xml
@@ -17,7 +17,7 @@
<item>
<inset>
<shape>
- <solid android:color="@color/qs_detail_transition"/>
+ <solid android:color="@android:color/transparent"/>
<corners android:radius="@dimen/qs_footer_action_corner_radius" />
</shape>
</inset>
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index e70084b..5cd9e94 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -43,7 +43,6 @@
android:id="@+id/build"
android:layout_width="0dp"
android:layout_height="match_parent"
- android:paddingStart="@dimen/qs_tile_margin_horizontal"
android:paddingEnd="4dp"
android:layout_weight="1"
android:clickable="true"
@@ -61,10 +60,23 @@
android:layout_gravity="center_vertical"
android:visibility="gone" />
- <View
+ <FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1" />
+ android:layout_weight="1">
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@android:id/edit"
+ android:layout_width="@dimen/qs_footer_action_button_size"
+ android:layout_height="@dimen/qs_footer_action_button_size"
+ android:layout_gravity="center_vertical|end"
+ android:background="?android:attr/selectableItemBackground"
+ android:clickable="true"
+ android:contentDescription="@string/accessibility_quick_settings_edit"
+ android:focusable="true"
+ android:padding="@dimen/qs_footer_icon_padding"
+ android:src="@*android:drawable/ic_mode_edit"
+ android:tint="?android:attr/textColorPrimary" />
+ </FrameLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index fc28f09..461a598 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -23,7 +23,6 @@
<color name="system_bar_background_transparent">#00000000</color>
<color name="qs_tile_divider">#29ffffff</color><!-- 16% white -->
<color name="qs_detail_button_white">#B3FFFFFF</color><!-- 70% white -->
- <color name="qs_detail_transition">#66FFFFFF</color>
<color name="status_bar_clock_color">#FFFFFFFF</color>
<color name="qs_tile_disabled_color">#9E9E9E</color> <!-- 38% black -->
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 9e350ee..4f4bd1e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -36,6 +36,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.provider.DeviceConfig;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -578,7 +579,9 @@
mMLModelThreshold = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_THRESHOLD, 0.9f);
if (mBackGestureTfClassifierProvider.isActive()) {
+ Trace.beginSection("EdgeBackGestureHandler#loadVocab");
mVocab = mBackGestureTfClassifierProvider.loadVocab(mContext.getAssets());
+ Trace.endSection();
mUseMLModel = true;
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
index e10e4d8..7ac9205 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
@@ -56,7 +56,6 @@
*/
class FooterActionsController @Inject constructor(
view: FooterActionsView,
- private val qsPanelController: QSPanelController,
private val activityStarter: ActivityStarter,
private val userManager: UserManager,
private val userTracker: UserTracker,
@@ -82,7 +81,6 @@
private val settingsButton: SettingsButton = view.findViewById(R.id.settings_button)
private val settingsButtonContainer: View? = view.findViewById(R.id.settings_button_container)
- private val editButton: View = view.findViewById(android.R.id.edit)
private val powerMenuLite: View = view.findViewById(R.id.pm_lite)
private val onUserInfoChangedListener = OnUserInfoChangedListener { _, picture, _ ->
@@ -176,13 +174,6 @@
powerMenuLite.visibility = View.GONE
}
settingsButton.setOnClickListener(onClickListener)
- editButton.setOnClickListener(View.OnClickListener { view: View? ->
- if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- return@OnClickListener
- }
- activityStarter.postQSRunnableDismissingKeyguard { qsPanelController.showEdit(view) }
- })
-
updateView()
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
index dd4dc87..7694be5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
@@ -36,7 +36,6 @@
import javax.inject.Named
class FooterActionsControllerBuilder @Inject constructor(
- private val qsPanelController: QSPanelController,
private val activityStarter: ActivityStarter,
private val userManager: UserManager,
private val userTracker: UserTracker,
@@ -66,7 +65,7 @@
}
fun build(): FooterActionsController {
- return FooterActionsController(view, qsPanelController, activityStarter, userManager,
+ return FooterActionsController(view, activityStarter, userManager,
userTracker, userInfoController, multiUserSwitchControllerFactory.create(view),
deviceProvisionedController, falsingManager, metricsLogger, tunerService,
globalActionsDialog, uiEventLogger, showPMLiteButton, buttonsVisibleState,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
index f81f7bf..e6fa2ae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
@@ -43,7 +43,6 @@
private lateinit var multiUserSwitch: MultiUserSwitch
private lateinit var multiUserAvatar: ImageView
private lateinit var tunerIcon: View
- private lateinit var editTilesButton: View
private var settingsCogAnimator: TouchAnimator? = null
@@ -52,7 +51,6 @@
override fun onFinishInflate() {
super.onFinishInflate()
- editTilesButton = requireViewById(android.R.id.edit)
settingsButton = findViewById(R.id.settings_button)
settingsContainer = findViewById(R.id.settings_button_container)
multiUserSwitch = findViewById(R.id.multi_user_switch)
@@ -130,7 +128,6 @@
private fun updateClickabilities() {
multiUserSwitch.isClickable = multiUserSwitch.visibility == VISIBLE
- editTilesButton.isClickable = editTilesButton.visibility == VISIBLE
settingsButton.isClickable = settingsButton.visibility == VISIBLE
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
index 066a286..4622660 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
@@ -47,6 +47,7 @@
private PageIndicator mPageIndicator;
private TextView mBuildText;
private View mActionsContainer;
+ private View mEditButton;
@Nullable
protected TouchAnimator mFooterAnimator;
@@ -79,6 +80,7 @@
mPageIndicator = findViewById(R.id.footer_page_indicator);
mActionsContainer = requireViewById(R.id.qs_footer_actions);
mBuildText = findViewById(R.id.build);
+ mEditButton = findViewById(android.R.id.edit);
updateResources();
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
@@ -130,6 +132,7 @@
.addFloat(mActionsContainer, "alpha", 0, 1)
.addFloat(mPageIndicator, "alpha", 0, 1)
.addFloat(mBuildText, "alpha", 0, 1)
+ .addFloat(mEditButton, "alpha", 0, 1)
.setStartDelay(0.9f);
return builder.build();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index e7c06e3..5327b7e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -26,6 +26,8 @@
import android.widget.Toast;
import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.ViewController;
@@ -45,10 +47,15 @@
private final FooterActionsController mFooterActionsController;
private final TextView mBuildText;
private final PageIndicator mPageIndicator;
+ private final View mEditButton;
+ private final FalsingManager mFalsingManager;
+ private final ActivityStarter mActivityStarter;
@Inject
QSFooterViewController(QSFooterView view,
UserTracker userTracker,
+ FalsingManager falsingManager,
+ ActivityStarter activityStarter,
QSPanelController qsPanelController,
QuickQSPanelController quickQSPanelController,
@Named(QS_FOOTER) FooterActionsController footerActionsController) {
@@ -57,9 +64,12 @@
mQsPanelController = qsPanelController;
mQuickQSPanelController = quickQSPanelController;
mFooterActionsController = footerActionsController;
+ mFalsingManager = falsingManager;
+ mActivityStarter = activityStarter;
mBuildText = mView.findViewById(R.id.build);
mPageIndicator = mView.findViewById(R.id.footer_page_indicator);
+ mEditButton = mView.findViewById(android.R.id.edit);
}
@Override
@@ -91,6 +101,14 @@
}
return false;
});
+
+ mEditButton.setOnClickListener(view -> {
+ if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ return;
+ }
+ mActivityStarter
+ .postQSRunnableDismissingKeyguard(() -> mQsPanelController.showEdit(view));
+ });
mQsPanelController.setFooterPageIndicator(mPageIndicator);
mView.updateEverything();
}
@@ -103,6 +121,7 @@
@Override
public void setVisibility(int visibility) {
mView.setVisibility(visibility);
+ mEditButton.setClickable(visibility == View.VISIBLE);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index 3449bd8..5aeab84 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -23,7 +23,6 @@
import android.os.Handler
import android.service.notification.NotificationListenerService.Ranking
import android.service.notification.NotificationListenerService.RankingMap
-import com.android.internal.statusbar.NotificationVisibility
import com.android.internal.widget.ConversationLayout
import com.android.internal.widget.MessagingImageMessage
import com.android.internal.widget.MessagingLayout
@@ -31,6 +30,7 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.inflation.BindEventManager
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
@@ -72,7 +72,8 @@
*/
@SysUISingleton
class AnimatedImageNotificationManager @Inject constructor(
- private val notificationEntryManager: NotificationEntryManager,
+ private val notifCollection: CommonNotifCollection,
+ private val bindEventManager: BindEventManager,
private val headsUpManager: HeadsUpManager,
private val statusBarStateController: StatusBarStateController
) {
@@ -83,33 +84,23 @@
fun bind() {
headsUpManager.addListener(object : OnHeadsUpChangedListener {
override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) {
- entry.row?.let { row ->
- updateAnimatedImageDrawables(row, animating = isHeadsUp || isStatusBarExpanded)
- }
+ updateAnimatedImageDrawables(entry)
}
})
statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
override fun onExpandedChanged(isExpanded: Boolean) {
isStatusBarExpanded = isExpanded
- notificationEntryManager.activeNotificationsForCurrentUser.forEach { entry ->
- entry.row?.let { row ->
- updateAnimatedImageDrawables(row, animating = isExpanded || row.isHeadsUp)
- }
- }
+ notifCollection.allNotifs.forEach(::updateAnimatedImageDrawables)
}
})
- notificationEntryManager.addNotificationEntryListener(object : NotificationEntryListener {
- override fun onEntryInflated(entry: NotificationEntry) {
- entry.row?.let { row ->
- updateAnimatedImageDrawables(
- row,
- animating = isStatusBarExpanded || row.isHeadsUp)
- }
- }
- override fun onEntryReinflated(entry: NotificationEntry) = onEntryInflated(entry)
- })
+ bindEventManager.addListener(::updateAnimatedImageDrawables)
}
+ private fun updateAnimatedImageDrawables(entry: NotificationEntry) =
+ entry.row?.let { row ->
+ updateAnimatedImageDrawables(row, animating = row.isHeadsUp || isStatusBarExpanded)
+ }
+
private fun updateAnimatedImageDrawables(row: ExpandableNotificationRow, animating: Boolean) =
(row.layouts?.asSequence() ?: emptySequence())
.flatMap { layout -> layout.allViews.asSequence() }
@@ -138,7 +129,7 @@
*/
@SysUISingleton
class ConversationNotificationManager @Inject constructor(
- private val notificationEntryManager: NotificationEntryManager,
+ private val bindEventManager: BindEventManager,
private val notificationGroupManager: NotificationGroupManagerLegacy,
private val context: Context,
private val notifCollection: CommonNotifCollection,
@@ -151,35 +142,12 @@
private var notifPanelCollapsed = true
- private val entryManagerListener = object : NotificationEntryListener {
- override fun onNotificationRankingUpdated(rankingMap: RankingMap) =
- updateNotificationRanking(rankingMap)
- override fun onEntryInflated(entry: NotificationEntry) =
- onEntryViewBound(entry)
- override fun onEntryReinflated(entry: NotificationEntry) = onEntryInflated(entry)
- override fun onEntryRemoved(
- entry: NotificationEntry,
- visibility: NotificationVisibility?,
- removedByUser: Boolean,
- reason: Int
- ) = removeTrackedEntry(entry)
- }
-
- private val notifCollectionListener = object : NotifCollectionListener {
- override fun onRankingUpdate(ranking: RankingMap) =
- updateNotificationRanking(ranking)
-
- override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
- removeTrackedEntry(entry)
- }
- }
-
private fun updateNotificationRanking(rankingMap: RankingMap) {
fun getLayouts(view: NotificationContentView) =
sequenceOf(view.contractedChild, view.expandedChild, view.headsUpChild)
val ranking = Ranking()
val activeConversationEntries = states.keys.asSequence()
- .mapNotNull { notificationEntryManager.getActiveNotificationUnfiltered(it) }
+ .mapNotNull { notifCollection.getEntry(it) }
for (entry in activeConversationEntries) {
if (rankingMap.getRanking(entry.sbn.key, ranking) && ranking.isConversation) {
val important = ranking.channel.isImportantConversation
@@ -204,7 +172,7 @@
layout.setIsImportantConversation(important, false)
}
}
- if (changed) {
+ if (changed && !featureFlags.isNewPipelineEnabled()) {
notificationGroupManager.updateIsolation(entry)
}
}
@@ -233,11 +201,14 @@
}
init {
- if (featureFlags.isNewPipelineEnabled()) {
- notifCollection.addCollectionListener(notifCollectionListener)
- } else {
- notificationEntryManager.addNotificationEntryListener(entryManagerListener)
- }
+ notifCollection.addCollectionListener(object : NotifCollectionListener {
+ override fun onRankingUpdate(ranking: RankingMap) =
+ updateNotificationRanking(ranking)
+
+ override fun onEntryRemoved(entry: NotificationEntry, reason: Int) =
+ removeTrackedEntry(entry)
+ })
+ bindEventManager.addListener(::onEntryViewBound)
}
private fun ConversationState.shouldIncrementUnread(newBuilder: Notification.Builder) =
@@ -265,11 +236,10 @@
val expanded = states
.asSequence()
.mapNotNull { (key, _) ->
- notificationEntryManager.getActiveNotificationUnfiltered(key)
- ?.let { entry ->
- if (entry.row?.isExpanded == true) key to entry
- else null
- }
+ notifCollection.getEntry(key)?.let { entry ->
+ if (entry.row?.isExpanded == true) key to entry
+ else null
+ }
}
.toMap()
states.replaceAll { key, state ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index 195f367..35fe0ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -31,13 +31,13 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.statusbar.notification.ConversationNotificationManager;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope;
+import com.android.systemui.statusbar.notification.collection.inflation.BindEventManagerImpl;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustment;
import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustmentProvider;
@@ -99,7 +99,7 @@
/** How long we can delay a group while waiting for all children to inflate */
private final long mMaxGroupInflationDelay;
- private final ConversationNotificationManager mConversationManager;
+ private final BindEventManagerImpl mBindEventManager;
@Inject
public PreparationCoordinator(
@@ -109,7 +109,7 @@
NotifViewBarn viewBarn,
NotifUiAdjustmentProvider adjustmentProvider,
IStatusBarService service,
- ConversationNotificationManager conversationManager) {
+ BindEventManagerImpl bindEventManager) {
this(
logger,
notifInflater,
@@ -117,7 +117,7 @@
viewBarn,
adjustmentProvider,
service,
- conversationManager,
+ bindEventManager,
CHILD_BIND_CUTOFF,
MAX_GROUP_INFLATION_DELAY);
}
@@ -130,7 +130,7 @@
NotifViewBarn viewBarn,
NotifUiAdjustmentProvider adjustmentProvider,
IStatusBarService service,
- ConversationNotificationManager conversationManager,
+ BindEventManagerImpl bindEventManager,
int childBindCutoff,
long maxGroupInflationDelay) {
mLogger = logger;
@@ -141,7 +141,7 @@
mStatusBarService = service;
mChildBindCutoff = childBindCutoff;
mMaxGroupInflationDelay = maxGroupInflationDelay;
- mConversationManager = conversationManager;
+ mBindEventManager = bindEventManager;
}
@Override
@@ -369,9 +369,7 @@
mInflatingNotifs.remove(entry);
mViewBarn.registerViewForEntry(entry, controller);
mInflationStates.put(entry, STATE_INFLATED);
- // NOTE: under the new pipeline there's no way to register for an inflation callback,
- // so this one method is called by the PreparationCoordinator directly.
- mConversationManager.onEntryViewBound(entry);
+ mBindEventManager.notifyViewBound(entry);
mNotifInflatingFilter.invalidateList();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManager.kt
new file mode 100644
index 0000000..51bdd00
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManager.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.inflation
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.util.ListenerSet
+
+/**
+ * Helper class that allows distributing bind events regardless of the pipeline.
+ *
+ * NOTE: This class isn't ideal; this exposes the concept of view inflation as something that can be
+ * globally registered for. This is built as it is to provide compatibility with patterns developed
+ * for the legacy pipeline. Ideally we'd have functionality that needs to know this information be
+ * handled by events that go through the ViewController itself.
+ */
+open class BindEventManager {
+ protected val listeners = ListenerSet<Listener>()
+
+ /** Register a listener */
+ fun addListener(listener: Listener) =
+ listeners.addIfAbsent(listener)
+
+ /** Deregister a listener */
+ fun removeListener(listener: Listener) =
+ listeners.remove(listener)
+
+ /** Listener interface for view bind events */
+ fun interface Listener {
+ fun onViewBound(entry: NotificationEntry)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManagerImpl.kt
new file mode 100644
index 0000000..9d5b859
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManagerImpl.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.inflation
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.NotificationEntryListener
+import com.android.systemui.statusbar.notification.NotificationEntryManager
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.inflation.BindEventManager.Listener
+import javax.inject.Inject
+
+/**
+ * Helper class that allows distributing bind events regardless of the pipeline.
+ */
+@SysUISingleton
+class BindEventManagerImpl @Inject constructor() : BindEventManager() {
+ /** Emit the [Listener.onViewBound] event to all registered listeners. */
+ fun notifyViewBound(entry: NotificationEntry) =
+ listeners.forEach { listener -> listener.onViewBound(entry) }
+
+ /** Initialize this for the legacy pipeline. */
+ fun attachToLegacyPipeline(notificationEntryManager: NotificationEntryManager) {
+ notificationEntryManager.addNotificationEntryListener(object : NotificationEntryListener {
+ override fun onEntryInflated(entry: NotificationEntry) = notifyViewBound(entry)
+ override fun onEntryReinflated(entry: NotificationEntry) = notifyViewBound(entry)
+ })
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index f1cba34..05c40b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -50,6 +50,8 @@
import com.android.systemui.statusbar.notification.collection.coordinator.ShadeEventCoordinator;
import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorsModule;
+import com.android.systemui.statusbar.notification.collection.inflation.BindEventManager;
+import com.android.systemui.statusbar.notification.collection.inflation.BindEventManagerImpl;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl;
@@ -358,5 +360,9 @@
/** */
@Binds
+ BindEventManager bindBindEventManagerImpl(BindEventManagerImpl bindEventManagerImpl);
+
+ /** */
+ @Binds
NotifLiveDataStore bindNotifLiveDataStore(NotifLiveDataStoreImpl notifLiveDataStoreImpl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 38f3c39..48f2daf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -32,6 +32,7 @@
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager
import com.android.systemui.statusbar.notification.collection.TargetSdkResolver
+import com.android.systemui.statusbar.notification.collection.inflation.BindEventManagerImpl
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
@@ -76,6 +77,7 @@
private val notifBindPipelineInitializer: NotifBindPipelineInitializer,
private val deviceProvisionedController: DeviceProvisionedController,
private val notificationRowBinder: NotificationRowBinderImpl,
+ private val bindEventManagerImpl: BindEventManagerImpl,
private val remoteInputUriController: RemoteInputUriController,
private val groupManagerLegacy: Lazy<NotificationGroupManagerLegacy>,
private val groupAlertTransferHelper: NotificationGroupAlertTransferHelper,
@@ -131,6 +133,7 @@
targetSdkResolver.initialize(entryManager)
remoteInputUriController.attach(entryManager)
groupAlertTransferHelper.bind(entryManager, groupManagerLegacy.get())
+ bindEventManagerImpl.attachToLegacyPipeline(entryManager)
headsUpManager.addListener(groupManagerLegacy.get())
headsUpManager.addListener(groupAlertTransferHelper)
headsUpController.attach(entryManager, headsUpManager)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 624e741..6eff799 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -552,14 +552,8 @@
final ViewGroup transientContainer = getTransientContainer();
if (parent == null || parent == newParent) {
// If this view's current parent is null or the same as the new parent, the add will
- // succeed, so just make sure the tracked transient container is in sync with the
- // current parent.
- if (transientContainer != null && transientContainer != parent) {
- Log.w(TAG, "Expandable view " + this
- + " has transient container " + transientContainer
- + " but different parent" + parent);
- setTransientContainer(null);
- }
+ // succeed as long as it's a true child, so just make sure the view isn't transient.
+ removeFromTransientContainer();
return;
}
if (transientContainer == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 8afa637..d2e1650 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -153,7 +153,7 @@
// to make sure correct color is returned before "prepare" is called
@Override
public int getBehindTint() {
- return Color.BLACK;
+ return DEBUG_MODE ? DEBUG_BEHIND_TINT : Color.BLACK;
}
},
@@ -264,6 +264,12 @@
}
};
+ private static final boolean DEBUG_MODE = false;
+
+ private static final int DEBUG_NOTIFICATIONS_TINT = Color.RED;
+ private static final int DEBUG_FRONT_TINT = Color.GREEN;
+ private static final int DEBUG_BEHIND_TINT = Color.BLUE;
+
boolean mBlankScreen = false;
long mAnimationDuration = ScrimController.ANIMATION_DURATION;
int mFrontTint = Color.TRANSPARENT;
@@ -323,15 +329,15 @@
}
public int getFrontTint() {
- return mFrontTint;
+ return DEBUG_MODE ? DEBUG_FRONT_TINT : mFrontTint;
}
public int getBehindTint() {
- return mBehindTint;
+ return DEBUG_MODE ? DEBUG_BEHIND_TINT : mBehindTint;
}
public int getNotifTint() {
- return mNotifTint;
+ return DEBUG_MODE ? DEBUG_NOTIFICATIONS_TINT : mNotifTint;
}
public long getAnimationDuration() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index dcb7307..1dd5e22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -34,7 +34,6 @@
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
@@ -44,6 +43,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.animation.ValueAnimator;
import android.app.Instrumentation;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -55,6 +55,7 @@
import android.os.Handler;
import android.os.SystemClock;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableResources;
import android.text.TextUtils;
import android.view.Display;
@@ -74,6 +75,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.model.SysUiState;
import com.android.systemui.util.leak.ReferenceTestUtils;
+import com.android.systemui.utils.os.FakeHandler;
import org.junit.After;
import org.junit.Before;
@@ -88,13 +90,12 @@
import java.util.List;
@LargeTest
+@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner.class)
public class WindowMagnificationControllerTest extends SysuiTestCase {
private static final int LAYOUT_CHANGE_TIMEOUT_MS = 5000;
@Mock
- private Handler mHandler;
- @Mock
private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
@Mock
private MirrorWindowControl mMirrorWindowControl;
@@ -102,17 +103,21 @@
private WindowMagnifierCallback mWindowMagnifierCallback;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+
+ private Handler mHandler;
private TestableWindowManager mWindowManager;
private SysUiState mSysUiState = new SysUiState();
private Resources mResources;
private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
private WindowMagnificationController mWindowMagnificationController;
private Instrumentation mInstrumentation;
+ private final ValueAnimator mValueAnimator = ValueAnimator.ofFloat(0, 1.0f).setDuration(0);
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = Mockito.spy(getContext());
+ mHandler = new FakeHandler(TestableLooper.get(this).getLooper());
mInstrumentation = InstrumentationRegistry.getInstrumentation();
final WindowManager wm = mContext.getSystemService(WindowManager.class);
mWindowManager = spy(new TestableWindowManager(wm));
@@ -124,17 +129,11 @@
return null;
}).when(mSfVsyncFrameProvider).postFrameCallback(
any(FrameCallback.class));
- doAnswer(invocation -> {
- final Runnable runnable = invocation.getArgument(0);
- runnable.run();
- return null;
- }).when(mHandler).post(
- any(Runnable.class));
mSysUiState.addCallback(Mockito.mock(SysUiState.SysUiStateCallback.class));
mResources = getContext().getOrCreateTestableResources().getResources();
mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
- mContext);
+ mContext, mValueAnimator);
mWindowMagnificationController = new WindowMagnificationController(mContext,
mHandler, mWindowMagnificationAnimationController, mSfVsyncFrameProvider,
mMirrorWindowControl, mTransaction, mWindowMagnifierCallback, mSysUiState);
@@ -147,6 +146,7 @@
public void tearDown() {
mInstrumentation.runOnMainSync(
() -> mWindowMagnificationController.deleteWindowMagnification());
+ mValueAnimator.cancel();
}
@Test
@@ -287,12 +287,6 @@
@Test
public void setScale_enabled_expectedValueAndUpdateStateDescription() {
- doAnswer(invocation -> {
- final Runnable runnable = invocation.getArgument(0);
- runnable.run();
- return null;
- }).when(mHandler).postDelayed(any(Runnable.class), anyLong());
-
mInstrumentation.runOnMainSync(
() -> mWindowMagnificationController.enableWindowMagnificationInternal(2.0f,
Float.NaN, Float.NaN));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
index 26f04fc..354bb51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
@@ -54,8 +54,6 @@
@Mock
private lateinit var userInfoController: UserInfoController
@Mock
- private lateinit var qsPanelController: QSPanelController
- @Mock
private lateinit var multiUserSwitchController: MultiUserSwitchController
@Mock
private lateinit var globalActionsDialog: GlobalActionsDialogLite
@@ -81,7 +79,7 @@
view = LayoutInflater.from(context)
.inflate(R.layout.footer_actions, null) as FooterActionsView
- controller = FooterActionsController(view, qsPanelController, activityStarter,
+ controller = FooterActionsController(view, activityStarter,
userManager, userTracker, userInfoController, multiUserSwitchController,
deviceProvisionedController, falsingManager, metricsLogger, fakeTunerService,
globalActionsDialog, uiEventLogger, showPMLiteButton = true,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
index 8b19c50..f43e68f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
@@ -18,7 +18,11 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.content.ClipData;
@@ -31,6 +35,8 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.utils.leaks.LeakCheckedTest;
@@ -60,13 +66,20 @@
private TextView mBuildText;
@Mock
private FooterActionsController mFooterActionsController;
+ @Mock
+ private FalsingManager mFalsingManager;
+ @Mock
+ private ActivityStarter mActivityStarter;
private QSFooterViewController mController;
+ private View mEditButton;
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
+ mEditButton = new View(mContext);
+
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
mContext.addMockSystemService(ClipboardManager.class, mClipboardManager);
@@ -77,9 +90,11 @@
when(mView.isAttachedToWindow()).thenReturn(true);
when(mView.findViewById(R.id.build)).thenReturn(mBuildText);
+ when(mView.findViewById(android.R.id.edit)).thenReturn(mEditButton);
- mController = new QSFooterViewController(mView, mUserTracker, mQSPanelController,
- mQuickQSPanelController, mFooterActionsController);
+ mController = new QSFooterViewController(mView, mUserTracker, mFalsingManager,
+ mActivityStarter, mQSPanelController, mQuickQSPanelController,
+ mFooterActionsController);
mController.init();
}
@@ -99,4 +114,27 @@
verify(mClipboardManager).setPrimaryClip(captor.capture());
assertThat(captor.getValue().getItemAt(0).getText()).isEqualTo(text);
}
+
+ @Test
+ public void testEditButton_falseTap() {
+ when(mFalsingManager.isFalseTap(anyInt())).thenReturn(true);
+
+ mEditButton.performClick();
+
+ verify(mQSPanelController, never()).showEdit(any());
+ verifyZeroInteractions(mActivityStarter);
+ }
+
+ @Test
+ public void testEditButton_realTap() {
+ when(mFalsingManager.isFalseTap(anyInt())).thenReturn(false);
+
+ mEditButton.performClick();
+
+ ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
+
+ verify(mActivityStarter).postQSRunnableDismissingKeyguard(captor.capture());
+ captor.getValue().run();
+ verify(mQSPanelController).showEdit(mEditButton);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
index bde6734..3b034f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.never;
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 static java.util.Objects.requireNonNull;
@@ -40,7 +41,6 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.RankingBuilder;
-import com.android.systemui.statusbar.notification.ConversationNotificationManager;
import com.android.systemui.statusbar.notification.SectionClassifier;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
@@ -48,6 +48,7 @@
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.inflation.BindEventManagerImpl;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustmentProvider;
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
@@ -93,7 +94,7 @@
@Mock private NotifSection mNotifSection;
@Mock private NotifPipeline mNotifPipeline;
@Mock private IStatusBarService mService;
- @Mock private ConversationNotificationManager mConvoManager;
+ @Mock private BindEventManagerImpl mBindEventManagerImpl;
@Spy private FakeNotifInflater mNotifInflater = new FakeNotifInflater();
private final SectionClassifier mSectionClassifier = new SectionClassifier();
private final NotifUiAdjustmentProvider mAdjustmentProvider =
@@ -121,7 +122,7 @@
mock(NotifViewBarn.class),
mAdjustmentProvider,
mService,
- mConvoManager,
+ mBindEventManagerImpl,
TEST_CHILD_BIND_CUTOFF,
TEST_MAX_GROUP_DELAY);
@@ -411,7 +412,8 @@
public void testCallConversationManagerBindWhenInflated() {
mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
mNotifInflater.getInflateCallback(mEntry).onInflationFinished(mEntry, null);
- verify(mConvoManager, times(1)).onEntryViewBound(eq(mEntry));
+ verify(mBindEventManagerImpl, times(1)).notifyViewBound(eq(mEntry));
+ verifyNoMoreInteractions(mBindEventManagerImpl);
}
@Test
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 1666d15..f56bfab 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -105,9 +105,10 @@
public static final int PACKAGE_COMPANION = 15;
public static final int PACKAGE_RETAIL_DEMO = 16;
public static final int PACKAGE_RECENTS = 17;
+ public static final int PACKAGE_AMBIENT_CONTEXT_DETECTION = 18;
// Integer value of the last known package ID. Increases as new ID is added to KnownPackage.
// Please note the numbers should be continuous.
- public static final int LAST_KNOWN_PACKAGE = PACKAGE_RECENTS;
+ public static final int LAST_KNOWN_PACKAGE = PACKAGE_AMBIENT_CONTEXT_DETECTION;
@LongDef(flag = true, prefix = "RESOLVE_", value = {
RESOLVE_NON_BROWSER_ONLY,
diff --git a/services/core/java/com/android/server/MasterClearReceiver.java b/services/core/java/com/android/server/MasterClearReceiver.java
index 62dc2733..be2b7f7 100644
--- a/services/core/java/com/android/server/MasterClearReceiver.java
+++ b/services/core/java/com/android/server/MasterClearReceiver.java
@@ -16,11 +16,14 @@
package com.android.server;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_TITLE;
+
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.ProgressDialog;
+import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -155,7 +158,7 @@
final Notification notification =
new Notification.Builder(context, SystemNotificationChannels.DEVICE_ADMIN)
.setSmallIcon(android.R.drawable.stat_sys_warning)
- .setContentTitle(context.getString(R.string.work_profile_deleted))
+ .setContentTitle(getWorkProfileDeletedTitle(context))
.setContentText(wipeReason)
.setColor(context.getColor(R.color.system_notification_accent_color))
.setStyle(new Notification.BigTextStyle().bigText(wipeReason))
@@ -164,6 +167,12 @@
SystemMessageProto.SystemMessage.NOTE_PROFILE_WIPED, notification);
}
+ private String getWorkProfileDeletedTitle(Context context) {
+ final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(WORK_PROFILE_DELETED_TITLE,
+ () -> context.getString(R.string.work_profile_deleted));
+ }
+
private @UserIdInt int getCurrentForegroundUserId() {
try {
return ActivityManager.getCurrentUser();
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 98764e0..f71f02a 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -160,8 +160,6 @@
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal.ScreenObserver;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
import libcore.io.IoUtils;
import libcore.util.EmptyArray;
@@ -173,6 +171,8 @@
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
@@ -3698,16 +3698,29 @@
@Nullable
public PendingIntent getManageSpaceActivityIntent(
@NonNull String packageName, int requestCode) {
- // Only Apps with MANAGE_EXTERNAL_STORAGE permission should be able to call this API.
- enforcePermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE);
-
- // We want to call the manageSpaceActivity as a SystemService and clear identity
- // of the calling App
+ // Only Apps with MANAGE_EXTERNAL_STORAGE permission which have package visibility for
+ // packageName should be able to call this API.
int originalUid = Binder.getCallingUidOrThrow();
- final long token = Binder.clearCallingIdentity();
-
try {
- ApplicationInfo appInfo = mIPackageManager.getApplicationInfo(packageName, 0,
+ // Get package name for calling app and verify it has MANAGE_EXTERNAL_STORAGE permission
+ final String[] packagesFromUid = mIPackageManager.getPackagesForUid(originalUid);
+ if (packagesFromUid == null) {
+ throw new SecurityException("Unknown uid " + originalUid);
+ }
+ // Checking first entry in packagesFromUid is enough as using "sharedUserId"
+ // mechanism is rare and discouraged. Also, Apps that share same UID share the same
+ // permissions.
+ if (!mStorageManagerInternal.hasExternalStorageAccess(originalUid,
+ packagesFromUid[0])) {
+ throw new SecurityException("Only File Manager Apps permitted");
+ }
+ } catch (RemoteException re) {
+ throw new SecurityException("Unknown uid " + originalUid, re);
+ }
+
+ ApplicationInfo appInfo;
+ try {
+ appInfo = mIPackageManager.getApplicationInfo(packageName, 0,
UserHandle.getUserId(originalUid));
if (appInfo == null) {
throw new IllegalArgumentException(
@@ -3717,8 +3730,15 @@
Log.i(TAG, packageName + " doesn't have a manageSpaceActivity");
return null;
}
- Context targetAppContext = mContext.createPackageContext(packageName, 0);
+ } catch (RemoteException e) {
+ throw new SecurityException("Only File Manager Apps permitted");
+ }
+ // We want to call the manageSpaceActivity as a SystemService and clear identity
+ // of the calling App
+ final long token = Binder.clearCallingIdentity();
+ try {
+ Context targetAppContext = mContext.createPackageContext(packageName, 0);
Intent intent = new Intent(Intent.ACTION_DEFAULT);
intent.setClassName(packageName,
appInfo.manageSpaceActivityName);
@@ -3728,8 +3748,6 @@
intent,
FLAG_ONE_SHOT | FLAG_CANCEL_CURRENT | FLAG_IMMUTABLE);
return activity;
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
} catch (PackageManager.NameNotFoundException e) {
throw new IllegalArgumentException(
"packageName not found");
@@ -4955,19 +4973,17 @@
@Override
public boolean hasExternalStorageAccess(int uid, String packageName) {
try {
- if (mIPackageManager.checkUidPermission(
- MANAGE_EXTERNAL_STORAGE, uid) == PERMISSION_GRANTED) {
- return true;
+ final int opMode = mIAppOpsService.checkOperation(
+ OP_MANAGE_EXTERNAL_STORAGE, uid, packageName);
+ if (opMode == AppOpsManager.MODE_DEFAULT) {
+ return mIPackageManager.checkUidPermission(
+ MANAGE_EXTERNAL_STORAGE, uid) == PERMISSION_GRANTED;
}
- if (mIAppOpsService.checkOperation(
- OP_MANAGE_EXTERNAL_STORAGE, uid, packageName) == MODE_ALLOWED) {
- return true;
- }
+ return opMode == AppOpsManager.MODE_ALLOWED;
} catch (RemoteException e) {
Slog.w("Failed to check MANAGE_EXTERNAL_STORAGE access for " + packageName, e);
}
-
return false;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f67e732..b1b4c44 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2871,13 +2871,51 @@
return mode == AppOpsManager.MODE_ALLOWED;
}
- @Override
- public int getPackageProcessState(String packageName, String callingPackage) {
- if (!hasUsageStatsPermission(callingPackage)) {
- enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
- "getPackageProcessState");
+ /**
+ * Checks whether the calling package is trusted.
+ *
+ * The calling package is trusted if it's from system or the supposed package name matches the
+ * UID making the call.
+ *
+ * @throws SecurityException if the package name and UID don't match.
+ */
+ private void verifyCallingPackage(String callingPackage) {
+ final int callingUid = Binder.getCallingUid();
+ // The caller is System or Shell.
+ if (callingUid == SYSTEM_UID || isCallerShell()) {
+ return;
}
+ // Handle the special UIDs that don't have real package (audioserver, cameraserver, etc).
+ final String resolvedPackage = AppOpsManager.resolvePackageName(callingUid,
+ null /* packageName */);
+ if (resolvedPackage != null && resolvedPackage.equals(callingPackage)) {
+ return;
+ }
+
+ final int claimedUid = getPackageManagerInternal().getPackageUid(callingPackage,
+ 0 /* flags */, UserHandle.getUserId(callingUid));
+ if (callingUid == claimedUid) {
+ return;
+ }
+
+ throw new SecurityException(
+ "Claimed calling package " + callingPackage + " does not match the calling UID "
+ + Binder.getCallingUid());
+ }
+
+ private void enforceUsageStatsPermission(String callingPackage, String func) {
+ verifyCallingPackage(callingPackage);
+ // Since the protection level of PACKAGE_USAGE_STATS has 'appop', apps may grant this
+ // permission via that way. We need to check both app-ops and permission.
+ if (!hasUsageStatsPermission(callingPackage)) {
+ enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS, func);
+ }
+ }
+
+ @Override
+ public int getPackageProcessState(String packageName, String callingPackage) {
+ enforceUsageStatsPermission(callingPackage, "getPackageProcessState");
final int[] procState = {PROCESS_STATE_NONEXISTENT};
synchronized (mProcLock) {
mProcessList.forEachLruProcessesLOSP(false, proc -> {
@@ -6938,11 +6976,7 @@
@Override
public int getUidProcessState(int uid, String callingPackage) {
- if (!hasUsageStatsPermission(callingPackage)) {
- enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
- "getUidProcessState");
- }
-
+ enforceUsageStatsPermission(callingPackage, "getUidProcessState");
synchronized (mProcLock) {
return mProcessList.getUidProcStateLOSP(uid);
}
@@ -6950,11 +6984,7 @@
@Override
public @ProcessCapability int getUidProcessCapabilities(int uid, String callingPackage) {
- if (!hasUsageStatsPermission(callingPackage)) {
- enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
- "getUidProcessState");
- }
-
+ enforceUsageStatsPermission(callingPackage, "getUidProcessCapabilities");
synchronized (mProcLock) {
return mProcessList.getUidProcessCapabilityLOSP(uid);
}
@@ -6963,10 +6993,7 @@
@Override
public void registerUidObserver(IUidObserver observer, int which, int cutpoint,
String callingPackage) {
- if (!hasUsageStatsPermission(callingPackage)) {
- enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
- "registerUidObserver");
- }
+ enforceUsageStatsPermission(callingPackage, "registerUidObserver");
mUidObserverController.register(observer, which, cutpoint, callingPackage,
Binder.getCallingUid());
}
@@ -6978,10 +7005,7 @@
@Override
public boolean isUidActive(int uid, String callingPackage) {
- if (!hasUsageStatsPermission(callingPackage)) {
- enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
- "isUidActive");
- }
+ enforceUsageStatsPermission(callingPackage, "isUidActive");
synchronized (mProcLock) {
if (isUidActiveLOSP(uid)) {
return true;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 921208c..0b92954 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -42,6 +42,7 @@
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Binder;
+import android.os.BluetoothBatteryStats;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -2627,6 +2628,20 @@
}
/**
+ * Gets a snapshot of Bluetooth stats
+ * @hide
+ */
+ public BluetoothBatteryStats getBluetoothBatteryStats() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BATTERY_STATS, null);
+
+ // Wait for the completion of pending works if there is any
+ awaitCompletion();
+ synchronized (mStats) {
+ return mStats.getBluetoothBatteryStats();
+ }
+ }
+
+ /**
* Gets a snapshot of the system health for a particular uid.
*/
@Override
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index c08cf64..6f22c61 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -962,7 +962,7 @@
}
opt.setFreezerOverride(false);
- if (!opt.isFrozen()) {
+ if (pid == 0 || !opt.isFrozen()) {
return;
}
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java
new file mode 100644
index 0000000..6982513
--- /dev/null
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2021 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.ambientcontext;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.app.BroadcastOptions;
+import android.app.PendingIntent;
+import android.app.ambientcontext.AmbientContextEvent;
+import android.app.ambientcontext.AmbientContextEventRequest;
+import android.app.ambientcontext.AmbientContextEventResponse;
+import android.app.ambientcontext.AmbientContextManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.Binder;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.service.ambientcontext.AmbientContextDetectionService;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.infra.AbstractPerUserSystemService;
+
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Per-user manager service for {@link AmbientContextEvent}s.
+ */
+final class AmbientContextManagerPerUserService extends
+ AbstractPerUserSystemService<AmbientContextManagerPerUserService,
+ AmbientContextManagerService> {
+ private static final String TAG = AmbientContextManagerPerUserService.class.getSimpleName();
+
+ @Nullable
+ @VisibleForTesting
+ RemoteAmbientContextDetectionService mRemoteService;
+
+ private ComponentName mComponentName;
+ private Context mContext;
+ private Set<PendingIntent> mExistingPendingIntents;
+
+ AmbientContextManagerPerUserService(
+ @NonNull AmbientContextManagerService master, Object lock, @UserIdInt int userId) {
+ super(master, lock, userId);
+ mContext = master.getContext();
+ mExistingPendingIntents = new HashSet<>();
+ }
+
+ void destroyLocked() {
+ if (isVerbose()) {
+ Slog.v(TAG, "destroyLocked()");
+ }
+
+ Slog.d(TAG, "Trying to cancel the remote request. Reason: Service destroyed.");
+ if (mRemoteService != null) {
+ synchronized (mLock) {
+ mRemoteService.unbind();
+ mRemoteService = null;
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void ensureRemoteServiceInitiated() {
+ if (mRemoteService == null) {
+ mRemoteService = new RemoteAmbientContextDetectionService(
+ getContext(), mComponentName, getUserId());
+ }
+ }
+
+ /**
+ * get the currently bound component name.
+ */
+ @VisibleForTesting
+ ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+
+ /**
+ * Resolves and sets up the service if it had not been done yet. Returns true if the service
+ * is available.
+ */
+ @GuardedBy("mLock")
+ @VisibleForTesting
+ boolean setUpServiceIfNeeded() {
+ if (mComponentName == null) {
+ mComponentName = updateServiceInfoLocked();
+ }
+ return mComponentName != null;
+ }
+
+ @Override
+ protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
+ throws PackageManager.NameNotFoundException {
+ ServiceInfo serviceInfo;
+ try {
+ serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+ 0, mUserId);
+ if (serviceInfo != null) {
+ final String permission = serviceInfo.permission;
+ if (!Manifest.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE.equals(
+ permission)) {
+ throw new SecurityException(String.format(
+ "Service %s requires %s permission. Found %s permission",
+ serviceInfo.getComponentName(),
+ Manifest.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE,
+ serviceInfo.permission));
+ }
+ }
+ } catch (RemoteException e) {
+ throw new PackageManager.NameNotFoundException(
+ "Could not get service for " + serviceComponent);
+ }
+ return serviceInfo;
+ }
+
+ @Override
+ protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
+ synchronized (super.mLock) {
+ super.dumpLocked(prefix, pw);
+ }
+ if (mRemoteService != null) {
+ mRemoteService.dump("", new IndentingPrintWriter(pw, " "));
+ }
+ }
+
+ /**
+ * Handles client registering as an observer. Only one registration is supported per app
+ * package. A new registration from the same package will overwrite the previous registration.
+ */
+ public void onRegisterObserver(AmbientContextEventRequest request,
+ PendingIntent pendingIntent) {
+ synchronized (mLock) {
+ if (!setUpServiceIfNeeded()) {
+ Slog.w(TAG, "Service is not available at this moment.");
+ sendStatusUpdateIntent(
+ pendingIntent, AmbientContextEventResponse.STATUS_SERVICE_UNAVAILABLE);
+ return;
+ }
+
+ // Remove any existing intent and unregister for this package before adding a new one.
+ String callingPackage = pendingIntent.getCreatorPackage();
+ PendingIntent duplicatePendingIntent = findExistingRequestByPackage(callingPackage);
+ if (duplicatePendingIntent != null) {
+ Slog.d(TAG, "Unregister duplicate request from " + callingPackage);
+ onUnregisterObserver(callingPackage);
+ mExistingPendingIntents.remove(duplicatePendingIntent);
+ }
+
+ // Register new package and add request to mExistingRequests
+ startDetection(request, callingPackage, createRemoteCallback());
+ mExistingPendingIntents.add(pendingIntent);
+ }
+ }
+
+ @VisibleForTesting
+ void startDetection(AmbientContextEventRequest request, String callingPackage,
+ RemoteCallback callback) {
+ Slog.d(TAG, "Requested detection of " + request.getEventTypes());
+ synchronized (mLock) {
+ ensureRemoteServiceInitiated();
+ mRemoteService.startDetection(request, callingPackage, callback);
+ }
+ }
+
+ /**
+ * Sends an intent with a status code and empty events.
+ */
+ void sendStatusUpdateIntent(PendingIntent pendingIntent, int statusCode) {
+ AmbientContextEventResponse response = new AmbientContextEventResponse.Builder()
+ .setStatusCode(statusCode)
+ .build();
+ sendResponseIntent(pendingIntent, response);
+ }
+
+ /**
+ * Unregisters the client from all previously registered events by removing from the
+ * mExistingRequests map, and unregister events from the service if those events are not
+ * requested by other apps.
+ */
+ public void onUnregisterObserver(String callingPackage) {
+ synchronized (mLock) {
+ PendingIntent pendingIntent = findExistingRequestByPackage(callingPackage);
+ if (pendingIntent == null) {
+ Slog.d(TAG, "No registration found for " + callingPackage);
+ return;
+ }
+
+ // Remove from existing requests
+ mExistingPendingIntents.remove(pendingIntent);
+ stopDetection(pendingIntent.getCreatorPackage());
+ }
+ }
+
+ @VisibleForTesting
+ void stopDetection(String packageName) {
+ Slog.d(TAG, "Stop detection for " + packageName);
+ synchronized (mLock) {
+ ensureRemoteServiceInitiated();
+ mRemoteService.stopDetection(packageName);
+ }
+ }
+
+ @Nullable
+ private PendingIntent findExistingRequestByPackage(String callingPackage) {
+ for (PendingIntent pendingIntent : mExistingPendingIntents) {
+ if (pendingIntent.getCreatorPackage().equals(callingPackage)) {
+ return pendingIntent;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Sends out the Intent to the client after the event is detected.
+ *
+ * @param pendingIntent Client's PendingIntent for callback
+ * @param response Response with status code and detection result
+ */
+ private void sendResponseIntent(PendingIntent pendingIntent,
+ AmbientContextEventResponse response) {
+ Intent intent = new Intent();
+ intent.putExtra(AmbientContextManager.EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE, response);
+ // Explicitly disallow the receiver from starting activities, to prevent apps from utilizing
+ // the PendingIntent as a backdoor to do this.
+ BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setPendingIntentBackgroundActivityLaunchAllowed(false);
+ try {
+ pendingIntent.send(getContext(), 0, intent, null, null, null,
+ options.toBundle());
+ Slog.i(TAG, "Sending PendingIntent to " + pendingIntent.getCreatorPackage() + ": "
+ + response);
+ } catch (PendingIntent.CanceledException e) {
+ Slog.w(TAG, "Couldn't deliver pendingIntent:" + pendingIntent);
+ }
+ }
+
+ @NonNull
+ private RemoteCallback createRemoteCallback() {
+ return new RemoteCallback(result -> {
+ AmbientContextEventResponse response = (AmbientContextEventResponse) result.get(
+ AmbientContextDetectionService.RESPONSE_BUNDLE_KEY);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ Set<PendingIntent> pendingIntentForFailedRequests = new HashSet<>();
+ for (PendingIntent pendingIntent : mExistingPendingIntents) {
+ // Send PendingIntent if a requesting package matches the response packages.
+ if (response.getPackageName().equals(pendingIntent.getCreatorPackage())) {
+ sendResponseIntent(pendingIntent, response);
+
+ int statusCode = response.getStatusCode();
+ if (statusCode != AmbientContextEventResponse.STATUS_SUCCESS) {
+ pendingIntentForFailedRequests.add(pendingIntent);
+ }
+ Slog.i(TAG, "Got response of " + response.getEvents() + " for "
+ + pendingIntent.getCreatorPackage() + ". Status: " + statusCode);
+ }
+ }
+
+ // Removes the failed requests from the existing requests.
+ for (PendingIntent pendingIntent : pendingIntentForFailedRequests) {
+ mExistingPendingIntents.remove(pendingIntent);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ });
+ }
+}
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
new file mode 100644
index 0000000..33905f2
--- /dev/null
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2021 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.ambientcontext;
+
+import static android.provider.DeviceConfig.NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.PendingIntent;
+import android.app.ambientcontext.AmbientContextEvent;
+import android.app.ambientcontext.AmbientContextEventRequest;
+import android.app.ambientcontext.AmbientContextEventResponse;
+import android.app.ambientcontext.IAmbientContextEventObserver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManagerInternal;
+import android.os.RemoteCallback;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.util.DumpUtils;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * System service for managing {@link AmbientContextEvent}s.
+ */
+public class AmbientContextManagerService extends
+ AbstractMasterSystemService<AmbientContextManagerService,
+ AmbientContextManagerPerUserService> {
+ private static final String TAG = AmbientContextManagerService.class.getSimpleName();
+ private static final String KEY_SERVICE_ENABLED = "service_enabled";
+
+ /** Default value in absence of {@link DeviceConfig} override. */
+ private static final boolean DEFAULT_SERVICE_ENABLED = true;
+
+ private final Context mContext;
+ boolean mIsServiceEnabled;
+
+ public AmbientContextManagerService(Context context) {
+ super(context,
+ new FrameworkResourcesServiceNameResolver(
+ context,
+ R.string.config_defaultAmbientContextDetectionService),
+ /*disallowProperty=*/null,
+ PACKAGE_UPDATE_POLICY_REFRESH_EAGER
+ | /*To avoid high latency*/ PACKAGE_RESTART_POLICY_REFRESH_EAGER);
+ mContext = context;
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.AMBIENT_CONTEXT_SERVICE, new AmbientContextEventObserver());
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ DeviceConfig.addOnPropertiesChangedListener(
+ NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE,
+ getContext().getMainExecutor(),
+ (properties) -> onDeviceConfigChange(properties.getKeyset()));
+
+ mIsServiceEnabled = DeviceConfig.getBoolean(
+ NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE,
+ KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED);
+ }
+ }
+
+ private void onDeviceConfigChange(@NonNull Set<String> keys) {
+ if (keys.contains(KEY_SERVICE_ENABLED)) {
+ mIsServiceEnabled = DeviceConfig.getBoolean(
+ NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE,
+ KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED);
+ }
+ }
+
+ @Override
+ protected AmbientContextManagerPerUserService newServiceLocked(int resolvedUserId,
+ boolean disabled) {
+ return new AmbientContextManagerPerUserService(this, mLock, resolvedUserId);
+ }
+
+ @Override
+ protected void onServiceRemoved(
+ AmbientContextManagerPerUserService service, @UserIdInt int userId) {
+ service.destroyLocked();
+ }
+
+ /** Returns {@code true} if the detection service is configured on this device. */
+ public static boolean isDetectionServiceConfigured() {
+ final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+ final String[] packageNames = pmi.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_AMBIENT_CONTEXT_DETECTION, UserHandle.USER_SYSTEM);
+ boolean isServiceConfigured = (packageNames.length != 0);
+ Slog.i(TAG, "Detection service configured: " + isServiceConfigured);
+ return isServiceConfigured;
+ }
+
+ /**
+ * Send request to the remote AmbientContextDetectionService impl to start detecting the
+ * specified events. Intended for use by shell command for testing.
+ * Requires ACCESS_AMBIENT_CONTEXT_EVENT permission.
+ */
+ void startAmbientContextEvent(@UserIdInt int userId, AmbientContextEventRequest request,
+ String packageName, RemoteCallback callback) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
+ synchronized (mLock) {
+ final AmbientContextManagerPerUserService service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.startDetection(request, packageName, callback);
+ } else {
+ Slog.i(TAG, "service not available for user_id: " + userId);
+ }
+ }
+ }
+
+ /**
+ * Send request to the remote AmbientContextDetectionService impl to stop detecting the
+ * specified events. Intended for use by shell command for testing.
+ * Requires ACCESS_AMBIENT_CONTEXT_EVENT permission.
+ */
+ void stopAmbientContextEvent(@UserIdInt int userId, String packageName) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
+ synchronized (mLock) {
+ final AmbientContextManagerPerUserService service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.stopDetection(packageName);
+ } else {
+ Slog.i(TAG, "service not available for user_id: " + userId);
+ }
+ }
+ }
+
+ /**
+ * Returns the AmbientContextManagerPerUserService component for this user.
+ */
+ public ComponentName getComponentName(@UserIdInt int userId) {
+ synchronized (mLock) {
+ final AmbientContextManagerPerUserService service = getServiceForUserLocked(userId);
+ if (service != null) {
+ return service.getComponentName();
+ }
+ }
+ return null;
+ }
+
+ private final class AmbientContextEventObserver extends IAmbientContextEventObserver.Stub {
+ final AmbientContextManagerPerUserService mService = getServiceForUserLocked(
+ UserHandle.getCallingUserId());
+
+ @Override
+ public void registerObserver(
+ AmbientContextEventRequest request, PendingIntent pendingIntent) {
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(pendingIntent);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available.");
+ mService.sendStatusUpdateIntent(pendingIntent,
+ AmbientContextEventResponse.STATUS_SERVICE_UNAVAILABLE);
+ return;
+ }
+ mService.onRegisterObserver(request, pendingIntent);
+ }
+
+ @Override
+ public void unregisterObserver(String callingPackage) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
+ mService.onUnregisterObserver(callingPackage);
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) {
+ return;
+ }
+ synchronized (mLock) {
+ dumpLocked("", pw);
+ }
+ }
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+ new AmbientContextShellCommand(AmbientContextManagerService.this).exec(
+ this, in, out, err, args, callback, resultReceiver);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java b/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java
new file mode 100644
index 0000000..b5cd985
--- /dev/null
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2021 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.ambientcontext;
+
+import static java.lang.System.out;
+
+import android.annotation.NonNull;
+import android.app.ambientcontext.AmbientContextEvent;
+import android.app.ambientcontext.AmbientContextEventRequest;
+import android.app.ambientcontext.AmbientContextEventResponse;
+import android.content.ComponentName;
+import android.os.Binder;
+import android.os.RemoteCallback;
+import android.os.ShellCommand;
+import android.service.ambientcontext.AmbientContextDetectionService;
+
+import java.io.PrintWriter;
+
+/**
+ * Shell command for {@link AmbientContextManagerService}.
+ */
+final class AmbientContextShellCommand extends ShellCommand {
+
+ @NonNull
+ private final AmbientContextManagerService mService;
+
+ AmbientContextShellCommand(@NonNull AmbientContextManagerService service) {
+ mService = service;
+ }
+
+ /** Callbacks for AmbientContextEventService results used internally for testing. */
+ static class TestableCallbackInternal {
+ private AmbientContextEventResponse mLastResponse;
+
+ public AmbientContextEventResponse getLastResponse() {
+ return mLastResponse;
+ }
+
+ @NonNull
+ private RemoteCallback createRemoteCallback() {
+ return new RemoteCallback(result -> {
+ AmbientContextEventResponse response =
+ (AmbientContextEventResponse) result.get(
+ AmbientContextDetectionService.RESPONSE_BUNDLE_KEY);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mLastResponse = response;
+ out.println("Response available: " + response);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ });
+ }
+ }
+
+ static final TestableCallbackInternal sTestableCallbackInternal =
+ new TestableCallbackInternal();
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+
+ switch (cmd) {
+ case "start-detection":
+ return runStartDetection();
+ case "stop-detection":
+ return runStopDetection();
+ case "get-last-status-code":
+ return getLastStatusCode();
+ case "get-bound-package":
+ return getBoundPackageName();
+ case "set-temporary-service":
+ return setTemporaryService();
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ private int runStartDetection() {
+ final int userId = Integer.parseInt(getNextArgRequired());
+ final String packageName = getNextArgRequired();
+ AmbientContextEventRequest request = new AmbientContextEventRequest.Builder()
+ .addEventType(AmbientContextEvent.EVENT_COUGH)
+ .addEventType(AmbientContextEvent.EVENT_SNORE)
+ .build();
+
+ mService.startAmbientContextEvent(userId, request, packageName,
+ sTestableCallbackInternal.createRemoteCallback());
+ return 0;
+ }
+
+ private int runStopDetection() {
+ final int userId = Integer.parseInt(getNextArgRequired());
+ final String packageName = getNextArgRequired();
+ mService.stopAmbientContextEvent(userId, packageName);
+ return 0;
+ }
+
+ private int getLastStatusCode() {
+ AmbientContextEventResponse lastResponse = sTestableCallbackInternal.getLastResponse();
+ if (lastResponse == null) {
+ return -1;
+ }
+ return lastResponse.getStatusCode();
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("AmbientContextEvent commands: ");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println();
+ pw.println(" start-detection USER_ID PACKAGE_NAME: Starts AmbientContextEvent detection.");
+ pw.println(" stop-detection USER_ID: Stops AmbientContextEvent detection.");
+ pw.println(" get-last-status-code: Prints the latest request status code.");
+ pw.println(" get-bound-package USER_ID:"
+ + " Print the bound package that implements the service.");
+ pw.println(" set-temporary-service USER_ID [COMPONENT_NAME DURATION]");
+ pw.println(" Temporarily (for DURATION ms) changes the service implementation.");
+ pw.println(" To reset, call with just the USER_ID argument.");
+ }
+
+ private int getBoundPackageName() {
+ final PrintWriter out = getOutPrintWriter();
+ final int userId = Integer.parseInt(getNextArgRequired());
+ final ComponentName componentName = mService.getComponentName(userId);
+ out.println(componentName == null ? "" : componentName.getPackageName());
+ return 0;
+ }
+
+ private int setTemporaryService() {
+ final PrintWriter out = getOutPrintWriter();
+ final int userId = Integer.parseInt(getNextArgRequired());
+ final String serviceName = getNextArg();
+ if (serviceName == null) {
+ mService.resetTemporaryService(userId);
+ out.println("AmbientContextDetectionService temporary reset. ");
+ return 0;
+ }
+
+ final int duration = Integer.parseInt(getNextArgRequired());
+ mService.setTemporaryService(userId, serviceName, duration);
+ out.println("AmbientContextDetectionService temporarily set to " + serviceName
+ + " for " + duration + "ms");
+ return 0;
+ }
+}
diff --git a/services/core/java/com/android/server/ambientcontext/OWNERS b/services/core/java/com/android/server/ambientcontext/OWNERS
new file mode 100644
index 0000000..a863297
--- /dev/null
+++ b/services/core/java/com/android/server/ambientcontext/OWNERS
@@ -0,0 +1,3 @@
+enxun@google.com
+kxchen@google.com
+tgadh@google.com
diff --git a/services/core/java/com/android/server/ambientcontext/RemoteAmbientContextDetectionService.java b/services/core/java/com/android/server/ambientcontext/RemoteAmbientContextDetectionService.java
new file mode 100644
index 0000000..5cc29b3
--- /dev/null
+++ b/services/core/java/com/android/server/ambientcontext/RemoteAmbientContextDetectionService.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 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.ambientcontext;
+
+import static android.content.Context.BIND_FOREGROUND_SERVICE;
+import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
+
+import android.annotation.NonNull;
+import android.app.ambientcontext.AmbientContextEventRequest;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteCallback;
+import android.service.ambientcontext.AmbientContextDetectionService;
+import android.service.ambientcontext.IAmbientContextDetectionService;
+import android.util.Slog;
+
+import com.android.internal.infra.ServiceConnector;
+
+/** Manages the connection to the remote service. */
+final class RemoteAmbientContextDetectionService
+ extends ServiceConnector.Impl<IAmbientContextDetectionService> {
+ private static final String TAG =
+ RemoteAmbientContextDetectionService.class.getSimpleName();
+
+ RemoteAmbientContextDetectionService(Context context, ComponentName serviceName,
+ int userId) {
+ super(context, new Intent(
+ AmbientContextDetectionService.SERVICE_INTERFACE).setComponent(serviceName),
+ BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId,
+ IAmbientContextDetectionService.Stub::asInterface);
+
+ // Bind right away
+ connect();
+ }
+
+ /**
+ * Asks the implementation to start detection.
+ *
+ * @param request The request with events to detect, and optional detection options.
+ * @param packageName The app package that requested the detection
+ * @param callback callback for detection results
+ */
+ public void startDetection(
+ @NonNull AmbientContextEventRequest request, String packageName,
+ RemoteCallback callback) {
+ Slog.i(TAG, "Start detection for " + request.getEventTypes());
+ post(service -> service.startDetection(request, packageName, callback));
+ }
+
+ /**
+ * Asks the implementation to stop detection.
+ *
+ * @param packageName stop detection for the given package
+ */
+ public void stopDetection(String packageName) {
+ Slog.i(TAG, "Stop detection for " + packageName);
+ post(service -> service.stopDetection(packageName));
+ }
+}
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 53f651b..f5f7bb3 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -43,6 +43,7 @@
import android.app.ActivityManager;
import android.app.GameManager;
import android.app.GameManager.GameMode;
+import android.app.GameModeInfo;
import android.app.GameState;
import android.app.IGameManagerService;
import android.app.compat.PackageOverride;
@@ -694,6 +695,32 @@
}
}
+ private @GameMode int[] getAvailableGameModesUnchecked(String packageName) {
+ GamePackageConfiguration config = null;
+ synchronized (mOverrideConfigLock) {
+ config = mOverrideConfigs.get(packageName);
+ }
+ if (config == null) {
+ synchronized (mDeviceConfigLock) {
+ config = mConfigs.get(packageName);
+ }
+ }
+ if (config == null) {
+ return new int[]{};
+ }
+ return config.getAvailableGameModes();
+ }
+
+ private boolean isPackageGame(String packageName, @UserIdInt int userId) {
+ try {
+ final ApplicationInfo applicationInfo = mPackageManager
+ .getApplicationInfoAsUser(packageName, PackageManager.MATCH_ALL, userId);
+ return applicationInfo.category == ApplicationInfo.CATEGORY_GAME;
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
/**
* Get an array of game modes available for a given package.
* Checks that the caller has {@link android.Manifest.permission#MANAGE_GAME_MODE}.
@@ -702,19 +729,7 @@
@RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
public @GameMode int[] getAvailableGameModes(String packageName) throws SecurityException {
checkPermission(Manifest.permission.MANAGE_GAME_MODE);
- GamePackageConfiguration config = null;
- synchronized (mOverrideConfigLock) {
- config = mOverrideConfigs.get(packageName);
- }
- if (config == null) {
- synchronized (mDeviceConfigLock) {
- config = mConfigs.get(packageName);
- }
- }
- if (config == null) {
- return new int[]{GameManager.GAME_MODE_UNSUPPORTED};
- }
- return config.getAvailableGameModes();
+ return getAvailableGameModesUnchecked(packageName);
}
private @GameMode int getGameModeFromSettings(String packageName, @UserIdInt int userId) {
@@ -735,28 +750,22 @@
* {@link android.Manifest.permission#MANAGE_GAME_MODE}.
*/
@Override
- public @GameMode int getGameMode(String packageName, int userId)
+ public @GameMode int getGameMode(@NonNull String packageName, @UserIdInt int userId)
throws SecurityException {
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, true, "getGameMode",
"com.android.server.app.GameManagerService");
// Restrict to games only.
- try {
- final ApplicationInfo applicationInfo = mPackageManager
- .getApplicationInfoAsUser(packageName, PackageManager.MATCH_ALL, userId);
- if (applicationInfo.category != ApplicationInfo.CATEGORY_GAME) {
- // The game mode for applications that are not identified as game is always
- // UNSUPPORTED. See {@link PackageManager#setApplicationCategoryHint(String, int)}
- return GameManager.GAME_MODE_UNSUPPORTED;
- }
- } catch (PackageManager.NameNotFoundException e) {
+ if (!isPackageGame(packageName, userId)) {
+ // The game mode for applications that are not identified as game is always
+ // UNSUPPORTED. See {@link PackageManager#setApplicationCategoryHint(String, int)}
return GameManager.GAME_MODE_UNSUPPORTED;
}
// This function handles two types of queries:
- // 1.) A normal, non-privileged app querying its own Game Mode.
- // 2.) A privileged system service querying the Game Mode of another package.
+ // 1) A normal, non-privileged app querying its own Game Mode.
+ // 2) A privileged system service querying the Game Mode of another package.
// The least privileged case is a normal app performing a query, so check that first and
// return a value if the package name is valid. Next, check if the caller has the necessary
// permission and return a value. Do this check last, since it can throw an exception.
@@ -769,14 +778,32 @@
return getGameModeFromSettings(packageName, userId);
}
- private boolean isPackageGame(String packageName, int userId) {
- try {
- final ApplicationInfo applicationInfo = mPackageManager
- .getApplicationInfoAsUser(packageName, PackageManager.MATCH_ALL, userId);
- return applicationInfo.category == ApplicationInfo.CATEGORY_GAME;
- } catch (PackageManager.NameNotFoundException e) {
- return false;
+ /**
+ * Get the GameModeInfo for the package name.
+ * Verifies that the calling process is for the matching package UID or has
+ * {@link android.Manifest.permission#MANAGE_GAME_MODE}. If the package is not a game,
+ * null is always returned.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+ @Nullable
+ public GameModeInfo getGameModeInfo(@NonNull String packageName, @UserIdInt int userId) {
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, false, true, "getGameModeInfo",
+ "com.android.server.app.GameManagerService");
+
+ // Check the caller has the necessary permission.
+ checkPermission(Manifest.permission.MANAGE_GAME_MODE);
+
+ // Restrict to games only.
+ if (!isPackageGame(packageName, userId)) {
+ return null;
}
+
+ final @GameMode int activeGameMode = getGameModeFromSettings(packageName, userId);
+ final @GameMode int[] availableGameModes = getAvailableGameModesUnchecked(packageName);
+
+ return new GameModeInfo(activeGameMode, availableGameModes);
}
/**
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
index 8996140..43c9839 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.app.TaskStackListener;
import android.content.ComponentName;
@@ -75,6 +76,13 @@
});
}
+ @Override
+ public void onTaskFocusChanged(int taskId, boolean focused) {
+ mBackgroundExecutor.execute(() -> {
+ GameServiceProviderInstanceImpl.this.onTaskFocusChanged(taskId, focused);
+ });
+ }
+
// TODO(b/204503192): Limit the lifespan of the game session in the Game Service provider
// to only when the associated task is running. Right now it is possible for a task to
// move into the background and for all associated processes to die and for the Game Session
@@ -212,6 +220,30 @@
}
}
+ private void onTaskFocusChanged(int taskId, boolean focused) {
+ synchronized (mLock) {
+ onTaskFocusChangedLocked(taskId, focused);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void onTaskFocusChangedLocked(int taskId, boolean focused) {
+ if (DEBUG) {
+ Slog.d(TAG, "onTaskFocusChangedLocked() id: " + taskId + " focused: " + focused);
+ }
+
+ final GameSessionRecord gameSessionRecord = mGameSessions.get(taskId);
+ if (gameSessionRecord == null || gameSessionRecord.getGameSession() == null) {
+ return;
+ }
+
+ try {
+ gameSessionRecord.getGameSession().onTaskFocusChanged(focused);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to notify session of task focus change: " + gameSessionRecord);
+ }
+ }
+
@GuardedBy("mLock")
private void gameTaskStartedLocked(int taskId, @NonNull ComponentName componentName) {
if (DEBUG) {
@@ -311,6 +343,12 @@
synchronized (mLock) {
attachGameSessionLocked(taskId, createGameSessionResult);
}
+
+ // The TaskStackListener may have made its task focused call for the
+ // game session's task before the game session was created, so check if
+ // the task is already focused so that the game session can be notified.
+ setGameSessionFocusedIfNecessary(taskId,
+ createGameSessionResult.getGameSession());
}, mBackgroundExecutor);
AndroidFuture<Void> unusedPostCreateGameSessionFuture =
@@ -327,6 +365,18 @@
});
}
+ private void setGameSessionFocusedIfNecessary(int taskId, IGameSession gameSession) {
+ try {
+ final ActivityTaskManager.RootTaskInfo rootTaskInfo =
+ mActivityTaskManager.getFocusedRootTaskInfo();
+ if (rootTaskInfo != null && rootTaskInfo.taskId == taskId) {
+ gameSession.onTaskFocusChanged(true);
+ }
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to set task focused for ID: " + taskId);
+ }
+ }
+
@GuardedBy("mLock")
private void attachGameSessionLocked(
int taskId,
@@ -368,7 +418,7 @@
int taskId,
CreateGameSessionResult createGameSessionResult) {
try {
- createGameSessionResult.getGameSession().destroy();
+ createGameSessionResult.getGameSession().onDestroyed();
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to destroy session: " + taskId);
}
@@ -408,7 +458,7 @@
IGameSession gameSession = gameSessionRecord.getGameSession();
if (gameSession != null) {
try {
- gameSession.destroy();
+ gameSession.onDestroyed();
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to destroy session: " + gameSessionRecord, ex);
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 0961fcb3..2dd6bf5 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -1360,6 +1360,9 @@
case AudioSystem.DEVICE_OUT_USB_HEADSET:
connType = AudioRoutesInfo.MAIN_USB;
break;
+ case AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET:
+ connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
+ break;
}
synchronized (mCurAudioRoutes) {
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 33a26ba..1e00ea9 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -36,6 +36,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.hardware.CameraSessionStats;
import android.hardware.CameraStreamStats;
import android.hardware.ICameraService;
@@ -305,6 +306,9 @@
@Override
public void onFixedRotationFinished(int displayId) { }
+
+ @Override
+ public void onKeepClearAreasChanged(int displayId, List<Rect> keepClearArea) { }
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index fd4cd8e..35e3db78 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -358,6 +358,12 @@
public float brightnessMaximum;
public float brightnessDefault;
+ /**
+ * Install orientation of display panel relative to its natural orientation.
+ */
+ @Surface.Rotation
+ public int installOrientation = Surface.ROTATION_0;
+
public void setAssumedDensityForExternalDisplay(int width, int height) {
densityDpi = Math.min(width, height) * DisplayMetrics.DENSITY_XHIGH / 1080;
// Technically, these values should be smaller than the apparent density
@@ -417,7 +423,8 @@
|| !BrightnessSynchronizer.floatEquals(brightnessMaximum, other.brightnessMaximum)
|| !BrightnessSynchronizer.floatEquals(brightnessDefault,
other.brightnessDefault)
- || !Objects.equals(roundedCorners, other.roundedCorners)) {
+ || !Objects.equals(roundedCorners, other.roundedCorners)
+ || installOrientation != other.installOrientation) {
diff |= DIFF_OTHER;
}
return diff;
@@ -461,6 +468,7 @@
brightnessMaximum = other.brightnessMaximum;
brightnessDefault = other.brightnessDefault;
roundedCorners = other.roundedCorners;
+ installOrientation = other.installOrientation;
}
// For debugging purposes
@@ -508,6 +516,7 @@
sb.append(", roundedCorners ").append(roundedCorners);
}
sb.append(flagsToString(flags));
+ sb.append(", installOrientation ").append(installOrientation);
sb.append("}");
return sb.toString();
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 84de822..3a9ef0a 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -641,6 +641,7 @@
mInfo.roundedCorners = RoundedCorners.fromResources(
res, mInfo.uniqueId, mInfo.width, mInfo.height);
+ mInfo.installOrientation = mStaticDisplayInfo.installOrientation;
if (mStaticDisplayInfo.isInternal) {
mInfo.type = Display.TYPE_INTERNAL;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 4d1367a3..e3ecf49 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -429,6 +429,7 @@
mBaseDisplayInfo.brightnessMaximum = deviceInfo.brightnessMaximum;
mBaseDisplayInfo.brightnessDefault = deviceInfo.brightnessDefault;
mBaseDisplayInfo.roundedCorners = deviceInfo.roundedCorners;
+ mBaseDisplayInfo.installOrientation = deviceInfo.installOrientation;
mPrimaryDisplayDeviceInfo = deviceInfo;
mInfo.set(null);
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 5d2a900..2a24489 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -219,7 +219,6 @@
}
private static final int MSG_SHOW_IM_SUBTYPE_PICKER = 1;
- private static final int MSG_SHOW_IM_CONFIG = 3;
private static final int MSG_HIDE_CURRENT_INPUT_METHOD = 1035;
private static final int MSG_REMOVE_IME_SURFACE = 1060;
@@ -237,8 +236,6 @@
private static final int MSG_SYSTEM_UNLOCK_USER = 5000;
private static final int MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED = 5010;
- private static final int MSG_INLINE_SUGGESTIONS_REQUEST = 6000;
-
private static final int MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE = 7000;
private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
@@ -1957,12 +1954,15 @@
IInputMethod curMethod = getCurMethodLocked();
if (userId == mSettings.getCurrentUserId() && imi != null
&& imi.isInlineSuggestionsEnabled() && curMethod != null) {
- executeOrSendMessage(curMethod,
- mCaller.obtainMessageOOO(MSG_INLINE_SUGGESTIONS_REQUEST, curMethod,
- requestInfo, new InlineSuggestionsRequestCallbackDecorator(callback,
- imi.getPackageName(), mCurTokenDisplayId,
- getCurTokenLocked(),
- this)));
+ final IInlineSuggestionsRequestCallback callbackImpl =
+ new InlineSuggestionsRequestCallbackDecorator(callback,
+ imi.getPackageName(), mCurTokenDisplayId, getCurTokenLocked(),
+ this);
+ try {
+ curMethod.onCreateInlineSuggestionsRequest(requestInfo, callbackImpl);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException calling onCreateInlineSuggestionsRequest()", e);
+ }
} else {
callback.onInlineSuggestionsUnsupported();
}
@@ -2208,16 +2208,6 @@
}
}
- // TODO(b/215609403): This method will be removed soon!
- private void executeOrSendMessage(IInputMethod target, Message msg) {
- if (target.asBinder() instanceof Binder) {
- throw new UnsupportedOperationException(
- "InputMethodService is not supported to run in the system_server");
- }
- handleMessage(msg);
- msg.recycle();
- }
-
private void executeOrSendMessage(IInputMethodClient target, Message msg) {
if (target.asBinder() instanceof Binder) {
// This is supposed to be emulating the one-way semantics when the IME client is
@@ -2347,11 +2337,20 @@
curId, getSequenceNumberLocked(), suppressesSpellChecker);
}
+ /**
+ * Called by {@link #startInputOrWindowGainedFocusInternalLocked} to bind/unbind/attach the
+ * selected InputMethod to the given focused IME client.
+ *
+ * Note that this should be called after validating if the IME client has IME focus.
+ *
+ * @see WindowManagerInternal#hasInputMethodClientFocus(IBinder, int, int, int)
+ */
@GuardedBy("ImfLock.class")
@NonNull
- InputBindResult startInputUncheckedLocked(@NonNull ClientState cs, IInputContext inputContext,
- @NonNull EditorInfo attribute, @StartInputFlags int startInputFlags,
- @StartInputReason int startInputReason, int unverifiedTargetSdkVersion) {
+ private InputBindResult startInputUncheckedLocked(@NonNull ClientState cs,
+ IInputContext inputContext, @NonNull EditorInfo attribute,
+ @StartInputFlags int startInputFlags, @StartInputReason int startInputReason,
+ int unverifiedTargetSdkVersion) {
// If no method is currently selected, do nothing.
String selectedMethodId = getSelectedMethodIdLocked();
if (selectedMethodId == null) {
@@ -2373,10 +2372,6 @@
return InputBindResult.INVALID_PACKAGE_NAME;
}
- if (!mWindowManagerInternal.isUidAllowedOnDisplay(cs.selfReportedDisplayId, cs.uid)) {
- // Wait, the client no longer has access to the display.
- return InputBindResult.INVALID_DISPLAY_ID;
- }
// Compute the final shown display ID with validated cs.selfReportedDisplayId for this
// session & other conditions.
mDisplayIdToShowIme = computeImeDisplayIdForTarget(cs.selfReportedDisplayId,
@@ -3200,14 +3195,11 @@
// be made before input is started in it.
final ClientState cs = mClients.get(client.asBinder());
if (cs == null) {
- throw new IllegalArgumentException(
- "unknown client " + client.asBinder());
+ throw new IllegalArgumentException("unknown client " + client.asBinder());
}
- if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid,
- cs.selfReportedDisplayId)) {
+ if (!isImeClientFocused(windowToken, cs)) {
if (DEBUG) {
- Slog.w(TAG,
- "Ignoring hideSoftInput of uid " + uid + ": " + client);
+ Slog.w(TAG, "Ignoring hideSoftInput of uid " + uid + ": " + client);
}
return false;
}
@@ -3277,6 +3269,12 @@
return res;
}
+ private boolean isImeClientFocused(IBinder windowToken, ClientState cs) {
+ final int imeClientFocus = mWindowManagerInternal.hasInputMethodClientFocus(
+ windowToken, cs.uid, cs.pid, cs.selfReportedDisplayId);
+ return imeClientFocus == WindowManagerInternal.ImeClientFocusResult.HAS_IME_FOCUS;
+ }
+
@NonNull
@Override
public InputBindResult startInputOrWindowGainedFocus(
@@ -3370,31 +3368,30 @@
+ " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion);
}
- final int windowDisplayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken);
-
final ClientState cs = mClients.get(client.asBinder());
if (cs == null) {
throw new IllegalArgumentException("unknown client " + client.asBinder());
}
- if (cs.selfReportedDisplayId != windowDisplayId) {
- Slog.e(TAG, "startInputOrWindowGainedFocusInternal: display ID mismatch."
- + " from client:" + cs.selfReportedDisplayId
- + " from window:" + windowDisplayId);
- return InputBindResult.DISPLAY_ID_MISMATCH;
- }
- if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid,
- cs.selfReportedDisplayId)) {
- // Check with the window manager to make sure this client actually
- // has a window with focus. If not, reject. This is thread safe
- // because if the focus changes some time before or after, the
- // next client receiving focus that has any interest in input will
- // be calling through here after that change happens.
- if (DEBUG) {
- Slog.w(TAG, "Focus gain on non-focused client " + cs.client
- + " (uid=" + cs.uid + " pid=" + cs.pid + ")");
- }
- return InputBindResult.NOT_IME_TARGET_WINDOW;
+ final int imeClientFocus = mWindowManagerInternal.hasInputMethodClientFocus(
+ windowToken, cs.uid, cs.pid, cs.selfReportedDisplayId);
+ switch (imeClientFocus) {
+ case WindowManagerInternal.ImeClientFocusResult.DISPLAY_ID_MISMATCH:
+ Slog.e(TAG, "startInputOrWindowGainedFocusInternal: display ID mismatch.");
+ return InputBindResult.DISPLAY_ID_MISMATCH;
+ case WindowManagerInternal.ImeClientFocusResult.NOT_IME_TARGET_WINDOW:
+ // Check with the window manager to make sure this client actually
+ // has a window with focus. If not, reject. This is thread safe
+ // because if the focus changes some time before or after, the
+ // next client receiving focus that has any interest in input will
+ // be calling through here after that change happens.
+ if (DEBUG) {
+ Slog.w(TAG, "Focus gain on non-focused client " + cs.client
+ + " (uid=" + cs.uid + " pid=" + cs.pid + ")");
+ }
+ return InputBindResult.NOT_IME_TARGET_WINDOW;
+ case WindowManagerInternal.ImeClientFocusResult.INVALID_DISPLAY_ID:
+ return InputBindResult.INVALID_DISPLAY_ID;
}
if (mUserSwitchHandlerTask != null) {
@@ -3622,8 +3619,7 @@
if (cs == null) {
throw new IllegalArgumentException("unknown client " + client.asBinder());
}
- if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid,
- cs.selfReportedDisplayId)) {
+ if (!isImeClientFocused(mCurFocusedWindow, cs)) {
Slog.w(TAG, String.format("Ignoring %s of uid %d : %s", methodName, uid, client));
return false;
}
@@ -4264,10 +4260,6 @@
mMenuController.showInputMethodMenu(showAuxSubtypes, displayId);
return true;
- case MSG_SHOW_IM_CONFIG:
- showConfigureInputMethods();
- return true;
-
// ---------------------------------------------------------
case MSG_HIDE_CURRENT_INPUT_METHOD:
@@ -4381,23 +4373,6 @@
}
// ---------------------------------------------------------------
- case MSG_INLINE_SUGGESTIONS_REQUEST: {
- args = (SomeArgs) msg.obj;
- final InlineSuggestionsRequestInfo requestInfo =
- (InlineSuggestionsRequestInfo) args.arg2;
- final IInlineSuggestionsRequestCallback callback =
- (IInlineSuggestionsRequestCallback) args.arg3;
- try {
- ((IInputMethod) args.arg1).onCreateInlineSuggestionsRequest(requestInfo,
- callback);
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException calling onCreateInlineSuggestionsRequest(): " + e);
- }
- args.recycle();
- return true;
- }
-
- // ---------------------------------------------------------------
case MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE: {
if (mAudioManagerInternal == null) {
mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
@@ -4685,14 +4660,6 @@
mContext.startActivityAsUser(intent, null, UserHandle.of(userId));
}
- private void showConfigureInputMethods() {
- Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
- | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- mContext.startActivityAsUser(intent, null, UserHandle.CURRENT);
- }
-
// ----------------------------------------------------------------------
/**
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 682a27a..8f05130 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -20,6 +20,9 @@
import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.READ_CONTACTS;
import static android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_DETAIL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_TITLE;
import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.UserHandle.USER_ALL;
@@ -117,6 +120,7 @@
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
@@ -642,12 +646,9 @@
private void showEncryptionNotificationForProfile(UserHandle user) {
Resources r = mContext.getResources();
- CharSequence title = r.getText(
- com.android.internal.R.string.profile_encrypted_title);
- CharSequence message = r.getText(
- com.android.internal.R.string.profile_encrypted_message);
- CharSequence detail = r.getText(
- com.android.internal.R.string.profile_encrypted_detail);
+ CharSequence title = getEncryptionNotificationTitle();
+ CharSequence message = getEncryptionNotificationMessage();
+ CharSequence detail = getEncryptionNotificationDetail();
final KeyguardManager km = (KeyguardManager) mContext.getSystemService(KEYGUARD_SERVICE);
final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null,
@@ -663,6 +664,24 @@
showEncryptionNotification(user, title, message, detail, intent);
}
+ private String getEncryptionNotificationTitle() {
+ return mInjector.getDevicePolicyManager().getString(
+ PROFILE_ENCRYPTED_TITLE,
+ () -> mContext.getString(R.string.profile_encrypted_title));
+ }
+
+ private String getEncryptionNotificationDetail() {
+ return mInjector.getDevicePolicyManager().getString(
+ PROFILE_ENCRYPTED_DETAIL,
+ () -> mContext.getString(R.string.profile_encrypted_detail));
+ }
+
+ private String getEncryptionNotificationMessage() {
+ return mInjector.getDevicePolicyManager().getString(
+ PROFILE_ENCRYPTED_MESSAGE,
+ () -> mContext.getString(R.string.profile_encrypted_message));
+ }
+
private void showEncryptionNotification(UserHandle user, CharSequence title,
CharSequence message, CharSequence detail, PendingIntent intent) {
if (DEBUG) Slog.v(TAG, "showing encryption notification, user: " + user.getIdentifier());
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index a8383b6..e555c13 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -63,7 +63,6 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.net.NetworkIdentity.OEM_NONE;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.SNOOZE_NEVER;
import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -1498,13 +1497,11 @@
for (int i = 0; i < mSubIdToSubscriberId.size(); i++) {
final int subId = mSubIdToSubscriberId.keyAt(i);
final String subscriberId = mSubIdToSubscriberId.valueAt(i);
- final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
- true, OEM_NONE);
- /* While OEM_NONE indicates "any non OEM managed network", OEM_NONE is meant to be a
- * placeholder value here. The probeIdent is matched against a NetworkTemplate which
- * should have its OEM managed value set to OEM_MANAGED_ALL, which will cause the
- * template to match probeIdent without regard to OEM managed status. */
+ final NetworkIdentity probeIdent = new NetworkIdentity.Builder()
+ .setType(TYPE_MOBILE)
+ .setSubscriberId(subscriberId)
+ .setMetered(true)
+ .setDefaultNetwork(true).build();
if (template.matches(probeIdent)) {
return subId;
}
@@ -1737,9 +1734,11 @@
// find and update the carrier NetworkPolicy for this subscriber id
boolean policyUpdated = false;
- final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true,
- OEM_NONE);
+ final NetworkIdentity probeIdent = new NetworkIdentity.Builder()
+ .setType(TYPE_MOBILE)
+ .setSubscriberId(subscriberId)
+ .setMetered(true)
+ .setDefaultNetwork(true).build();
for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
final NetworkTemplate template = mNetworkPolicy.keyAt(i);
if (template.matches(probeIdent)) {
@@ -1967,10 +1966,11 @@
for (int i = 0; i < mSubIdToSubscriberId.size(); i++) {
final int subId = mSubIdToSubscriberId.keyAt(i);
final String subscriberId = mSubIdToSubscriberId.valueAt(i);
-
- final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
- true, OEM_NONE);
+ final NetworkIdentity probeIdent = new NetworkIdentity.Builder()
+ .setType(TYPE_MOBILE)
+ .setSubscriberId(subscriberId)
+ .setMetered(true)
+ .setDefaultNetwork(true).build();
// Template is matched when subscriber id matches.
if (template.matches(probeIdent)) {
matchingSubIds.add(subId);
@@ -2074,11 +2074,9 @@
for (final NetworkStateSnapshot snapshot : snapshots) {
mNetIdToSubId.put(snapshot.getNetwork().getNetId(), parseSubId(snapshot));
- // Policies matched by NPMS only match by subscriber ID or by network ID. Thus subtype
- // in the object created here is never used and its value doesn't matter, so use
- // NETWORK_TYPE_UNKNOWN.
- final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, snapshot,
- true, TelephonyManager.NETWORK_TYPE_UNKNOWN /* subType */);
+ // Policies matched by NPMS only match by subscriber ID or by network ID.
+ final NetworkIdentity ident = new NetworkIdentity.Builder()
+ .setNetworkStateSnapshot(snapshot).setDefaultNetwork(true).build();
identified.put(snapshot, ident);
}
@@ -2275,9 +2273,11 @@
@GuardedBy("mNetworkPoliciesSecondLock")
private boolean ensureActiveCarrierPolicyAL(int subId, String subscriberId) {
// Poke around to see if we already have a policy
- final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true,
- OEM_NONE);
+ final NetworkIdentity probeIdent = new NetworkIdentity.Builder()
+ .setType(TYPE_MOBILE)
+ .setSubscriberId(subscriberId)
+ .setMetered(true)
+ .setDefaultNetwork(true).build();
for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
final NetworkTemplate template = mNetworkPolicy.keyAt(i);
if (template.matches(probeIdent)) {
@@ -2687,7 +2687,7 @@
final List<WifiConfiguration> configs = wm.getConfiguredNetworks();
for (int i = 0; i < configs.size(); ++i) {
final WifiConfiguration config = configs.get(i);
- for (String key : config.getAllPersistableNetworkKeys()) {
+ for (String key : config.getAllNetworkKeys()) {
final Boolean metered = wifiNetworkKeys.get(key);
if (metered != null) {
Slog.d(TAG, "Found network " + key + "; upgrading metered hint");
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 1f10d77..ccc375f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -16,6 +16,8 @@
package com.android.server.pm;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_DELETED_BY_DO;
+
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -29,6 +31,7 @@
import android.app.NotificationManager;
import android.app.PackageDeleteObserver;
import android.app.admin.DevicePolicyEventLogger;
+import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.Context;
import android.content.Intent;
@@ -1312,7 +1315,7 @@
mPackageName = packageName;
if (showNotification) {
mNotification = buildSuccessNotification(mContext,
- mContext.getResources().getString(R.string.package_deleted_device_owner),
+ getDeviceOwnerDeletedPackageMsg(),
packageName,
userId);
} else {
@@ -1320,6 +1323,12 @@
}
}
+ private String getDeviceOwnerDeletedPackageMsg() {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(PACKAGE_DELETED_BY_DO,
+ () -> mContext.getString(R.string.package_updated_device_owner));
+ }
+
@Override
public void onUserActionRequired(Intent intent) {
if (mTarget == null) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index e0f1b0b..d9ade96 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -17,6 +17,8 @@
package com.android.server.pm;
import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_INSTALLED_BY_DO;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_UPDATED_BY_DO;
import static android.content.pm.DataLoaderType.INCREMENTAL;
import static android.content.pm.DataLoaderType.STREAMING;
import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
@@ -56,6 +58,7 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.admin.DevicePolicyEventLogger;
+import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.ComponentName;
import android.content.Context;
@@ -91,7 +94,6 @@
import android.content.pm.parsing.ApkLite;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.graphics.Bitmap;
@@ -156,6 +158,7 @@
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import libcore.io.IoUtils;
import libcore.util.EmptyArray;
@@ -4336,9 +4339,7 @@
if (INSTALL_SUCCEEDED == returnCode && showNotification) {
boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);
Notification notification = PackageInstallerService.buildSuccessNotification(context,
- context.getResources()
- .getString(update ? R.string.package_updated_device_owner :
- R.string.package_installed_device_owner),
+ getDeviceOwnerInstalledPackageMsg(context, update),
basePackageName,
userId);
if (notification != null) {
@@ -4370,6 +4371,15 @@
}
}
+ private static String getDeviceOwnerInstalledPackageMsg(Context context, boolean update) {
+ DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ return update
+ ? dpm.getString(PACKAGE_UPDATED_BY_DO,
+ () -> context.getString(R.string.package_updated_device_owner))
+ : dpm.getString(PACKAGE_INSTALLED_BY_DO,
+ () -> context.getString(R.string.package_installed_device_owner));
+ }
+
/**
* This method doesn't change internal states and is safe to call outside the lock.
*/
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 733b327..e00f4f5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -950,6 +950,7 @@
final @Nullable String mRetailDemoPackage;
final @Nullable String mOverlayConfigSignaturePackage;
final @Nullable String mRecentsPackage;
+ final @Nullable String mAmbientContextDetectionPackage;
@GuardedBy("mLock")
private final PackageUsage mPackageUsage = new PackageUsage();
@@ -1668,6 +1669,7 @@
mSystemTextClassifierPackageName = testParams.systemTextClassifierPackage;
mRetailDemoPackage = testParams.retailDemoPackage;
mRecentsPackage = testParams.recentsPackage;
+ mAmbientContextDetectionPackage = testParams.ambientContextDetectionPackage;
mConfiguratorPackage = testParams.configuratorPackage;
mAppPredictionServicePackage = testParams.appPredictionServicePackage;
mIncidentReportApproverPackage = testParams.incidentReportApproverPackage;
@@ -1995,6 +1997,7 @@
mRetailDemoPackage = getRetailDemoPackageName();
mOverlayConfigSignaturePackage = getOverlayConfigSignaturePackageName();
mRecentsPackage = getRecentsPackageName();
+ mAmbientContextDetectionPackage = getAmbientContextDetectionPackageName();
// Now that we know all of the shared libraries, update all clients to have
// the correct library paths.
@@ -5618,6 +5621,11 @@
return mPmInternal.getSetupWizardPackageName();
}
+ public @Nullable String getAmbientContextDetectionPackageName() {
+ return ensureSystemPackageName(getPackageFromComponentString(
+ R.string.config_defaultAmbientContextDetectionService));
+ }
+
public String getIncidentReportApproverPackageName() {
return ensureSystemPackageName(mContext.getString(
R.string.config_incidentReportApproverPackage));
@@ -8693,6 +8701,8 @@
return mComputer.filterOnlySystemPackages(mConfiguratorPackage);
case PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER:
return mComputer.filterOnlySystemPackages(mIncidentReportApproverPackage);
+ case PackageManagerInternal.PACKAGE_AMBIENT_CONTEXT_DETECTION:
+ return mComputer.filterOnlySystemPackages(mAmbientContextDetectionPackage);
case PackageManagerInternal.PACKAGE_APP_PREDICTOR:
return mComputer.filterOnlySystemPackages(mAppPredictionServicePackage);
case PackageManagerInternal.PACKAGE_COMPANION:
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index 0d6555c..db60686 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -91,6 +91,7 @@
public ViewCompiler viewCompiler;
public @Nullable String retailDemoPackage;
public @Nullable String recentsPackage;
+ public @Nullable String ambientContextDetectionPackage;
public ComponentName resolveComponentName;
public ArrayMap<String, AndroidPackage> packages;
public boolean enableFreeCacheV2;
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index d29dbbc..d6e88f4 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3675,9 +3675,18 @@
@UserInfoFlag int flags, @UserIdInt int parentId,
@Nullable String[] disallowedPackages)
throws UserManager.CheckedUserOperationException {
- String restriction = (UserManager.isUserTypeManagedProfile(userType))
- ? UserManager.DISALLOW_ADD_MANAGED_PROFILE
- : UserManager.DISALLOW_ADD_USER;
+
+ // Checking user restriction before creating new user,
+ // default check is for DISALLOW_ADD_USER
+ // If new user is of type CLONE, check if creation of clone profile is allowed
+ // If new user is of type MANAGED, check if creation of managed profile is allowed
+ String restriction = UserManager.DISALLOW_ADD_USER;
+ if (UserManager.isUserTypeCloneProfile(userType)) {
+ restriction = UserManager.DISALLOW_ADD_CLONE_PROFILE;
+ } else if (UserManager.isUserTypeManagedProfile(userType)) {
+ restriction = UserManager.DISALLOW_ADD_MANAGED_PROFILE;
+ }
+
enforceUserRestriction(restriction, UserHandle.getCallingUserId(),
"Cannot add user");
return createUserInternalUnchecked(name, userType, flags, parentId,
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 0f3b4bc..1fa9013 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -101,6 +101,7 @@
UserManager.DISALLOW_FACTORY_RESET,
UserManager.DISALLOW_ADD_USER,
UserManager.DISALLOW_ADD_MANAGED_PROFILE,
+ UserManager.DISALLOW_ADD_CLONE_PROFILE,
UserManager.ENSURE_VERIFY_APPS,
UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
index 35cc43f..b3649a7 100644
--- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
@@ -34,6 +34,7 @@
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvTrackInfo;
+import android.media.tv.interactive.AppLinkInfo;
import android.media.tv.interactive.ITvInteractiveAppClient;
import android.media.tv.interactive.ITvInteractiveAppManager;
import android.media.tv.interactive.ITvInteractiveAppManagerCallback;
@@ -95,6 +96,10 @@
@GuardedBy("mLock")
private final SparseArray<UserState> mUserStates = new SparseArray<>();
+ // TODO: remove mGetServiceListCalled if onBootPhrase work correctly
+ @GuardedBy("mLock")
+ private boolean mGetServiceListCalled = false;
+
private final UserManager mUserManager;
/**
@@ -638,6 +643,10 @@
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
+ if (!mGetServiceListCalled) {
+ buildTvInteractiveAppServiceListLocked(userId, null);
+ mGetServiceListCalled = true;
+ }
UserState userState = getOrCreateUserStateLocked(resolvedUserId);
List<TvInteractiveAppInfo> iAppList = new ArrayList<>();
for (TvInteractiveAppState state : userState.mIAppMap.values()) {
@@ -687,9 +696,9 @@
}
@Override
- public void registerAppLinkInfo(String tiasId, Bundle appLinkInfo, int userId) {
+ public void registerAppLinkInfo(String tiasId, AppLinkInfo appLinkInfo, int userId) {
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
- Binder.getCallingUid(), userId, "registerAppLinkInfo");
+ Binder.getCallingUid(), userId, "registerAppLinkInfo: " + appLinkInfo);
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
@@ -723,9 +732,9 @@
}
@Override
- public void unregisterAppLinkInfo(String tiasId, Bundle appLinkInfo, int userId) {
+ public void unregisterAppLinkInfo(String tiasId, AppLinkInfo appLinkInfo, int userId) {
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
- Binder.getCallingUid(), userId, "unregisterAppLinkInfo");
+ Binder.getCallingUid(), userId, "unregisterAppLinkInfo: " + appLinkInfo);
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
@@ -1811,7 +1820,7 @@
private final ServiceConnection mConnection;
private final ComponentName mComponent;
private final String mIAppServiceId;
- private final List<Pair<Bundle, Boolean>> mPendingAppLinkInfo = new ArrayList<>();
+ private final List<Pair<AppLinkInfo, Boolean>> mPendingAppLinkInfo = new ArrayList<>();
private final List<Bundle> mPendingAppLinkCommand = new ArrayList<>();
private boolean mPendingPrepare = false;
@@ -1834,7 +1843,7 @@
mIAppServiceId = tias;
}
- private void addPendingAppLink(Bundle info, boolean register) {
+ private void addPendingAppLink(AppLinkInfo info, boolean register) {
mPendingAppLinkInfo.add(Pair.create(info, register));
}
@@ -1891,10 +1900,10 @@
}
if (!serviceState.mPendingAppLinkInfo.isEmpty()) {
- for (Iterator<Pair<Bundle, Boolean>> it =
+ for (Iterator<Pair<AppLinkInfo, Boolean>> it =
serviceState.mPendingAppLinkInfo.iterator();
it.hasNext(); ) {
- Pair<Bundle, Boolean> appLinkInfoPair = it.next();
+ Pair<AppLinkInfo, Boolean> appLinkInfoPair = it.next();
final long identity = Binder.clearCallingIdentity();
try {
if (appLinkInfoPair.second) {
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index 3442704..6aa06e8 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -104,7 +104,8 @@
import java.util.Objects;
/** Manages uri grants. */
-public class UriGrantsManagerService extends IUriGrantsManager.Stub {
+public class UriGrantsManagerService extends IUriGrantsManager.Stub implements
+ UriMetricsHelper.PersistentUriGrantsProvider {
private static final boolean DEBUG = false;
private static final String TAG = "UriGrantsManagerService";
// Maximum number of persisted Uri grants a package is allowed
@@ -115,6 +116,7 @@
private final H mH;
ActivityManagerInternal mAmInternal;
PackageManagerInternal mPmInternal;
+ UriMetricsHelper mMetricsHelper;
/** File storing persisted {@link #mGrantedUriPermissions}. */
private final AtomicFile mGrantFile;
@@ -168,16 +170,19 @@
}
public static final class Lifecycle extends SystemService {
+ private final Context mContext;
private final UriGrantsManagerService mService;
public Lifecycle(Context context) {
super(context);
+ mContext = context;
mService = new UriGrantsManagerService();
}
@Override
public void onStart() {
publishBinderService(Context.URI_GRANTS_SERVICE, mService);
+ mService.mMetricsHelper = new UriMetricsHelper(mContext, mService);
mService.start();
}
@@ -186,6 +191,7 @@
if (phase == PHASE_SYSTEM_SERVICES_READY) {
mService.mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
mService.mPmInternal = LocalServices.getService(PackageManagerInternal.class);
+ mService.mMetricsHelper.registerPuller();
}
}
@@ -1298,20 +1304,50 @@
return false;
}
+ @Override
+ public ArrayList<UriPermission> providePersistentUriGrants() {
+ final ArrayList<UriPermission> result = new ArrayList<>();
+
+ synchronized (mLock) {
+ final int size = mGrantedUriPermissions.size();
+ for (int i = 0; i < size; i++) {
+ final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
+
+ final int permissionsForPackageSize = perms.size();
+ for (int j = 0; j < permissionsForPackageSize; j++) {
+ final UriPermission permission = perms.valueAt(j);
+
+ if (permission.persistedModeFlags != 0) {
+ result.add(permission);
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
private void writeGrantedUriPermissions() {
if (DEBUG) Slog.v(TAG, "writeGrantedUriPermissions()");
final long startTime = SystemClock.uptimeMillis();
+ int persistentUriPermissionsCount = 0;
+
// Snapshot permissions so we can persist without lock
ArrayList<UriPermission.Snapshot> persist = Lists.newArrayList();
synchronized (mLock) {
final int size = mGrantedUriPermissions.size();
for (int i = 0; i < size; i++) {
final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
- for (UriPermission perm : perms.values()) {
- if (perm.persistedModeFlags != 0) {
- persist.add(perm.snapshot());
+
+ final int permissionsForPackageSize = perms.size();
+ for (int j = 0; j < permissionsForPackageSize; j++) {
+ final UriPermission permission = perms.valueAt(j);
+
+ if (permission.persistedModeFlags != 0) {
+ persistentUriPermissionsCount++;
+ persist.add(permission.snapshot());
}
}
}
@@ -1345,6 +1381,8 @@
mGrantFile.failWrite(fos);
}
}
+
+ mMetricsHelper.reportPersistentUriFlushed(persistentUriPermissionsCount);
}
final class H extends Handler {
diff --git a/services/core/java/com/android/server/uri/UriMetricsHelper.java b/services/core/java/com/android/server/uri/UriMetricsHelper.java
new file mode 100644
index 0000000..dbc9599
--- /dev/null
+++ b/services/core/java/com/android/server/uri/UriMetricsHelper.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 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.uri;
+
+import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
+
+import android.app.StatsManager;
+import android.content.Context;
+import android.util.SparseArray;
+import android.util.StatsEvent;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+final class UriMetricsHelper {
+
+ private static final StatsManager.PullAtomMetadata DAILY_PULL_METADATA =
+ new StatsManager.PullAtomMetadata.Builder()
+ .setCoolDownMillis(TimeUnit.DAYS.toMillis(1))
+ .build();
+
+
+ private final Context mContext;
+ private final PersistentUriGrantsProvider mPersistentUriGrantsProvider;
+
+ UriMetricsHelper(Context context, PersistentUriGrantsProvider provider) {
+ mContext = context;
+ mPersistentUriGrantsProvider = provider;
+ }
+
+ void registerPuller() {
+ final StatsManager statsManager = mContext.getSystemService(StatsManager.class);
+ statsManager.setPullAtomCallback(
+ FrameworkStatsLog.PERSISTENT_URI_PERMISSIONS_AMOUNT_PER_PACKAGE,
+ DAILY_PULL_METADATA,
+ DIRECT_EXECUTOR,
+ (atomTag, data) -> {
+ reportPersistentUriPermissionsPerPackage(data);
+ return StatsManager.PULL_SUCCESS;
+ });
+ }
+
+ void reportPersistentUriFlushed(int amount) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.PERSISTENT_URI_PERMISSIONS_FLUSHED,
+ amount
+ );
+ }
+
+ private void reportPersistentUriPermissionsPerPackage(List<StatsEvent> data) {
+ final ArrayList<UriPermission> persistentUriGrants =
+ mPersistentUriGrantsProvider.providePersistentUriGrants();
+
+ final SparseArray<Integer> perUidCount = new SparseArray<>();
+
+ final int persistentUriGrantsSize = persistentUriGrants.size();
+ for (int i = 0; i < persistentUriGrantsSize; i++) {
+ final UriPermission uriPermission = persistentUriGrants.get(i);
+
+ perUidCount.put(
+ uriPermission.targetUid,
+ perUidCount.get(uriPermission.targetUid, 0) + 1
+ );
+ }
+
+ final int perUidCountSize = perUidCount.size();
+ for (int i = 0; i < perUidCountSize; i++) {
+ final int uid = perUidCount.keyAt(i);
+ final int amount = perUidCount.valueAt(i);
+
+ data.add(
+ FrameworkStatsLog.buildStatsEvent(
+ FrameworkStatsLog.PERSISTENT_URI_PERMISSIONS_AMOUNT_PER_PACKAGE,
+ uid,
+ amount
+ )
+ );
+ }
+ }
+
+ interface PersistentUriGrantsProvider {
+ ArrayList<UriPermission> providePersistentUriGrants();
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 6c5d952..eafd9d7 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -179,7 +179,7 @@
try {
ActivityManager.getService().registerUidObserver(mUidObserver,
ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
- ActivityManager.PROCESS_STATE_UNKNOWN, null);
+ ActivityManager.PROCESS_STATE_UNKNOWN, mContext.getOpPackageName());
} catch (RemoteException e) {
// ignored; both services live in system_server
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 86ef8d2..76434c7 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -7364,12 +7364,17 @@
@VisibleForTesting
void clearSizeCompatMode() {
+ final float lastSizeCompatScale = mSizeCompatScale;
mInSizeCompatModeForBounds = false;
mSizeCompatScale = 1f;
mSizeCompatBounds = null;
mCompatDisplayInsets = null;
+ if (mSizeCompatScale != lastSizeCompatScale) {
+ forAllWindows(WindowState::updateGlobalScale, false /* traverseTopToBottom */);
+ }
- onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
+ // Clear config override in #updateCompatDisplayInsets().
+ onRequestedOverrideConfigurationChanged(EMPTY);
}
@Override
@@ -7924,6 +7929,7 @@
final int contentH = resolvedAppBounds.height();
final int viewportW = containerAppBounds.width();
final int viewportH = containerAppBounds.height();
+ final float lastSizeCompatScale = mSizeCompatScale;
// Only allow to scale down.
mSizeCompatScale = (contentW <= viewportW && contentH <= viewportH)
? 1f : Math.min((float) viewportW / contentW, (float) viewportH / contentH);
@@ -7942,6 +7948,9 @@
} else {
mSizeCompatBounds = null;
}
+ if (mSizeCompatScale != lastSizeCompatScale) {
+ forAllWindows(WindowState::updateGlobalScale, false /* traverseTopToBottom */);
+ }
// Vertically center within parent (bounds) - this is a UX choice and exclude the horizontal
// decor if needed. Horizontal position is adjusted in
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c65ca08..e449dde 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -122,6 +122,7 @@
import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_TARGET;
import static com.android.server.wm.DisplayContentProto.INSETS_SOURCE_PROVIDERS;
import static com.android.server.wm.DisplayContentProto.IS_SLEEPING;
+import static com.android.server.wm.DisplayContentProto.KEEP_CLEAR_AREAS;
import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
@@ -169,6 +170,7 @@
import android.graphics.Bitmap;
import android.graphics.ColorSpace;
import android.graphics.Insets;
+import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
@@ -838,7 +840,6 @@
}
w.mSurfacePlacementNeeded = true;
w.mLayoutNeeded = false;
- w.prelayout();
final boolean firstLayout = !w.isLaidOut();
getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
w.mLayoutSeq = mLayoutSeq;
@@ -881,7 +882,6 @@
}
w.mSurfacePlacementNeeded = true;
w.mLayoutNeeded = false;
- w.prelayout();
getDisplayPolicy().layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
w.mLayoutSeq = mLayoutSeq;
if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrame()
@@ -2001,6 +2001,7 @@
}
void configureDisplayPolicy() {
+ mRootWindowContainer.updateDisplayImePolicyCache();
mDisplayPolicy.updateConfigurationAndScreenSizeDependentBehaviors();
mDisplayRotation.configure(mBaseDisplayWidth, mBaseDisplayHeight);
}
@@ -2718,6 +2719,7 @@
void onDisplayChanged(DisplayContent dc) {
super.onDisplayChanged(dc);
updateSystemGestureExclusionLimit();
+ updateKeepClearAreas();
}
void updateSystemGestureExclusionLimit() {
@@ -3327,6 +3329,9 @@
}
}
proto.write(IME_POLICY, getImePolicy());
+ for (Rect r : getKeepClearAreas()) {
+ r.dumpDebug(proto, KEEP_CLEAR_AREAS);
+ }
proto.end(token);
}
@@ -3386,6 +3391,13 @@
pw.println(mSystemGestureExclusion);
}
+ final List<Rect> keepClearAreas = getKeepClearAreas();
+ if (!keepClearAreas.isEmpty()) {
+ pw.println();
+ pw.print(" keepClearAreas=");
+ pw.println(keepClearAreas);
+ }
+
pw.println();
pw.println(prefix + "Display areas in top down Z order:");
dumpChildDisplayArea(pw, subPrefix, dumpAll);
@@ -3613,6 +3625,7 @@
}
adjustForImeIfNeeded();
+ updateKeepClearAreas();
// We may need to schedule some toast windows to be removed. The toasts for an app that
// does not have input focus are removed within a timeout to prevent apps to redress
@@ -3939,6 +3952,9 @@
}
}
+ // IMPORTANT: When introducing new dependencies in this method, make sure that
+ // changes to those result in RootWindowContainer.updateDisplayImePolicyCache()
+ // being called.
@DisplayImePolicy int getImePolicy() {
if (!isTrusted()) {
return DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
@@ -3987,11 +4003,12 @@
if (target == mImeLayeringTarget) {
return;
}
- // Prepare the IME screenshot for the last IME target when its task is applying app
- // transition. This is for the better IME transition to keep IME visibility when
- // transitioning to the next task.
+ // If the IME target is the input target, before it changes, prepare the IME screenshot
+ // for the last IME target when its task is applying app transition. This is for the
+ // better IME transition to keep IME visibility when transitioning to the next task.
if (mImeLayeringTarget != null && mImeLayeringTarget.isAnimating(PARENTS | TRANSITION,
- ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)) {
+ ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)
+ && mImeLayeringTarget == mImeInputTarget) {
attachAndShowImeScreenshotOnTarget();
}
@@ -5477,19 +5494,28 @@
mSystemGestureExclusionListeners.unregister(listener);
}
+ void updateKeepClearAreas() {
+ mWmService.mDisplayNotificationController.dispatchKeepClearAreasChanged(
+ this, getKeepClearAreas());
+ }
+
/**
- * @see IWindowManager#setForwardedInsets
+ * Returns all keep-clear areas from visible windows on this display.
*/
- public void setForwardedInsets(Insets insets) {
- if (insets == null) {
- insets = Insets.NONE;
- }
- if (mDisplayPolicy.getForwardedInsets().equals(insets)) {
- return;
- }
- mDisplayPolicy.setForwardedInsets(insets);
- setLayoutNeeded();
- mWmService.mWindowPlacerLocked.requestTraversal();
+ ArrayList<Rect> getKeepClearAreas() {
+ final ArrayList<Rect> keepClearAreas = new ArrayList<Rect>();
+ final Matrix tmpMatrix = new Matrix();
+ final float[] tmpFloat9 = new float[9];
+ forAllWindows(w -> {
+ if (w.isVisible() && !w.inPinnedWindowingMode()) {
+ keepClearAreas.addAll(w.getKeepClearAreas(tmpMatrix, tmpFloat9));
+ }
+
+ // We stop traversing when we reach the base of a fullscreen app.
+ return w.getWindowType() == TYPE_BASE_APPLICATION
+ && w.getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
+ }, true);
+ return keepClearAreas;
}
protected MetricsLogger getMetricsLogger() {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 0745b3b..ab1e349 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -359,16 +359,6 @@
private int mDisplayCutoutTouchableRegionSize;
- /**
- * The area covered by system windows which belong to another display. Forwarded insets is set
- * in case this is a virtual display, this is displayed on another display that has insets, and
- * the bounds of this display is overlapping with the insets of the host display (e.g. IME is
- * displayed on the host display, and it covers a part of this virtual display.)
- * The forwarded insets is used to compute display frames of this virtual display, which will
- * be then used to layout windows in the virtual display.
- */
- @NonNull private Insets mForwardedInsets = Insets.NONE;
-
private RefreshRatePolicy mRefreshRatePolicy;
/**
@@ -1442,33 +1432,6 @@
return mForceShowSystemBars;
}
- // TODO: Should probably be moved into DisplayFrames.
- /**
- * Return the layout hints for a newly added window. These values are computed on the
- * most recent layout, so they are not guaranteed to be correct.
- *
- * @param attrs The LayoutParams of the window.
- * @param windowToken The token of the window.
- * @param outInsetsState The insets state of this display from the client's perspective.
- * @param localClient Whether the client is from the our process.
- * @return Whether to always consume the system bars.
- * See {@link #areSystemBarsForcedShownLw()}.
- */
- boolean getLayoutHint(LayoutParams attrs, WindowToken windowToken, InsetsState outInsetsState,
- boolean localClient) {
- final InsetsState state =
- mDisplayContent.getInsetsPolicy().getInsetsForWindowMetrics(attrs);
- final boolean hasCompatScale = WindowState.hasCompatScale(attrs, windowToken);
- outInsetsState.set(state, hasCompatScale || localClient);
- if (hasCompatScale) {
- final float compatScale = windowToken != null
- ? windowToken.getSizeCompatScale()
- : mDisplayContent.mCompatibleScreenScale;
- outInsetsState.scale(1f / compatScale);
- }
- return mForceShowSystemBars;
- }
-
/**
* Computes the frames of display (its logical size, rotation and cutout should already be set)
* used to layout window. This method only changes the given display frames, insets state and
@@ -2149,18 +2112,6 @@
}
}
- /**
- * @see IWindowManager#setForwardedInsets
- */
- public void setForwardedInsets(@NonNull Insets forwardedInsets) {
- mForwardedInsets = forwardedInsets;
- }
-
- @NonNull
- public Insets getForwardedInsets() {
- return mForwardedInsets;
- }
-
@NavigationBarPosition
int navigationBarPosition(int displayRotation) {
if (mNavigationBar != null) {
diff --git a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
index 4141090..276dbe9 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
@@ -17,11 +17,14 @@
package com.android.server.wm;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.IntArray;
import android.view.IDisplayWindowListener;
+import java.util.List;
+
/**
* Manages dispatch of relevant hierarchy changes to interested listeners. Listeners are assumed
* to be remote.
@@ -116,4 +119,16 @@
}
mDisplayListeners.finishBroadcast();
}
+
+ void dispatchKeepClearAreasChanged(DisplayContent display, List<Rect> keepClearAreas) {
+ int count = mDisplayListeners.beginBroadcast();
+ for (int i = 0; i < count; ++i) {
+ try {
+ mDisplayListeners.getBroadcastItem(i).onKeepClearAreasChanged(
+ display.mDisplayId, keepClearAreas);
+ } catch (RemoteException e) {
+ }
+ }
+ mDisplayListeners.finishBroadcast();
+ }
}
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index 963f326..8d3e071 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -97,7 +97,7 @@
// activities are actually behind other fullscreen activities, but still required
// to be visible (such as performing Recents animation).
final boolean resumeTopActivity = mTop != null && !mTop.mLaunchTaskBehind
- && mTaskFragment.isTopActivityFocusable()
+ && mTaskFragment.canBeResumed(starting)
&& (starting == null || !starting.isDescendantOf(mTaskFragment));
ArrayList<TaskFragment> adjacentTaskFragments = null;
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 1a1101e..a1468cc 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -366,6 +366,7 @@
if (changed) {
notifyInsetsChanged();
mDisplayContent.updateSystemGestureExclusion();
+ mDisplayContent.updateKeepClearAreas();
mDisplayContent.getDisplayPolicy().updateSystemBarAttributes();
}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 5a420ca..d031bec 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -165,6 +165,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -986,6 +987,7 @@
forAllDisplays(dc -> {
dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
dc.updateSystemGestureExclusion();
+ dc.updateKeepClearAreas();
dc.updateTouchExcludeRegion();
});
@@ -2530,9 +2532,16 @@
// Drop any cached DisplayInfos associated with this display id - the values are now
// out of date given this display changed event.
mWmService.mPossibleDisplayInfoMapper.removePossibleDisplayInfos(displayId);
+ updateDisplayImePolicyCache();
}
}
+ void updateDisplayImePolicyCache() {
+ ArrayMap<Integer, Integer> displayImePolicyMap = new ArrayMap<>();
+ forAllDisplays(dc -> displayImePolicyMap.put(dc.getDisplayId(), dc.getImePolicy()));
+ mWmService.mDisplayImePolicyCache = Collections.unmodifiableMap(displayImePolicyMap);
+ }
+
/** Update lists of UIDs that are present on displays and have access to them. */
void updateUIDsPresentOnDisplay() {
mDisplayAccessUIDs.clear();
@@ -3665,7 +3674,8 @@
try {
if (mTaskSupervisor.realStartActivityLocked(r, mApp,
- mTop == r && r.isFocusable() /* andResume */, true /* checkConfig */)) {
+ mTop == r && r.getTask().canBeResumed(r) /* andResume */,
+ true /* checkConfig */)) {
mHasActivityStarted = true;
}
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 005544b..7acc0c5 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -73,6 +73,7 @@
import android.view.SurfaceSession;
import android.view.WindowManager;
import android.window.ClientWindowFrames;
+import android.window.IOnBackInvokedCallback;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.logging.MetricsLoggerWrapper;
@@ -490,6 +491,16 @@
}
}
+ @Override
+ public void reportKeepClearAreasChanged(IWindow window, List<Rect> keepClearAreas) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mService.reportKeepClearAreasChanged(this, window, keepClearAreas);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
private void actionOnWallpaper(IBinder window,
BiConsumer<WallpaperController, WindowState> action) {
final WindowState windowState = mService.windowForClientLocked(this, window, true);
@@ -863,4 +874,10 @@
Binder.restoreCallingIdentity(origId);
}
}
+
+ @Override
+ public void setOnBackInvokedCallback(IWindow iWindow,
+ IOnBackInvokedCallback iOnBackInvokedCallback) throws RemoteException {
+ // TODO: Set the callback to the WindowState of the window.
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index c8781ae..177d2e6 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -24,8 +24,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
@@ -796,13 +794,8 @@
return TASK_FRAGMENT_VISIBILITY_VISIBLE;
}
- boolean gotRootSplitScreenFragment = false;
- boolean gotOpaqueSplitScreenPrimary = false;
- boolean gotOpaqueSplitScreenSecondary = false;
boolean gotTranslucentFullscreen = false;
boolean gotTranslucentAdjacent = false;
- boolean gotTranslucentSplitScreenPrimary = false;
- boolean gotTranslucentSplitScreenSecondary = false;
boolean shouldBeVisible = true;
// This TaskFragment is only considered visible if all its parent TaskFragments are
@@ -821,8 +814,6 @@
}
final List<TaskFragment> adjacentTaskFragments = new ArrayList<>();
- final int windowingMode = getWindowingMode();
- final boolean isAssistantType = isActivityTypeAssistant();
for (int i = parent.getChildCount() - 1; i >= 0; --i) {
final WindowContainer other = parent.getChildAt(i);
if (other == null) continue;
@@ -870,37 +861,6 @@
}
// Multi-window TaskFragment that matches parent bounds would occlude other children
return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
- } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- && !gotOpaqueSplitScreenPrimary) {
- gotRootSplitScreenFragment = true;
- gotTranslucentSplitScreenPrimary = isTranslucent(other, starting);
- gotOpaqueSplitScreenPrimary = !gotTranslucentSplitScreenPrimary;
- if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- && gotOpaqueSplitScreenPrimary) {
- // Can't be visible behind another opaque TaskFragment in split-screen-primary.
- return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
- }
- } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
- && !gotOpaqueSplitScreenSecondary) {
- gotRootSplitScreenFragment = true;
- gotTranslucentSplitScreenSecondary = isTranslucent(other, starting);
- gotOpaqueSplitScreenSecondary = !gotTranslucentSplitScreenSecondary;
- if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
- && gotOpaqueSplitScreenSecondary) {
- // Can't be visible behind another opaque TaskFragment in split-screen-secondary
- return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
- }
- }
- if (gotOpaqueSplitScreenPrimary && gotOpaqueSplitScreenSecondary) {
- // Can not be visible if we are in split-screen windowing mode and both halves of
- // the screen are opaque.
- return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
- }
- if (isAssistantType && gotRootSplitScreenFragment) {
- // Assistant TaskFragment can't be visible behind split-screen. In addition to
- // this not making sense, it also works around an issue here we boost the z-order
- // of the assistant window surfaces in window manager whenever it is visible.
- return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
}
final TaskFragment otherTaskFrag = other.asTaskFragment();
@@ -926,34 +886,6 @@
return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
}
- // Handle cases when there can be a translucent split-screen TaskFragment on top.
- switch (windowingMode) {
- case WINDOWING_MODE_FULLSCREEN:
- if (gotTranslucentSplitScreenPrimary || gotTranslucentSplitScreenSecondary) {
- // At least one of the split-screen TaskFragment that covers this one is
- // translucent.
- // When in split mode, home will be reparented to the secondary split while
- // leaving TaskFragments not supporting split below. Due to
- // TaskDisplayArea#assignRootTaskOrdering always adjusts home surface layer to
- // the bottom, this makes sure TaskFragments not in split roots won't occlude
- // home task unexpectedly.
- return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
- }
- break;
- case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
- if (gotTranslucentSplitScreenPrimary) {
- // Covered by translucent primary split-screen on top.
- return TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
- }
- break;
- case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
- if (gotTranslucentSplitScreenSecondary) {
- // Covered by translucent secondary split-screen on top.
- return TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
- }
- break;
- }
-
// Lastly - check if there is a translucent fullscreen TaskFragment on top.
return gotTranslucentFullscreen
? TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 1ab191b..b9fa297 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -16,6 +16,9 @@
package com.android.server.wm;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ClipData;
@@ -40,6 +43,7 @@
import com.android.server.input.InputManagerService;
import com.android.server.policy.WindowManagerPolicy;
+import java.lang.annotation.Retention;
import java.util.List;
import java.util.Set;
@@ -609,6 +613,7 @@
/**
* Checks whether the specified IME client has IME focus or not.
*
+ * @param windowToken The window token of the input method client
* @param uid UID of the process to be queried
* @param pid PID of the process to be queried
* @param displayId Display ID reported from the client. Note that this method also verifies
@@ -616,7 +621,22 @@
* @return {@code true} if the IME client specified with {@code uid}, {@code pid}, and
* {@code displayId} has IME focus
*/
- public abstract boolean isInputMethodClientFocus(int uid, int pid, int displayId);
+ public abstract @ImeClientFocusResult int hasInputMethodClientFocus(IBinder windowToken,
+ int uid, int pid, int displayId);
+
+ @Retention(SOURCE)
+ @IntDef({
+ ImeClientFocusResult.HAS_IME_FOCUS,
+ ImeClientFocusResult.NOT_IME_TARGET_WINDOW,
+ ImeClientFocusResult.DISPLAY_ID_MISMATCH,
+ ImeClientFocusResult.INVALID_DISPLAY_ID
+ })
+ public @interface ImeClientFocusResult {
+ int HAS_IME_FOCUS = 0;
+ int NOT_IME_TARGET_WINDOW = -1;
+ int DISPLAY_ID_MISMATCH = -2;
+ int INVALID_DISPLAY_ID = -3;
+ }
/**
* Checks whether the given {@code uid} is allowed to use the given {@code displayId} or not.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 3515926..c1a8ceb 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -171,15 +171,12 @@
import android.content.res.TypedArray;
import android.database.ContentObserver;
import android.graphics.Bitmap;
-import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.configstore.V1_0.OptionalBool;
-import android.hardware.configstore.V1_1.DisplayOrientation;
import android.hardware.configstore.V1_1.ISurfaceFlingerConfigs;
-import android.hardware.configstore.V1_1.OptionalDisplayOrientation;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.InputManager;
@@ -231,7 +228,6 @@
import android.util.proto.ProtoOutputStream;
import android.view.Choreographer;
import android.view.Display;
-import android.view.DisplayAddress;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.IAppTransitionAnimationSpecsFuture;
@@ -442,8 +438,6 @@
*/
static final boolean ENABLE_FIXED_ROTATION_TRANSFORM =
SystemProperties.getBoolean("persist.wm.fixed_rotation_transform", true);
- private @Surface.Rotation int mPrimaryDisplayOrientation = Surface.ROTATION_0;
- private DisplayAddress mPrimaryDisplayPhysicalAddress;
// Enums for animation scale update types.
@Retention(RetentionPolicy.SOURCE)
@@ -592,6 +586,13 @@
final ArrayList<WindowState> mResizingWindows = new ArrayList<>();
/**
+ * Mapping of displayId to {@link DisplayImePolicy}.
+ * Note that this can be accessed without holding the lock.
+ */
+ volatile Map<Integer, Integer> mDisplayImePolicyCache = Collections.unmodifiableMap(
+ new ArrayMap<>());
+
+ /**
* Windows whose surface should be destroyed.
*/
final ArrayList<WindowState> mDestroySurface = new ArrayList<>();
@@ -1793,8 +1794,7 @@
prepareNoneTransitionForRelaunching(activity);
}
- if (displayPolicy.getLayoutHint(win.mAttrs, token, outInsetsState,
- win.isClientLocal())) {
+ if (displayPolicy.areSystemBarsForcedShownLw()) {
res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
}
@@ -1841,6 +1841,7 @@
displayContent.getInsetsStateController().updateAboveInsetsState(
win, false /* notifyInsetsChanged */);
+ outInsetsState.set(win.getCompatInsetsState(), win.isClientLocal());
getInsetsSourceControls(win, outActiveControls);
}
@@ -2443,23 +2444,6 @@
configChanged = displayContent.updateOrientation();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- final DisplayInfo displayInfo = win.getDisplayInfo();
- int transformHint = displayInfo.rotation;
- // If the window is on the primary display, use the panel orientation to adjust the
- // transform hint
- final boolean isPrimaryDisplay = displayInfo.address != null &&
- displayInfo.address.equals(mPrimaryDisplayPhysicalAddress);
- if (isPrimaryDisplay) {
- transformHint = (transformHint + mPrimaryDisplayOrientation) % 4;
- }
- outSurfaceControl.setTransformHint(
- SurfaceControl.rotationToBufferTransform(transformHint));
- ProtoLog.v(WM_DEBUG_ORIENTATION,
- "Passing transform hint %d for window %s%s",
- transformHint, win,
- isPrimaryDisplay ? " on primary display with orientation "
- + mPrimaryDisplayOrientation : "");
-
if (toBeDisplayed && win.mIsWallpaper) {
displayContent.mWallpaperController.updateWallpaperOffset(win, false /* sync */);
}
@@ -4355,6 +4339,15 @@
}
}
+ void reportKeepClearAreasChanged(Session session, IWindow window, List<Rect> keepClearAreas) {
+ synchronized (mGlobalLock) {
+ final WindowState win = windowForClientLocked(session, window, true);
+ if (win.setKeepClearAreas(keepClearAreas)) {
+ win.getDisplayContent().updateKeepClearAreas();
+ }
+ }
+ }
+
@Override
public void registerDisplayFoldListener(IDisplayFoldListener listener) {
mPolicy.registerDisplayFoldListener(listener);
@@ -4927,9 +4920,6 @@
mTaskSnapshotController.systemReady();
mHasWideColorGamutSupport = queryWideColorGamutSupport();
mHasHdrSupport = queryHdrSupport();
- mPrimaryDisplayOrientation = queryPrimaryDisplayOrientation();
- mPrimaryDisplayPhysicalAddress =
- DisplayAddress.fromPhysicalDisplayId(SurfaceControl.getPrimaryPhysicalDisplayId());
UiThread.getHandler().post(mSettingsObserver::loadSettings);
IVrManager vrManager = IVrManager.Stub.asInterface(
ServiceManager.getService(Context.VR_SERVICE));
@@ -4992,39 +4982,6 @@
return false;
}
- private static @Surface.Rotation int queryPrimaryDisplayOrientation() {
- Optional<SurfaceFlingerProperties.primary_display_orientation_values> prop =
- SurfaceFlingerProperties.primary_display_orientation();
- if (prop.isPresent()) {
- switch (prop.get()) {
- case ORIENTATION_90: return Surface.ROTATION_90;
- case ORIENTATION_180: return Surface.ROTATION_180;
- case ORIENTATION_270: return Surface.ROTATION_270;
- case ORIENTATION_0:
- default:
- return Surface.ROTATION_0;
- }
- }
- try {
- ISurfaceFlingerConfigs surfaceFlinger = ISurfaceFlingerConfigs.getService();
- OptionalDisplayOrientation primaryDisplayOrientation =
- surfaceFlinger.primaryDisplayOrientation();
- if (primaryDisplayOrientation != null && primaryDisplayOrientation.specified) {
- switch (primaryDisplayOrientation.value) {
- case DisplayOrientation.ORIENTATION_90: return Surface.ROTATION_90;
- case DisplayOrientation.ORIENTATION_180: return Surface.ROTATION_180;
- case DisplayOrientation.ORIENTATION_270: return Surface.ROTATION_270;
- case DisplayOrientation.ORIENTATION_0:
- default:
- return Surface.ROTATION_0;
- }
- }
- } catch (Exception e) {
- // Use default value if we can't talk to config store.
- }
- return Surface.ROTATION_0;
- }
-
// Returns an input target which is mapped to the given input token. This can be a WindowState
// or an embedded window.
@Nullable InputTarget getInputTargetFromToken(IBinder inputToken) {
@@ -6947,6 +6904,7 @@
void setForceDesktopModeOnExternalDisplays(boolean forceDesktopModeOnExternalDisplays) {
synchronized (mGlobalLock) {
mForceDesktopModeOnExternalDisplays = forceDesktopModeOnExternalDisplays;
+ mRoot.updateDisplayImePolicyCache();
}
}
@@ -7002,23 +6960,6 @@
}
}
- @Override
- public void setForwardedInsets(int displayId, Insets insets) throws RemoteException {
- synchronized (mGlobalLock) {
- final DisplayContent dc = mRoot.getDisplayContent(displayId);
- if (dc == null) {
- return;
- }
- final int callingUid = Binder.getCallingUid();
- final int displayOwnerUid = dc.getDisplay().getOwnerUid();
- if (callingUid != displayOwnerUid) {
- throw new SecurityException(
- "Only owner of the display can set ForwardedInsets to it.");
- }
- dc.setForwardedInsets(insets);
- }
- }
-
MousePositionTracker mMousePositionTracker = new MousePositionTracker();
private static class MousePositionTracker implements PointerEventListener {
@@ -7378,16 +7319,14 @@
if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "getDisplayImePolicy()")) {
throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
}
- final DisplayContent dc = mRoot.getDisplayContent(displayId);
- if (dc == null) {
+ final Map<Integer, Integer> displayImePolicyCache = mDisplayImePolicyCache;
+ if (!displayImePolicyCache.containsKey(displayId)) {
ProtoLog.w(WM_ERROR,
"Attempted to get IME policy of a display that does not exist: %d",
displayId);
return DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
}
- synchronized (mGlobalLock) {
- return dc.getImePolicy();
- }
+ return displayImePolicyCache.get(displayId);
}
@Override
@@ -7736,19 +7675,32 @@
}
@Override
- public boolean isInputMethodClientFocus(int uid, int pid, int displayId) {
+ public @ImeClientFocusResult int hasInputMethodClientFocus(IBinder windowToken,
+ int uid, int pid, int displayId) {
if (displayId == Display.INVALID_DISPLAY) {
- return false;
+ return ImeClientFocusResult.INVALID_DISPLAY_ID;
}
synchronized (mGlobalLock) {
final DisplayContent displayContent = mRoot.getTopFocusedDisplayContent();
+ final WindowState window = mWindowMap.get(windowToken);
+ if (window == null) {
+ return ImeClientFocusResult.NOT_IME_TARGET_WINDOW;
+ }
+ final int tokenDisplayId = window.getDisplayContent().getDisplayId();
+ if (tokenDisplayId != displayId) {
+ Slog.e(TAG, "isInputMethodClientFocus: display ID mismatch."
+ + " from client: " + displayId
+ + " from window: " + tokenDisplayId);
+ return ImeClientFocusResult.DISPLAY_ID_MISMATCH;
+ }
if (displayContent == null
|| displayContent.getDisplayId() != displayId
|| !displayContent.hasAccess(uid)) {
- return false;
+ return ImeClientFocusResult.INVALID_DISPLAY_ID;
}
+
if (displayContent.isInputMethodClientFocus(uid, pid)) {
- return true;
+ return ImeClientFocusResult.HAS_IME_FOCUS;
}
// Okay, how about this... what is the current focus?
// It seems in some cases we may not have moved the IM
@@ -7761,10 +7713,11 @@
final WindowState currentFocus = displayContent.mCurrentFocus;
if (currentFocus != null && currentFocus.mSession.mUid == uid
&& currentFocus.mSession.mPid == pid) {
- return currentFocus.canBeImeTarget();
+ return currentFocus.canBeImeTarget() ? ImeClientFocusResult.HAS_IME_FOCUS
+ : ImeClientFocusResult.NOT_IME_TARGET_WINDOW;
}
}
- return false;
+ return ImeClientFocusResult.NOT_IME_TARGET_WINDOW;
}
@Override
@@ -7863,9 +7816,7 @@
@Override
public @DisplayImePolicy int getDisplayImePolicy(int displayId) {
- synchronized (mGlobalLock) {
- return WindowManagerService.this.getDisplayImePolicy(displayId);
- }
+ return WindowManagerService.this.getDisplayImePolicy(displayId);
}
@Override
@@ -8521,6 +8472,7 @@
public boolean getWindowInsets(WindowManager.LayoutParams attrs, int displayId,
InsetsState outInsetsState) {
final boolean fromLocal = Binder.getCallingPid() == myPid();
+ final int uid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -8529,9 +8481,20 @@
throw new WindowManager.InvalidDisplayException("Display#" + displayId
+ "could not be found!");
}
- final WindowToken windowToken = dc.getWindowToken(attrs.token);
- return dc.getDisplayPolicy().getLayoutHint(attrs, windowToken, outInsetsState,
- fromLocal);
+ final WindowToken token = dc.getWindowToken(attrs.token);
+ final float overrideScale = mAtmService.mCompatModePackages.getCompatScale(
+ attrs.packageName, uid);
+ final InsetsState state = dc.getInsetsPolicy().getInsetsForWindowMetrics(attrs);
+ final boolean hasCompatScale =
+ WindowState.hasCompatScale(attrs, token, overrideScale);
+ outInsetsState.set(state, hasCompatScale || fromLocal);
+ if (hasCompatScale) {
+ final float compatScale = token != null && token.hasSizeCompatBounds()
+ ? token.getSizeCompatScale() * overrideScale
+ : overrideScale;
+ outInsetsState.scale(1f / compatScale);
+ }
+ return dc.getDisplayPolicy().areSystemBarsForcedShownLw();
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -8760,20 +8723,21 @@
}
boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken) {
+ final Task imeTargetWindowTask;
synchronized (mGlobalLock) {
final WindowState imeTargetWindow = mWindowMap.get(imeTargetWindowToken);
if (imeTargetWindow == null) {
return false;
}
- final Task imeTargetWindowTask = imeTargetWindow.getTask();
+ imeTargetWindowTask = imeTargetWindow.getTask();
if (imeTargetWindowTask == null) {
return false;
}
- final TaskSnapshot snapshot = getTaskSnapshot(imeTargetWindowTask.mTaskId,
- imeTargetWindowTask.mUserId, false /* isLowResolution */,
- false /* restoreFromDisk */);
- return snapshot != null && snapshot.hasImeSurface();
}
+ final TaskSnapshot snapshot = getTaskSnapshot(imeTargetWindowTask.mTaskId,
+ imeTargetWindowTask.mUserId, false /* isLowResolution */,
+ false /* restoreFromDisk */);
+ return snapshot != null && snapshot.hasImeSurface();
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e4d3e05..1f83767 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -172,6 +172,7 @@
import static com.android.server.wm.WindowStateProto.IS_ON_SCREEN;
import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY;
import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
+import static com.android.server.wm.WindowStateProto.KEEP_CLEAR_AREAS;
import static com.android.server.wm.WindowStateProto.PENDING_SEAMLESS_ROTATION;
import static com.android.server.wm.WindowStateProto.REMOVED;
import static com.android.server.wm.WindowStateProto.REMOVE_ON_EXIT;
@@ -196,6 +197,7 @@
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.Region;
import android.gui.TouchOcclusionMode;
import android.os.Binder;
@@ -442,9 +444,8 @@
// Current transformation being applied.
float mGlobalScale=1;
- float mLastGlobalScale=1;
float mInvGlobalScale=1;
- float mOverrideScale = 1;
+ final float mOverrideScale;
float mHScale=1, mVScale=1;
float mLastHScale=1, mLastVScale=1;
@@ -471,6 +472,12 @@
* Coordinates are relative to the window's position.
*/
private final List<Rect> mExclusionRects = new ArrayList<>();
+ /**
+ * List of rects which should ideally not be covered by floating windows like Pip.
+ *
+ * Coordinates are relative to the window's position.
+ */
+ private final List<Rect> mKeepClearAreas = new ArrayList<>();
// 0 = left, 1 = right
private final int[] mLastRequestedExclusionHeight = {0, 0};
@@ -1012,6 +1019,55 @@
}
}
+ /**
+ * @return a list of rects that should ideally not be covered by floating windows like pip.
+ * The returned rect coordinates are relative to the display origin.
+ */
+ List<Rect> getKeepClearAreas() {
+ final Matrix tmpMatrix = new Matrix();
+ final float[] tmpFloat9 = new float[9];
+ return getKeepClearAreas(tmpMatrix, tmpFloat9);
+ }
+
+ /**
+ * @param tmpMatrix a temporary matrix to be used for transformations
+ * @param float9 a temporary array of 9 floats
+ *
+ * @return a list of rects that should ideally not be covered by floating windows like pip.
+ * The returned rect coordinates are relative to the display origin.
+ */
+ List<Rect> getKeepClearAreas(Matrix tmpMatrix, float[] float9) {
+ getTransformationMatrix(float9, tmpMatrix);
+
+ // Translate all keep-clear rects to screen coordinates.
+ final List<Rect> transformedKeepClearAreas = new ArrayList<Rect>();
+ final RectF tmpRect = new RectF();
+ Rect curr;
+ for (Rect r : mKeepClearAreas) {
+ tmpRect.set(r);
+ tmpMatrix.mapRect(tmpRect);
+ curr = new Rect();
+ tmpRect.roundOut(curr);
+ transformedKeepClearAreas.add(curr);
+ }
+ return transformedKeepClearAreas;
+ }
+
+ /**
+ * @param keepClearAreas the new keep-clear areas for this window. The rects should be defined
+ * in window coordinate space
+ *
+ * @return true if there is a change in the list of keep-clear areas; false otherwise
+ */
+ boolean setKeepClearAreas(List<Rect> keepClearAreas) {
+ if (mKeepClearAreas.equals(keepClearAreas)) {
+ return false;
+ }
+ mKeepClearAreas.clear();
+ mKeepClearAreas.addAll(keepClearAreas);
+ return true;
+ }
+
interface PowerManagerWrapper {
void wakeUp(long time, @WakeReason int reason, String details);
@@ -1091,6 +1147,7 @@
mSubLayer = 0;
mWinAnimator = null;
mWpcForDisplayAreaConfigChanges = null;
+ mOverrideScale = 1f;
return;
}
mDeathRecipient = deathRecipient;
@@ -1138,6 +1195,7 @@
mLayer = 0;
mOverrideScale = mWmService.mAtmService.mCompatModePackages.getCompatScale(
mAttrs.packageName, s.mUid);
+ updateGlobalScale();
// Make sure we initial all fields before adding to parentWindow, to prevent exception
// during onDisplayChanged.
@@ -1167,6 +1225,23 @@
mSession.windowAddedLocked();
}
+ boolean updateGlobalScale() {
+ if (hasCompatScale()) {
+ if (mOverrideScale != 1f) {
+ mGlobalScale = mToken.hasSizeCompatBounds()
+ ? mToken.getSizeCompatScale() * mOverrideScale
+ : mOverrideScale;
+ } else {
+ mGlobalScale = mToken.getSizeCompatScale();
+ }
+ mInvGlobalScale = 1f / mGlobalScale;
+ return true;
+ }
+
+ mGlobalScale = mInvGlobalScale = 1f;
+ return false;
+ }
+
/**
* @return {@code true} if the application runs in size compatibility mode or has an app level
* scaling override set.
@@ -1175,7 +1250,7 @@
* @see ActivityRecord#hasSizeCompatBounds()
*/
boolean hasCompatScale() {
- return mOverrideScale != 1f || hasCompatScale(mAttrs, mActivityRecord);
+ return hasCompatScale(mAttrs, mActivityRecord, mOverrideScale);
}
/**
@@ -1183,11 +1258,16 @@
* @see android.content.res.CompatibilityInfo#supportsScreen
* @see ActivityRecord#hasSizeCompatBounds()
*/
- static boolean hasCompatScale(WindowManager.LayoutParams attrs, WindowToken windowToken) {
- return (attrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0
- || (windowToken != null && windowToken.hasSizeCompatBounds()
- // Exclude starting window because it is not displayed by the application.
- && attrs.type != TYPE_APPLICATION_STARTING);
+ static boolean hasCompatScale(WindowManager.LayoutParams attrs, WindowToken token,
+ float overrideScale) {
+ if ((attrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0) {
+ return true;
+ }
+ if (attrs.type == TYPE_APPLICATION_STARTING) {
+ // Exclude starting window because it is not displayed by the application.
+ return false;
+ }
+ return token != null && token.hasSizeCompatBounds() || overrideScale != 1f;
}
/**
@@ -1691,21 +1771,6 @@
&& (mActivityRecord.firstWindowDrawn || mActivityRecord.startingDisplayed);
}
- void prelayout() {
- if (hasCompatScale()) {
- if (mOverrideScale != 1f) {
- mGlobalScale = mToken.hasSizeCompatBounds()
- ? mToken.getSizeCompatScale() * mOverrideScale
- : mOverrideScale;
- } else {
- mGlobalScale = mToken.getSizeCompatScale();
- }
- mInvGlobalScale = 1 / mGlobalScale;
- } else {
- mGlobalScale = mInvGlobalScale = 1;
- }
- }
-
@Override
boolean hasContentToDisplay() {
if (!mAppFreezing && isDrawn() && (mViewVisibility == View.VISIBLE
@@ -2928,7 +2993,6 @@
@Override
public void binderDied() {
try {
- boolean resetSplitScreenResizing = false;
synchronized (mWmService.mGlobalLock) {
final WindowState win = mWmService
.windowForClientLocked(mSession, mClient, false);
@@ -2944,16 +3008,6 @@
WindowState.this.removeIfPossible();
}
}
- if (resetSplitScreenResizing) {
- try {
- // Note: this calls into ActivityManager, so we must *not* hold the window
- // manager lock while calling this.
- mWmService.mActivityTaskManager.setSplitScreenResizing(false);
- } catch (RemoteException e) {
- // Local call, shouldn't return RemoteException.
- throw e.rethrowAsRuntimeException();
- }
- }
} catch (IllegalArgumentException ex) {
// This will happen if the window has already been removed.
}
@@ -4063,6 +4117,9 @@
proto.write(FORCE_SEAMLESS_ROTATION, mForceSeamlesslyRotate);
proto.write(HAS_COMPAT_SCALE, hasCompatScale());
proto.write(GLOBAL_SCALE, mGlobalScale);
+ for (Rect r : getKeepClearAreas()) {
+ r.dumpDebug(proto, KEEP_CLEAR_AREAS);
+ }
proto.end(token);
}
@@ -4231,6 +4288,7 @@
}
pw.println(prefix + "isOnScreen=" + isOnScreen());
pw.println(prefix + "isVisible=" + isVisible());
+ pw.println(prefix + "keepClearAreas=" + getKeepClearAreas());
if (dumpAll) {
final String visibilityString = mRequestedVisibilities.toString();
if (!visibilityString.isEmpty()) {
@@ -5259,7 +5317,6 @@
mLastVScale != newVScale ) {
getPendingTransaction().setMatrix(getSurfaceControl(),
newHScale, 0, 0, newVScale);
- mLastGlobalScale = mGlobalScale;
mLastHScale = newHScale;
mLastVScale = newVScale;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index df9ab50..f19202a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -145,6 +145,10 @@
private static final String TAG_PREFERENTIAL_NETWORK_SERVICE_ENABLED =
"preferential-network-service-enabled";
private static final String TAG_USB_DATA_SIGNALING = "usb-data-signaling";
+ private static final String TAG_WIFI_MIN_SECURITY = "wifi-min-security";
+ private static final String TAG_SSID_ALLOWLIST = "ssid-allowlist";
+ private static final String TAG_SSID_DENYLIST = "ssid-denylist";
+ private static final String TAG_SSID = "ssid";
private static final String ATTR_VALUE = "value";
private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
@@ -237,6 +241,14 @@
// List of package names to keep cached.
List<String> keepUninstalledPackages;
+ // The allowlist of SSIDs the device may connect to.
+ // By default, the allowlist restriction is deactivated.
+ List<String> mSsidAllowlist;
+
+ // The denylist of SSIDs the device may not connect to.
+ // By default, the denylist restriction is deactivated.
+ List<String> mSsidDenylist;
+
// TODO: review implementation decisions with frameworks team
boolean specifiesGlobalProxy = false;
String globalProxySpec = null;
@@ -298,6 +310,8 @@
private static final boolean USB_DATA_SIGNALING_ENABLED_DEFAULT = true;
boolean mUsbDataSignalingEnabled = USB_DATA_SIGNALING_ENABLED_DEFAULT;
+ int mWifiMinimumSecurityLevel = DevicePolicyManager.WIFI_SECURITY_OPEN;
+
ActiveAdmin(DeviceAdminInfo info, boolean isParent) {
this.info = info;
this.isParent = isParent;
@@ -574,6 +588,15 @@
if (mUsbDataSignalingEnabled != USB_DATA_SIGNALING_ENABLED_DEFAULT) {
writeAttributeValueToXml(out, TAG_USB_DATA_SIGNALING, mUsbDataSignalingEnabled);
}
+ if (mWifiMinimumSecurityLevel != DevicePolicyManager.WIFI_SECURITY_OPEN) {
+ writeAttributeValueToXml(out, TAG_WIFI_MIN_SECURITY, mWifiMinimumSecurityLevel);
+ }
+ if (mSsidAllowlist != null && !mSsidAllowlist.isEmpty()) {
+ writeAttributeValuesToXml(out, TAG_SSID_ALLOWLIST, TAG_SSID, mSsidAllowlist);
+ }
+ if (mSsidDenylist != null && !mSsidDenylist.isEmpty()) {
+ writeAttributeValuesToXml(out, TAG_SSID_DENYLIST, TAG_SSID, mSsidDenylist);
+ }
}
void writeTextToXml(TypedXmlSerializer out, String tag, String text) throws IOException {
@@ -826,6 +849,14 @@
} else if (TAG_USB_DATA_SIGNALING.equals(tag)) {
mUsbDataSignalingEnabled = parser.getAttributeBoolean(null, ATTR_VALUE,
USB_DATA_SIGNALING_ENABLED_DEFAULT);
+ } else if (TAG_WIFI_MIN_SECURITY.equals(tag)) {
+ mWifiMinimumSecurityLevel = parser.getAttributeInt(null, ATTR_VALUE);
+ } else if (TAG_SSID_ALLOWLIST.equals(tag)) {
+ mSsidAllowlist = new ArrayList<>();
+ readAttributeValues(parser, TAG_SSID, mSsidAllowlist);
+ } else if (TAG_SSID_DENYLIST.equals(tag)) {
+ mSsidDenylist = new ArrayList<>();
+ readAttributeValues(parser, TAG_SSID, mSsidDenylist);
} else {
Slogf.w(LOG_TAG, "Unknown admin tag: %s", tag);
XmlUtils.skipCurrentTag(parser);
@@ -1184,5 +1215,14 @@
pw.print("mUsbDataSignaling=");
pw.println(mUsbDataSignalingEnabled);
+
+ pw.print("mWifiMinimumSecurityLevel=");
+ pw.println(mWifiMinimumSecurityLevel);
+
+ pw.print("mSsidAllowlist=");
+ pw.println(mSsidAllowlist);
+
+ pw.print("mSsidDenylist=");
+ pw.println(mSsidDenylist);
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7c0d549..92952cd 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -100,6 +100,18 @@
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
import static android.app.admin.DevicePolicyManager.WIPE_SILENTLY;
+import static android.app.admin.DevicePolicyResources.Strings.Core.LOCATION_CHANGED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.LOCATION_CHANGED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.NETWORK_LOGGING_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.NETWORK_LOGGING_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PERSONAL_APP_SUSPENSION_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PRINTING_DISABLED_NAMED_ADMIN;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_GENERIC_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_TITLE;
import static android.app.admin.ProvisioningException.ERROR_ADMIN_PACKAGE_INSTALLATION_FAILED;
import static android.app.admin.ProvisioningException.ERROR_PRE_CONDITION_FAILED;
import static android.app.admin.ProvisioningException.ERROR_PROFILE_CREATION_FAILED;
@@ -2236,7 +2248,7 @@
* a managed profile.
*/
@GuardedBy("getLockObject()")
- private void applyManagedProfileRestrictionIfDeviceOwnerLocked() {
+ private void applyProfileRestrictionsIfDeviceOwnerLocked() {
final int doUserId = mOwners.getDeviceOwnerUserId();
if (doUserId == UserHandle.USER_NULL) {
if (VERBOSE_LOG) Slogf.d(LOG_TAG, "No DO found, skipping application of restriction.");
@@ -2244,7 +2256,17 @@
}
final UserHandle doUserHandle = UserHandle.of(doUserId);
- // Set the restriction if not set.
+
+ // Based on CDD : https://source.android.com/compatibility/12/android-12-cdd#95_multi-user_support,
+ // creation of clone profile is not allowed in case device owner is set.
+ // Enforcing this restriction on setting up of device owner.
+ if (!mUserManager.hasUserRestriction(
+ UserManager.DISALLOW_ADD_CLONE_PROFILE, doUserHandle)) {
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE, true,
+ doUserHandle);
+ }
+ // Creation of managed profile is restricted in case device owner is set, enforcing this
+ // restriction by setting user level restriction at time of device owner setup.
if (!mUserManager.hasUserRestriction(
UserManager.DISALLOW_ADD_MANAGED_PROFILE, doUserHandle)) {
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
@@ -3153,7 +3175,7 @@
case SystemService.PHASE_ACTIVITY_MANAGER_READY:
synchronized (getLockObject()) {
migrateToProfileOnOrganizationOwnedDeviceIfCompLocked();
- applyManagedProfileRestrictionIfDeviceOwnerLocked();
+ applyProfileRestrictionsIfDeviceOwnerLocked();
}
maybeStartSecurityLogMonitorOnActivityManagerReady();
break;
@@ -3778,6 +3800,12 @@
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, false,
userHandle);
}
+ // When a device owner is set, the system automatically restricts adding a clone profile.
+ // Remove this restriction when the device owner is cleared.
+ if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE, userHandle)) {
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE, false,
+ userHandle);
+ }
}
/**
@@ -6929,12 +6957,8 @@
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_WIPE_DATA);
if (TextUtils.isEmpty(wipeReasonForUser)) {
- if (calledByProfileOwnerOnOrgOwnedDevice && !calledOnParentInstance) {
- wipeReasonForUser = mContext.getString(R.string.device_ownership_relinquished);
- } else {
- wipeReasonForUser = mContext.getString(
- R.string.work_profile_deleted_description_dpm_wipe);
- }
+ wipeReasonForUser = getGenericWipeReason(
+ calledByProfileOwnerOnOrgOwnedDevice, calledOnParentInstance);
}
int userId = admin != null ? admin.getUserHandle().getIdentifier()
@@ -6985,6 +7009,18 @@
wipeDataNoLock(adminComp, flags, internalReason, wipeReasonForUser, userId);
}
+ private String getGenericWipeReason(
+ boolean calledByProfileOwnerOnOrgOwnedDevice, boolean calledOnParentInstance) {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return calledByProfileOwnerOnOrgOwnedDevice && !calledOnParentInstance
+ ? dpm.getString(WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE,
+ () -> mContext.getString(
+ R.string.device_ownership_relinquished))
+ : dpm.getString(WORK_PROFILE_DELETED_GENERIC_MESSAGE,
+ () -> mContext.getString(
+ R.string.work_profile_deleted_description_dpm_wipe));
+ }
+
/**
* Clears device wide policies enforced by COPE PO when relinquishing the device. This method
* should be invoked once the admin is gone, so that all methods that rely on calculating
@@ -7069,7 +7105,7 @@
Notification notification =
new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN)
.setSmallIcon(android.R.drawable.stat_sys_warning)
- .setContentTitle(mContext.getString(R.string.work_profile_deleted))
+ .setContentTitle(getWorkProfileDeletedTitle())
.setContentText(wipeReasonForUser)
.setColor(mContext.getColor(R.color.system_notification_accent_color))
.setStyle(new Notification.BigTextStyle().bigText(wipeReasonForUser))
@@ -7077,6 +7113,12 @@
mInjector.getNotificationManager().notify(SystemMessage.NOTE_PROFILE_WIPED, notification);
}
+ private String getWorkProfileDeletedTitle() {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(WORK_PROFILE_DELETED_TITLE,
+ () -> mContext.getString(R.string.work_profile_deleted));
+ }
+
private void clearWipeProfileNotification() {
mInjector.getNotificationManager().cancel(SystemMessage.NOTE_PROFILE_WIPED);
}
@@ -7307,12 +7349,10 @@
// able to do so).
// IMPORTANT: Call without holding the lock to prevent deadlock.
try {
- String wipeReasonForUser = mContext.getString(
- R.string.work_profile_deleted_reason_maximum_password_failure);
wipeDataNoLock(strictestAdmin.info.getComponent(),
/*flags=*/ 0,
/*reason=*/ "reportFailedPasswordAttempt()",
- wipeReasonForUser,
+ getFailedPasswordAttemptWipeMessage(),
userId);
} catch (SecurityException e) {
Slogf.w(LOG_TAG, "Failed to wipe user " + userId
@@ -7326,6 +7366,13 @@
}
}
+ private String getFailedPasswordAttemptWipeMessage() {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE,
+ () -> mContext.getString(
+ R.string.work_profile_deleted_reason_maximum_password_failure));
+ }
+
/**
* Returns which user should be wiped if this admin's maximum filed password attempts policy is
* violated.
@@ -8470,6 +8517,12 @@
// on the primary profile).
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
UserHandle.of(userId));
+ // Restrict adding a clone profile when a device owner is set on the device.
+ // That is to prevent the co-existence of a clone profile and a device owner
+ // on the same device.
+ // CDD for reference : https://source.android.com/compatibility/12/android-12-cdd#95_multi-user_support
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE, true,
+ UserHandle.of(userId));
// TODO Send to system too?
sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED, userId);
});
@@ -8942,7 +8995,7 @@
mOwners.writeProfileOwner(userId);
deleteTransferOwnershipBundleLocked(userId);
toggleBackupServiceActive(userId, true);
- applyManagedProfileRestrictionIfDeviceOwnerLocked();
+ applyProfileRestrictionsIfDeviceOwnerLocked();
setNetworkLoggingActiveInternal(false);
}
@@ -12397,8 +12450,8 @@
Notification notification = new Notification.Builder(mContext,
SystemNotificationChannels.DEVICE_ADMIN)
.setSmallIcon(R.drawable.ic_info_outline)
- .setContentTitle(mContext.getString(R.string.location_changed_notification_title))
- .setContentText(mContext.getString(R.string.location_changed_notification_text))
+ .setContentTitle(getLocationChangedTitle())
+ .setContentText(getLocationChangedText())
.setColor(mContext.getColor(R.color.system_notification_accent_color))
.setShowWhen(true)
.setContentIntent(locationSettingsIntent)
@@ -12408,6 +12461,18 @@
notification);
}
+ private String getLocationChangedTitle() {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(LOCATION_CHANGED_TITLE,
+ () -> mContext.getString(R.string.location_changed_notification_title));
+ }
+
+ private String getLocationChangedText() {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(LOCATION_CHANGED_MESSAGE,
+ () -> mContext.getString(R.string.location_changed_notification_text));
+ }
+
@Override
public boolean setTime(ComponentName who, long millis) {
Objects.requireNonNull(who, "ComponentName is null");
@@ -12998,11 +13063,19 @@
Slogf.e(LOG_TAG, "appLabel is inexplicably null");
return null;
}
- return ((Context) ActivityThread.currentActivityThread().getSystemUiContext())
- .getResources().getString(R.string.printing_disabled_by, appLabel);
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(
+ PRINTING_DISABLED_NAMED_ADMIN,
+ () -> getDefaultPrintingDisabledMsg(appLabel),
+ appLabel);
}
}
+ private String getDefaultPrintingDisabledMsg(CharSequence appLabel) {
+ return ((Context) ActivityThread.currentActivityThread().getSystemUiContext())
+ .getResources().getString(R.string.printing_disabled_by, appLabel);
+ }
+
@Override
protected DevicePolicyCache getDevicePolicyCache() {
return mPolicyCache;
@@ -15534,16 +15607,18 @@
// Simple notification clicks are immutable
final PendingIntent pendingIntent = PendingIntent.getBroadcastAsUser(mContext, 0, intent,
PendingIntent.FLAG_IMMUTABLE, UserHandle.CURRENT);
+
+ final String title = getNetworkLoggingTitle();
+ final String text = getNetworkLoggingText();
Notification notification =
new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN)
.setSmallIcon(R.drawable.ic_info_outline)
- .setContentTitle(mContext.getString(R.string.network_logging_notification_title))
- .setContentText(mContext.getString(R.string.network_logging_notification_text))
- .setTicker(mContext.getString(R.string.network_logging_notification_title))
+ .setContentTitle(title)
+ .setContentText(text)
+ .setTicker(title)
.setShowWhen(true)
.setContentIntent(pendingIntent)
- .setStyle(new Notification.BigTextStyle()
- .bigText(mContext.getString(R.string.network_logging_notification_text)))
+ .setStyle(new Notification.BigTextStyle().bigText(text))
.build();
Slogf.i(LOG_TAG, "Sending network logging notification to user %d",
mNetworkLoggingNotificationUserId);
@@ -15552,6 +15627,18 @@
UserHandle.of(mNetworkLoggingNotificationUserId));
}
+ private String getNetworkLoggingTitle() {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(NETWORK_LOGGING_TITLE,
+ () -> mContext.getString(R.string.network_logging_notification_title));
+ }
+
+ private String getNetworkLoggingText() {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(NETWORK_LOGGING_MESSAGE,
+ () -> mContext.getString(R.string.network_logging_notification_text));
+ }
+
private void handleCancelNetworkLoggingNotification() {
if (mNetworkLoggingNotificationUserId == UserHandle.USER_NULL) {
// Happens when setNetworkLoggingActive(false) is called before called with true
@@ -17044,10 +17131,8 @@
0 /* requestCode */, intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- final String buttonText =
- mContext.getString(R.string.personal_apps_suspended_turn_profile_on);
- final Notification.Action turnProfileOnButton =
- new Notification.Action.Builder(null /* icon */, buttonText, pendingIntent).build();
+ final Notification.Action turnProfileOnButton = new Notification.Action.Builder(
+ /* icon= */ null, getPersonalAppSuspensionButtonText(), pendingIntent).build();
final String text;
final boolean ongoing;
@@ -17059,26 +17144,24 @@
mContext, profileOwner.mProfileOffDeadline, DateUtils.FORMAT_SHOW_DATE);
final String time = DateUtils.formatDateTime(
mContext, profileOwner.mProfileOffDeadline, DateUtils.FORMAT_SHOW_TIME);
- text = mContext.getString(
- R.string.personal_apps_suspension_soon_text, date, time, maxDays);
+ text = getPersonalAppSuspensionSoonText(date, time, maxDays);
ongoing = false;
} else {
- text = mContext.getString(R.string.personal_apps_suspension_text);
+ text = getPersonalAppSuspensionText();
ongoing = true;
}
final int color = mContext.getColor(R.color.personal_apps_suspension_notification_color);
final Bundle extras = new Bundle();
// TODO: Create a separate string for this.
- extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
- mContext.getString(R.string.notification_work_profile_content_description));
+ extras.putString(
+ Notification.EXTRA_SUBSTITUTE_APP_NAME, getWorkProfileContentDescription());
final Notification notification =
new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN)
.setSmallIcon(R.drawable.ic_corp_badge_no_background)
.setOngoing(ongoing)
.setAutoCancel(false)
- .setContentTitle(mContext.getString(
- R.string.personal_apps_suspension_title))
+ .setContentTitle(getPersonalAppSuspensionTitle())
.setContentText(text)
.setStyle(new Notification.BigTextStyle().bigText(text))
.setColor(color)
@@ -17089,6 +17172,38 @@
SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED, notification);
}
+ private String getPersonalAppSuspensionButtonText() {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE,
+ () -> mContext.getString(R.string.personal_apps_suspended_turn_profile_on));
+ }
+
+ private String getPersonalAppSuspensionTitle() {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(PERSONAL_APP_SUSPENSION_TITLE,
+ () -> mContext.getString(R.string.personal_apps_suspension_title));
+ }
+
+ private String getPersonalAppSuspensionText() {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(PERSONAL_APP_SUSPENSION_TITLE,
+ () -> mContext.getString(R.string.personal_apps_suspension_text));
+ }
+
+ private String getPersonalAppSuspensionSoonText(String date, String time, int maxDays) {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(PERSONAL_APP_SUSPENSION_TITLE,
+ () -> mContext.getString(
+ R.string.personal_apps_suspension_soon_text, date, time, maxDays),
+ date, time, maxDays);
+ }
+
+ private String getWorkProfileContentDescription() {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION,
+ () -> mContext.getString(R.string.notification_work_profile_content_description));
+ }
+
@Override
public void setManagedProfileMaximumTimeOff(ComponentName who, long timeoutMillis) {
Objects.requireNonNull(who, "ComponentName is null");
@@ -17979,6 +18094,117 @@
);
}
+ private void validateCurrentWifiMeetsAdminRequirements() {
+ mInjector.binderWithCleanCallingIdentity(
+ () -> mInjector.getWifiManager().validateCurrentWifiMeetsAdminRequirements());
+ }
+
+ @Override
+ public void setMinimumRequiredWifiSecurityLevel(int level) {
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(
+ isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ "Wi-Fi minimum security level can only be controlled by a device owner or "
+ + "a profile owner on an organization-owned device.");
+
+ boolean valueChanged = false;
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
+ if (admin.mWifiMinimumSecurityLevel != level) {
+ admin.mWifiMinimumSecurityLevel = level;
+ saveSettingsLocked(caller.getUserId());
+ valueChanged = true;
+ }
+ }
+ if (valueChanged) validateCurrentWifiMeetsAdminRequirements();
+ }
+
+ @Override
+ public int getMinimumRequiredWifiSecurityLevel() {
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+ UserHandle.USER_SYSTEM);
+ return (admin == null) ? DevicePolicyManager.WIFI_SECURITY_OPEN
+ : admin.mWifiMinimumSecurityLevel;
+ }
+ }
+
+ @Override
+ public void setSsidAllowlist(List<String> ssids) {
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(
+ isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ "SSID allowlist can only be controlled by a device owner or "
+ + "a profile owner on an organization-owned device.");
+
+ Collections.sort(ssids);
+ boolean changed = false;
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
+ if (!ssids.equals(admin.mSsidAllowlist)) {
+ admin.mSsidAllowlist = ssids;
+ admin.mSsidDenylist = null;
+ changed = true;
+ }
+ if (changed) saveSettingsLocked(caller.getUserId());
+ }
+ if (changed) validateCurrentWifiMeetsAdminRequirements();
+ }
+
+ @Override
+ public List<String> getSsidAllowlist() {
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(
+ isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)
+ || isSystemUid(caller),
+ "SSID allowlist can only be retrieved by a device owner or "
+ + "a profile owner on an organization-owned device or a system app.");
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+ UserHandle.USER_SYSTEM);
+ return (admin == null || admin.mSsidAllowlist == null) ? new ArrayList<>()
+ : admin.mSsidAllowlist;
+ }
+ }
+
+ @Override
+ public void setSsidDenylist(List<String> ssids) {
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(
+ isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ "SSID denylist can only be controlled by a device owner or "
+ + "a profile owner on an organization-owned device.");
+
+ Collections.sort(ssids);
+ boolean changed = false;
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
+ if (!ssids.equals(admin.mSsidDenylist)) {
+ admin.mSsidDenylist = ssids;
+ admin.mSsidAllowlist = null;
+ changed = true;
+ }
+ if (changed) saveSettingsLocked(caller.getUserId());
+ }
+ if (changed) validateCurrentWifiMeetsAdminRequirements();
+ }
+
+ @Override
+ public List<String> getSsidDenylist() {
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(
+ isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)
+ || isSystemUid(caller),
+ "SSID denylist can only be retrieved by a device owner or "
+ + "a profile owner on an organization-owned device or a system app.");
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+ UserHandle.USER_SYSTEM);
+ return (admin == null || admin.mSsidDenylist == null) ? new ArrayList<>()
+ : admin.mSsidDenylist;
+ }
+ }
+
@Override
public void setDrawables(@NonNull List<DevicePolicyDrawableResource> drawables) {
Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a7b7d1a..ad8753d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -54,6 +54,7 @@
import android.net.ConnectivityManager;
import android.net.ConnectivityModuleConnector;
import android.net.NetworkStackClient;
+import android.net.TrafficStats;
import android.os.BaseBundle;
import android.os.Binder;
import android.os.Build;
@@ -103,6 +104,7 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.ILockSettings;
import com.android.server.am.ActivityManagerService;
+import com.android.server.ambientcontext.AmbientContextManagerService;
import com.android.server.appbinding.AppBindingService;
import com.android.server.art.ArtManagerLocal;
import com.android.server.attention.AttentionManagerService;
@@ -1809,6 +1811,7 @@
startRotationResolverService(context, t);
startSystemCaptionsManagerService(context, t);
startTextToSpeechManagerService(context, t);
+ startAmbientContextService(t);
// System Speech Recognition Service
t.traceBegin("StartSpeechRecognitionManagerService");
@@ -1903,6 +1906,7 @@
try {
networkStats = NetworkStatsService.create(context);
ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
+ TrafficStats.init(context);
} catch (Throwable e) {
reportWtf("starting NetworkStats Service", e);
}
@@ -3160,6 +3164,17 @@
}
+ private void startAmbientContextService(@NonNull TimingsTraceAndSlog t) {
+ if (!AmbientContextManagerService.isDetectionServiceConfigured()) {
+ Slog.d(TAG, "AmbientContextDetectionService is not configured on this device");
+ return;
+ }
+
+ t.traceBegin("StartAmbientContextService");
+ mSystemServiceManager.startService(AmbientContextManagerService.class);
+ t.traceEnd();
+ }
+
private static void startSystemUi(Context context, WindowManagerService windowManager) {
PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
Intent intent = new Intent();
diff --git a/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java b/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java
index fe6af94..fec9b12 100644
--- a/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java
+++ b/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java
@@ -20,6 +20,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -29,11 +31,12 @@
import android.graphics.Bitmap;
import android.platform.test.annotations.Presubmit;
import android.service.games.GameSession.ScreenshotCallback;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.view.SurfaceControlViewHost;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.infra.AndroidFuture;
@@ -44,15 +47,18 @@
import org.mockito.Mock;
import org.mockito.MockitoSession;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Unit tests for the {@link android.service.games.GameSession}.
*/
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
@SmallTest
@Presubmit
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
public final class GameSessionTest {
private static final long WAIT_FOR_CALLBACK_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(1);
private static final Bitmap TEST_BITMAP = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
@@ -61,8 +67,7 @@
private IGameSessionController mMockGameSessionController;
@Mock
SurfaceControlViewHost mSurfaceControlViewHost;
- private GameSession mGameSession;
-
+ private LifecycleTrackingGameSession mGameSession;
private MockitoSession mMockitoSession;
@Before
@@ -71,7 +76,7 @@
.initMocks(this)
.startMocking();
- mGameSession = new GameSession() {};
+ mGameSession = new LifecycleTrackingGameSession() {};
mGameSession.attach(mMockGameSessionController, /* taskId= */ 10,
InstrumentationRegistry.getContext(),
mSurfaceControlViewHost,
@@ -191,4 +196,196 @@
assertTrue(countDownLatch.await(
WAIT_FOR_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
+
+ @Test
+ public void moveState_InitializedToInitialized_noLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.INITIALIZED);
+
+ assertThat(mGameSession.mLifecycleMethodCalls.isEmpty()).isTrue();
+ }
+
+ @Test
+ public void moveState_FullLifecycle_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.DESTROYED);
+
+ assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_FOCUSED,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_UNFOCUSED,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_DESTROY).inOrder();
+ }
+
+ @Test
+ public void moveState_DestroyedWhenInitialized_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.DESTROYED);
+
+ // ON_CREATE is always called before ON_DESTROY.
+ assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_DESTROY).inOrder();
+ }
+
+ @Test
+ public void moveState_DestroyedWhenFocused_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+ mGameSession.moveToState(GameSession.LifecycleState.DESTROYED);
+
+ // The ON_GAME_TASK_UNFOCUSED lifecycle event is implied because the session is destroyed
+ // while in focus.
+ assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_FOCUSED,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_UNFOCUSED,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_DESTROY).inOrder();
+ }
+
+ @Test
+ public void moveState_FocusCycled_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_UNFOCUSED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_UNFOCUSED);
+
+ // Both cycles from focus and unfocus are captured.
+ assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_FOCUSED,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_UNFOCUSED,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_FOCUSED,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_UNFOCUSED).inOrder();
+ }
+
+ @Test
+ public void moveState_MultipleFocusAndUnfocusCalls_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_UNFOCUSED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_UNFOCUSED);
+
+ // The second TASK_FOCUSED call and the second TASK_UNFOCUSED call are ignored.
+ assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_FOCUSED,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_UNFOCUSED).inOrder();
+ }
+
+ @Test
+ public void moveState_CreatedAfterFocused_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+
+ // The second CREATED call is ignored.
+ assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_FOCUSED).inOrder();
+ }
+
+ @Test
+ public void moveState_UnfocusedWithoutFocused_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_UNFOCUSED);
+
+ // The TASK_UNFOCUSED call without an earlier TASK_FOCUSED call is ignored.
+ assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE).inOrder();
+ }
+
+ @Test
+ public void moveState_NeverFocused_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.DESTROYED);
+
+ assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_DESTROY).inOrder();
+ }
+
+ @Test
+ public void moveState_MultipleFocusCalls_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+
+ // The extra TASK_FOCUSED moves are ignored.
+ assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_FOCUSED).inOrder();
+ }
+
+ @Test
+ public void moveState_MultipleCreateCalls_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+
+ // The extra CREATE moves are ignored.
+ assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE).inOrder();
+ }
+
+ @Test
+ public void moveState_FocusBeforeCreate_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+
+ // The TASK_FOCUSED move before CREATE is ignored.
+ assertThat(mGameSession.mLifecycleMethodCalls.isEmpty()).isTrue();
+ }
+
+ @Test
+ public void moveState_UnfocusBeforeCreate_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_UNFOCUSED);
+
+ // The TASK_UNFOCUSED move before CREATE is ignored.
+ assertThat(mGameSession.mLifecycleMethodCalls.isEmpty()).isTrue();
+ }
+
+ @Test
+ public void moveState_FocusWhenDestroyed_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.DESTROYED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+
+ // The TASK_FOCUSED move after DESTROYED is ignored.
+ assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_DESTROY).inOrder();
+ }
+
+ private static class LifecycleTrackingGameSession extends GameSession {
+ private enum LifecycleMethodCall {
+ ON_CREATE,
+ ON_DESTROY,
+ ON_GAME_TASK_FOCUSED,
+ ON_GAME_TASK_UNFOCUSED
+ }
+
+ final List<LifecycleMethodCall> mLifecycleMethodCalls = new ArrayList<>();
+
+ @Override
+ public void onCreate() {
+ mLifecycleMethodCalls.add(LifecycleMethodCall.ON_CREATE);
+ }
+
+ @Override
+ public void onDestroy() {
+ mLifecycleMethodCalls.add(LifecycleMethodCall.ON_DESTROY);
+ }
+
+ @Override
+ public void onGameTaskFocusChanged(boolean focused) {
+ if (focused) {
+ mLifecycleMethodCalls.add(LifecycleMethodCall.ON_GAME_TASK_FOCUSED);
+ } else {
+ mLifecycleMethodCalls.add(LifecycleMethodCall.ON_GAME_TASK_UNFOCUSED);
+ }
+ }
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index d6705a5..0198253 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -28,6 +29,7 @@
import android.Manifest;
import android.app.GameManager;
+import android.app.GameModeInfo;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
@@ -515,7 +517,7 @@
public void testDeviceConfigDefault() {
mockDeviceConfigDefault();
mockModifyGameModeGranted();
- checkReportedModes(null, GameManager.GAME_MODE_UNSUPPORTED);
+ checkReportedModes(null);
}
/**
@@ -525,7 +527,7 @@
public void testDeviceConfigNone() {
mockDeviceConfigNone();
mockModifyGameModeGranted();
- checkReportedModes(null, GameManager.GAME_MODE_UNSUPPORTED);
+ checkReportedModes(null);
}
/**
@@ -566,7 +568,7 @@
public void testDeviceConfigInvalid() {
mockDeviceConfigInvalid();
mockModifyGameModeGranted();
- checkReportedModes(null, GameManager.GAME_MODE_UNSUPPORTED);
+ checkReportedModes(null);
}
/**
@@ -576,7 +578,7 @@
public void testDeviceConfigMalformed() {
mockDeviceConfigMalformed();
mockModifyGameModeGranted();
- checkReportedModes(null, GameManager.GAME_MODE_UNSUPPORTED);
+ checkReportedModes(null);
}
/**
@@ -966,4 +968,84 @@
static {
System.loadLibrary("mockingservicestestjni");
}
+ @Test
+ public void testGetGameModeInfoPermissionDenied() {
+ mockDeviceConfigAll();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+
+ // Deny permission.MANAGE_GAME_MODE and verify the game mode is not updated.
+ mockModifyGameModeDenied();
+ assertThrows(SecurityException.class,
+ () -> gameManagerService.getGameModeInfo(mPackageName, USER_ID_1));
+ }
+
+ @Test
+ public void testGetGameModeInfoWithAllGameModesDefault() {
+ mockDeviceConfigAll();
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ GameModeInfo gameModeInfo = gameManagerService.getGameModeInfo(mPackageName, USER_ID_1);
+
+ assertEquals(GameManager.GAME_MODE_STANDARD, gameModeInfo.getActiveGameMode());
+ assertEquals(3, gameModeInfo.getAvailableGameModes().length);
+ }
+
+ @Test
+ public void testGetGameModeInfoWithAllGameModes() {
+ mockDeviceConfigAll();
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
+ GameModeInfo gameModeInfo = gameManagerService.getGameModeInfo(mPackageName, USER_ID_1);
+
+ assertEquals(GameManager.GAME_MODE_PERFORMANCE, gameModeInfo.getActiveGameMode());
+ assertEquals(3, gameModeInfo.getAvailableGameModes().length);
+ }
+
+ @Test
+ public void testGetGameModeInfoWithBatteryMode() {
+ mockDeviceConfigBattery();
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_BATTERY, USER_ID_1);
+ GameModeInfo gameModeInfo = gameManagerService.getGameModeInfo(mPackageName, USER_ID_1);
+
+ assertEquals(GameManager.GAME_MODE_BATTERY, gameModeInfo.getActiveGameMode());
+ assertEquals(2, gameModeInfo.getAvailableGameModes().length);
+ }
+
+ @Test
+ public void testGetGameModeInfoWithPerformanceMode() {
+ mockDeviceConfigPerformance();
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
+ GameModeInfo gameModeInfo = gameManagerService.getGameModeInfo(mPackageName, USER_ID_1);
+
+ assertEquals(GameManager.GAME_MODE_PERFORMANCE, gameModeInfo.getActiveGameMode());
+ assertEquals(2, gameModeInfo.getAvailableGameModes().length);
+ }
+
+ @Test
+ public void testGetGameModeInfoWithUnsupportedGameMode() {
+ mockDeviceConfigNone();
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ GameModeInfo gameModeInfo = gameManagerService.getGameModeInfo(mPackageName, USER_ID_1);
+
+ assertEquals(GameManager.GAME_MODE_UNSUPPORTED, gameModeInfo.getActiveGameMode());
+ assertEquals(0, gameModeInfo.getAvailableGameModes().length);
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
index aacd015..bdfa3bf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
@@ -34,6 +34,7 @@
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.app.ITaskStackListener;
import android.content.ComponentName;
@@ -331,6 +332,7 @@
.complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
assertThat(gameSession10.mIsDestroyed).isFalse();
+ assertThat(gameSession10.mIsFocused).isFalse();
}
@Test
@@ -365,6 +367,45 @@
}
@Test
+ public void gameTaskFocused_propagatedToGameSession() throws Exception {
+ mGameServiceProviderInstance.start();
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(10);
+
+ FakeGameSession gameSession10 = new FakeGameSession();
+ SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(10)
+ .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+ assertThat(gameSession10.mIsFocused).isFalse();
+
+ dispatchTaskFocused(10, /*focused=*/ true);
+ assertThat(gameSession10.mIsFocused).isTrue();
+
+ dispatchTaskFocused(10, /*focused=*/ false);
+ assertThat(gameSession10.mIsFocused).isFalse();
+ }
+
+ @Test
+ public void gameTaskAlreadyFocusedWhenGameSessionCreated_propagatedToGameSession()
+ throws Exception {
+ ActivityTaskManager.RootTaskInfo gameATaskInfo = new ActivityTaskManager.RootTaskInfo();
+ gameATaskInfo.taskId = 10;
+ when(mMockActivityTaskManager.getFocusedRootTaskInfo()).thenReturn(gameATaskInfo);
+
+ mGameServiceProviderInstance.start();
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(10);
+
+ FakeGameSession gameSession10 = new FakeGameSession();
+ SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(10)
+ .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+ assertThat(gameSession10.mIsFocused).isTrue();
+ }
+
+ @Test
public void gameTaskRemoved_whileAwaitingGameSessionAttached_destroysGameSession()
throws Exception {
mGameServiceProviderInstance.start();
@@ -647,6 +688,12 @@
});
}
+ private void dispatchTaskFocused(int taskId, boolean focused) {
+ dispatchTaskChangeEvent(taskStackListener -> {
+ taskStackListener.onTaskFocusChanged(taskId, focused);
+ });
+ }
+
private void dispatchTaskChangeEvent(
ThrowingConsumer<ITaskStackListener> taskStackListenerConsumer) {
for (ITaskStackListener taskStackListener : mTaskStackListeners) {
@@ -767,10 +814,16 @@
private static class FakeGameSession extends IGameSession.Stub {
boolean mIsDestroyed = false;
+ boolean mIsFocused = false;
@Override
- public void destroy() {
+ public void onDestroyed() {
mIsDestroyed = true;
}
+
+ @Override
+ public void onTaskFocusChanged(boolean focused) {
+ mIsFocused = focused;
+ }
}
}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 36c37c4..677f0f6 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -541,11 +541,14 @@
| ActivityManager.UID_OBSERVER_CAPABILITY
};
final IUidObserver[] observers = new IUidObserver.Stub[changesToObserve.length];
+ doReturn(Process.myUid()).when(sPackageManagerInternal)
+ .getPackageUid(mContext.getOpPackageName(), 0 /* flags */, mContext.getUserId());
for (int i = 0; i < observers.length; ++i) {
observers[i] = mock(IUidObserver.Stub.class);
when(observers[i].asBinder()).thenReturn((IBinder) observers[i]);
mAms.registerUidObserver(observers[i], changesToObserve[i] /* which */,
- ActivityManager.PROCESS_STATE_UNKNOWN /* cutpoint */, null /* caller */);
+ ActivityManager.PROCESS_STATE_UNKNOWN /* cutpoint */,
+ mContext.getOpPackageName());
// When we invoke AMS.registerUidObserver, there are some interactions with observers[i]
// mock in RemoteCallbackList class. We don't want to test those interactions and
@@ -674,10 +677,12 @@
mockNoteOperation();
final IUidObserver observer = mock(IUidObserver.Stub.class);
-
when(observer.asBinder()).thenReturn((IBinder) observer);
+ doReturn(Process.myUid()).when(sPackageManagerInternal)
+ .getPackageUid(mContext.getOpPackageName(), 0 /* flags */, mContext.getUserId());
mAms.registerUidObserver(observer, ActivityManager.UID_OBSERVER_PROCSTATE /* which */,
- ActivityManager.PROCESS_STATE_SERVICE /* cutpoint */, null /* callingPackage */);
+ ActivityManager.PROCESS_STATE_SERVICE /* cutpoint */,
+ mContext.getOpPackageName());
// When we invoke AMS.registerUidObserver, there are some interactions with observer
// mock in RemoteCallbackList class. We don't want to test those interactions and
// at the same time, we don't want those to interfere with verifyNoMoreInteractions.
@@ -771,7 +776,9 @@
final IUidObserver observer = mock(IUidObserver.Stub.class);
when(observer.asBinder()).thenReturn((IBinder) observer);
- mAms.registerUidObserver(observer, 0, 0, null);
+ doReturn(Process.myUid()).when(sPackageManagerInternal)
+ .getPackageUid(mContext.getOpPackageName(), 0 /* flags */, mContext.getUserId());
+ mAms.registerUidObserver(observer, 0, 0, mContext.getOpPackageName());
// Verify that when observers are registered, then validateUids is correctly updated.
addPendingUidChanges(pendingItemsForUids);
mAms.mUidObserverController.dispatchUidsChanged();
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 1228d62..8a5b16e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -18,7 +18,6 @@
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.app.AppOpsManager.OP_ACTIVATE_VPN;
-import static android.app.Notification.EXTRA_TEXT;
import static android.app.Notification.EXTRA_TITLE;
import static android.app.admin.DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE;
import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS;
@@ -34,6 +33,10 @@
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_SET_NO_ERROR;
+import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_ENTERPRISE_192;
+import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_ENTERPRISE_EAP;
+import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_OPEN;
+import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_PERSONAL;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.PasswordMetrics.computeForPasswordOrPin;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
@@ -91,6 +94,7 @@
import android.app.admin.FactoryResetProtectionPolicy;
import android.app.admin.PasswordMetrics;
import android.app.admin.SystemUpdatePolicy;
+import android.app.admin.WifiSsidPolicy;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Intent;
@@ -1112,6 +1116,10 @@
eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE),
eq(true), eq(UserHandle.SYSTEM));
+ verify(getServices().userManager, times(1)).setUserRestriction(
+ eq(UserManager.DISALLOW_ADD_CLONE_PROFILE),
+ eq(true), eq(UserHandle.SYSTEM));
+
verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
MockUtils.checkIntentAction(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED),
MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
@@ -1393,6 +1401,10 @@
eq(false),
MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
+ verify(getServices().userManager)
+ .setUserRestriction(eq(UserManager.DISALLOW_ADD_CLONE_PROFILE), eq(false),
+ MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
+
verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
eq(UserHandle.USER_SYSTEM), MockUtils.checkUserRestrictions(),
MockUtils.checkUserRestrictions(UserHandle.USER_SYSTEM), eq(true));
@@ -7209,8 +7221,7 @@
verify(getServices().alarmManager, times(1)).set(anyInt(), eq(PROFILE_OFF_DEADLINE), any());
// Now the user should see a warning notification.
verify(getServices().notificationManager, times(1))
- .notify(anyInt(), argThat(hasExtra(EXTRA_TITLE, PROFILE_OFF_SUSPENSION_TITLE,
- EXTRA_TEXT, PROFILE_OFF_SUSPENSION_SOON_TEXT)));
+ .notify(anyInt(), any());
// Apps shouldn't be suspended yet.
verifyZeroInteractions(getServices().ipackageManager);
clearInvocations(getServices().alarmManager);
@@ -7224,8 +7235,7 @@
verifyZeroInteractions(getServices().alarmManager);
// Now the user should see a notification about suspended apps.
verify(getServices().notificationManager, times(1))
- .notify(anyInt(), argThat(hasExtra(EXTRA_TITLE, PROFILE_OFF_SUSPENSION_TITLE,
- EXTRA_TEXT, PROFILE_OFF_SUSPENSION_TEXT)));
+ .notify(anyInt(), any());
// Verify that the apps are suspended.
verify(getServices().ipackageManager, times(1)).setPackagesSuspendedAsUser(
any(), eq(true), any(), any(), any(), any(), anyInt());
@@ -7828,6 +7838,128 @@
() -> dpm.getOrganizationNameForUser(UserHandle.USER_SYSTEM));
}
+ @Test
+ public void testSetWifiMinimumSecurity_noDeviceOwnerOrPoOfOrgOwnedDevice() {
+ assertThrows(SecurityException.class, () -> dpm.setMinimumRequiredWifiSecurityLevel(
+ DevicePolicyManager.WIFI_SECURITY_PERSONAL));
+ }
+
+ @Test
+ public void testSetWifiMinimumSecurity_asDeviceOwner() throws Exception {
+ setDeviceOwner();
+
+ final Set<Integer> allowedLevels = Set.of(WIFI_SECURITY_OPEN, WIFI_SECURITY_PERSONAL,
+ WIFI_SECURITY_ENTERPRISE_EAP, WIFI_SECURITY_ENTERPRISE_192);
+ for (int level : allowedLevels) {
+ dpm.setMinimumRequiredWifiSecurityLevel(level);
+ assertThat(dpm.getMinimumRequiredWifiSecurityLevel()).isEqualTo(level);
+ }
+ }
+
+ @Test
+ public void testSetWifiMinimumSecurity_asPoOfOrgOwnedDevice() throws Exception {
+ final int managedProfileUserId = 15;
+ final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
+ addManagedProfile(admin1, managedProfileAdminUid, admin1);
+ configureProfileOwnerOfOrgOwnedDevice(admin1, managedProfileUserId);
+ mContext.binder.callingUid = managedProfileAdminUid;
+
+ final Set<Integer> allowedLevels = Set.of(WIFI_SECURITY_OPEN, WIFI_SECURITY_PERSONAL,
+ WIFI_SECURITY_ENTERPRISE_EAP, WIFI_SECURITY_ENTERPRISE_192);
+ for (int level : allowedLevels) {
+ dpm.setMinimumRequiredWifiSecurityLevel(level);
+ assertThat(dpm.getMinimumRequiredWifiSecurityLevel()).isEqualTo(level);
+ }
+ }
+
+ @Test
+ public void testSetSsidAllowlist_noDeviceOwnerOrPoOfOrgOwnedDevice() {
+ final Set<String> ssids = Collections.singleton("ssid1");
+ WifiSsidPolicy policy = WifiSsidPolicy.createAllowlistPolicy(ssids);
+ assertThrows(SecurityException.class, () -> dpm.setWifiSsidPolicy(policy));
+ }
+
+ @Test
+ public void testSetSsidAllowlist_asDeviceOwner() throws Exception {
+ setDeviceOwner();
+
+ final Set<String> ssids = Collections.singleton("ssid1");
+ WifiSsidPolicy policy = WifiSsidPolicy.createAllowlistPolicy(ssids);
+ dpm.setWifiSsidPolicy(policy);
+ assertThat(dpm.getWifiSsidPolicy().getSsids()).isEqualTo(ssids);
+ assertThat(dpm.getWifiSsidPolicy().getPolicyType()).isEqualTo(
+ WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST);
+ }
+
+ @Test
+ public void testSetSsidAllowlist_asPoOfOrgOwnedDevice() throws Exception {
+ final int managedProfileUserId = 15;
+ final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
+ addManagedProfile(admin1, managedProfileAdminUid, admin1);
+ configureProfileOwnerOfOrgOwnedDevice(admin1, managedProfileUserId);
+ mContext.binder.callingUid = managedProfileAdminUid;
+
+ final Set<String> ssids = new ArraySet<>(Arrays.asList("ssid1", "ssid2", "ssid3"));
+ WifiSsidPolicy policy = WifiSsidPolicy.createAllowlistPolicy(ssids);
+ dpm.setWifiSsidPolicy(policy);
+ assertThat(dpm.getWifiSsidPolicy().getSsids()).isEqualTo(ssids);
+ assertThat(dpm.getWifiSsidPolicy().getPolicyType()).isEqualTo(
+ WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST);
+ }
+
+ @Test
+ public void testSetSsidAllowlist_emptyList() throws Exception {
+ setDeviceOwner();
+
+ final Set<String> ssids = new ArraySet<>();
+ assertThrows(IllegalArgumentException.class,
+ () -> WifiSsidPolicy.createAllowlistPolicy(ssids));
+ }
+
+ @Test
+ public void testSetSsidDenylist_noDeviceOwnerOrPoOfOrgOwnedDevice() {
+ final Set<String> ssids = Collections.singleton("ssid1");
+ WifiSsidPolicy policy = WifiSsidPolicy.createDenylistPolicy(ssids);
+ assertThrows(SecurityException.class, () -> dpm.setWifiSsidPolicy(policy));
+ }
+
+ @Test
+ public void testSetSsidDenylist_asDeviceOwner() throws Exception {
+ setDeviceOwner();
+
+ final Set<String> ssids = Collections.singleton("ssid1");
+ WifiSsidPolicy policy = WifiSsidPolicy.createDenylistPolicy(ssids);
+ dpm.setWifiSsidPolicy(policy);
+ assertThat(dpm.getWifiSsidPolicy().getSsids()).isEqualTo(ssids);
+ assertThat(dpm.getWifiSsidPolicy().getPolicyType()).isEqualTo(
+ WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST);
+ }
+
+ @Test
+ public void testSetSsidDenylist_asPoOfOrgOwnedDevice() throws Exception {
+ final int managedProfileUserId = 15;
+ final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
+ addManagedProfile(admin1, managedProfileAdminUid, admin1);
+ configureProfileOwnerOfOrgOwnedDevice(admin1, managedProfileUserId);
+ mContext.binder.callingUid = managedProfileAdminUid;
+
+ final Set<String> ssids = new ArraySet<>(Arrays.asList("ssid1", "ssid2", "ssid3"));
+ WifiSsidPolicy policy = WifiSsidPolicy.createDenylistPolicy(ssids);
+ dpm.setWifiSsidPolicy(policy);
+ assertThat(dpm.getWifiSsidPolicy().getSsids()).isEqualTo(ssids);
+ assertThat(dpm.getWifiSsidPolicy().getPolicyType()).isEqualTo(
+ WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST);
+ }
+
+ @Test
+ public void testSetSsidDenylist_emptyList() throws Exception {
+ setDeviceOwner();
+
+ final Set<String> ssids = new ArraySet<>();
+ assertThrows(IllegalArgumentException.class,
+ () -> WifiSsidPolicy.createDenylistPolicy(ssids));
+ }
+
private void setupVpnAuthorization(String userVpnPackage, int userVpnUid) {
final AppOpsManager.PackageOps vpnOp = new AppOpsManager.PackageOps(userVpnPackage,
userVpnUid, List.of(new AppOpsManager.OpEntry(
@@ -7863,18 +7995,6 @@
// To allow creation of Notification via Notification.Builder
mContext.applicationInfo = mRealTestContext.getApplicationInfo();
- // Setup resources to render notification titles and texts.
- when(mServiceContext.resources
- .getString(R.string.personal_apps_suspension_title))
- .thenReturn(PROFILE_OFF_SUSPENSION_TITLE);
- when(mServiceContext.resources
- .getString(R.string.personal_apps_suspension_text))
- .thenReturn(PROFILE_OFF_SUSPENSION_TEXT);
- when(mServiceContext.resources
- .getString(eq(R.string.personal_apps_suspension_soon_text),
- anyString(), anyString(), anyInt()))
- .thenReturn(PROFILE_OFF_SUSPENSION_SOON_TEXT);
-
// Make locale available for date formatting:
when(mServiceContext.resources.getConfiguration())
.thenReturn(mRealTestContext.getResources().getConfiguration());
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index d4b1165..6eb2085 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -232,6 +232,8 @@
return mMockSystemServices.crossProfileApps;
case Context.VPN_MANAGEMENT_SERVICE:
return mMockSystemServices.vpnManager;
+ case Context.DEVICE_POLICY_SERVICE:
+ return mMockSystemServices.devicePolicyManager;
}
throw new UnsupportedOperationException();
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 8a2919d..597a165 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -31,6 +31,7 @@
import android.app.IActivityManager;
import android.app.IActivityTaskManager;
import android.app.NotificationManager;
+import android.app.admin.DevicePolicyManager;
import android.app.backup.IBackupManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
@@ -125,6 +126,7 @@
public final AppOpsManager appOpsManager;
public final UsbManager usbManager;
public final VpnManager vpnManager;
+ public final DevicePolicyManager devicePolicyManager;
/** Note this is a partial mock, not a real mock. */
public final PackageManager packageManager;
public final BuildMock buildMock = new BuildMock();
@@ -172,6 +174,7 @@
appOpsManager = mock(AppOpsManager.class);
usbManager = mock(UsbManager.class);
vpnManager = mock(VpnManager.class);
+ devicePolicyManager = mock(DevicePolicyManager.class);
// Package manager is huge, so we use a partial mock instead.
packageManager = spy(realContext.getPackageManager());
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 c544f5c..81c9871 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -2046,7 +2046,7 @@
private static NetworkStateSnapshot buildWifi() {
WifiInfo mockWifiInfo = mock(WifiInfo.class);
when(mockWifiInfo.makeCopy(anyLong())).thenReturn(mockWifiInfo);
- when(mockWifiInfo.getCurrentNetworkKey()).thenReturn(TEST_WIFI_NETWORK_KEY);
+ when(mockWifiInfo.getNetworkKey()).thenReturn(TEST_WIFI_NETWORK_KEY);
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_IFACE);
final NetworkCapabilities networkCapabilities = new NetworkCapabilities.Builder()
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 429445f..7e5fe04 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -664,6 +664,24 @@
}
}
+ // Make sure createProfile would fail if we have DISALLOW_ADD_CLONE_PROFILE.
+ @MediumTest
+ @Test
+ public void testCreateUser_disallowAddClonedUserProfile() throws Exception {
+ final int primaryUserId = ActivityManager.getCurrentUser();
+ final UserHandle primaryUserHandle = asHandle(primaryUserId);
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE,
+ true, primaryUserHandle);
+ try {
+ UserInfo cloneProfileUserInfo = createProfileForUser("Clone",
+ UserManager.USER_TYPE_PROFILE_CLONE, primaryUserId);
+ assertThat(cloneProfileUserInfo).isNull();
+ } finally {
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE, false,
+ primaryUserHandle);
+ }
+ }
+
// Make sure createProfile would fail if we have DISALLOW_ADD_MANAGED_PROFILE.
@MediumTest
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 0dcf799..774e5b9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -24,7 +24,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
@@ -571,7 +570,7 @@
final ActivityRecord activity = createActivityWith2LevelTask();
final Task task = activity.getTask();
final Task rootTask = activity.getRootTask();
- rootTask.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
final Rect stableRect = new Rect();
rootTask.mDisplayContent.getStableRect(stableRect);
@@ -616,7 +615,7 @@
spyOn(tda);
doReturn(true).when(tda).supportsNonResizableMultiWindow();
final Task rootTask = mDisplayContent.getDefaultTaskDisplayArea().createRootTask(
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, true /* onTop */);
rootTask.setBounds(0, 0, 1000, 500);
final ActivityRecord activity = new ActivityBuilder(mAtm)
.setParentTask(rootTask)
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index a2b04c2..7c340ec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -77,6 +77,7 @@
import org.mockito.MockitoSession;
import java.util.ArrayList;
+import java.util.List;
import java.util.function.Consumer;
/**
@@ -188,6 +189,9 @@
@Override
public void onFixedRotationFinished(int displayId) {}
+
+ @Override
+ public void onKeepClearAreasChanged(int displayId, List<Rect> keepClearAreas) {}
};
int[] displayIds = mAtm.mWindowManager.registerDisplayWindowListener(listener);
for (int i = 0; i < displayIds.length; i++) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 2f78b58..8d58ec0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -119,6 +119,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
@@ -1101,7 +1102,7 @@
final DisplayContent dc = createNewDisplay();
dc.setImeLayeringTarget(createWindow(null, TYPE_STATUS_BAR, "app"));
dc.getImeTarget(IME_TARGET_LAYERING).getWindow().setWindowingMode(
- WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent());
}
@@ -1152,7 +1153,7 @@
final DisplayContent dc = createNewDisplay();
dc.setImeInputTarget(createWindow(null, TYPE_BASE_APPLICATION, "app"));
dc.getImeTarget(IME_TARGET_INPUT).getWindow().setWindowingMode(
- WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
dc.setImeLayeringTarget(dc.getImeTarget(IME_TARGET_INPUT).getWindow());
dc.setRemoteInsetsController(createDisplayWindowInsetsController());
assertNotEquals(dc.getImeTarget(IME_TARGET_INPUT).getWindow(),
@@ -1982,6 +1983,7 @@
// Test step 1: appWin1 is the current IME target and soft-keyboard is visible.
mDisplayContent.computeImeTarget(true);
assertEquals(appWin1, mDisplayContent.getImeTarget(IME_TARGET_LAYERING));
+ mDisplayContent.setImeInputTarget(appWin1);
spyOn(mDisplayContent.mInputMethodWindow);
doReturn(true).when(mDisplayContent.mInputMethodWindow).isVisible();
mDisplayContent.getInsetsStateController().getImeSourceProvider().setImeShowing(true);
@@ -1998,7 +2000,6 @@
// be shown at this time.
final Transaction t = mDisplayContent.getPendingTransaction();
spyOn(t);
- mDisplayContent.setImeInputTarget(appWin2);
mDisplayContent.computeImeTarget(true);
assertEquals(appWin2, mDisplayContent.getImeTarget(IME_TARGET_LAYERING));
assertTrue(mDisplayContent.shouldImeAttachedToApp());
@@ -2436,6 +2437,31 @@
mockSession.finishMocking();
}
+ @Test
+ public void testKeepClearAreasMultipleWindows() {
+ final WindowState w1 = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent, "w1");
+ final Rect rect1 = new Rect(0, 0, 10, 10);
+ w1.setKeepClearAreas(Arrays.asList(rect1));
+ final WindowState w2 = createWindow(null, TYPE_NOTIFICATION_SHADE, mDisplayContent, "w2");
+ final Rect rect2 = new Rect(10, 10, 20, 20);
+ w2.setKeepClearAreas(Arrays.asList(rect2));
+
+ // No keep clear areas on display, because the windows are not visible
+ assertEquals(Arrays.asList(), mDisplayContent.getKeepClearAreas());
+
+ makeWindowVisible(w1);
+
+ // The returned keep-clear areas contain the areas just from the visible window
+ assertEquals(new ArraySet(Arrays.asList(rect1)),
+ new ArraySet(mDisplayContent.getKeepClearAreas()));
+
+ makeWindowVisible(w1, w2);
+
+ // The returned keep-clear areas contain the areas from all visible windows
+ assertEquals(new ArraySet(Arrays.asList(rect1, rect2)),
+ new ArraySet(mDisplayContent.getKeepClearAreas()));
+ }
+
private class TestToken extends Binder {
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index acf6dc5..497ae1d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -31,10 +31,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.spy;
@@ -44,7 +42,6 @@
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.util.Pair;
-import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.InsetsState;
import android.view.PrivacyIndicatorBounds;
@@ -210,24 +207,6 @@
expectThrows(IllegalArgumentException.class, () -> addWindow(win2));
}
- @Test
- public void layoutHint_appWindow() {
- mWindow.mAttrs.setFitInsetsTypes(0);
-
- final DisplayCutout.ParcelableWrapper outDisplayCutout =
- new DisplayCutout.ParcelableWrapper();
- final InsetsState outState = new InsetsState();
-
- mDisplayPolicy.getLayoutHint(mWindow.mAttrs, null /* windowToken */, outState,
- true /* localClient */);
-
- assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
- assertThat(outState.getSource(ITYPE_STATUS_BAR).getFrame(),
- is(new Rect(0, 0, DISPLAY_WIDTH, STATUS_BAR_HEIGHT)));
- assertThat(outState.getSource(ITYPE_NAVIGATION_BAR).getFrame(),
- is(new Rect(0, DISPLAY_HEIGHT - NAV_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT)));
- }
-
/**
* Verify that {@link DisplayPolicy#simulateLayoutDisplay} outputs the same display frames as
* the real one.
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 94bc7f2..2eece4c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -19,7 +19,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_IME;
@@ -246,7 +245,7 @@
final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
app.mAboveInsetsState.addSource(getController().getRawInsetsState().peekSource(ITYPE_IME));
child.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
- child.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ child.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
mDisplayContent.computeImeTarget(true);
mDisplayContent.setLayoutNeeded();
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
index fc298b0..0c2de5c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
@@ -20,7 +20,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.view.Display.INVALID_DISPLAY;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -334,7 +333,7 @@
params.mWindowingMode = windowingMode;
final InstrumentedPositioner positioner = new InstrumentedPositioner(RESULT_DONE, params);
final Task task = new TaskBuilder(mAtm.mTaskSupervisor).setCreateParentTask(true).build();
- task.getRootTask().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ task.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
mController.registerModifier(positioner);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index 3cb0bed..65b5cf5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -88,6 +88,7 @@
import androidx.test.filters.SmallTest;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -529,6 +530,7 @@
// TODO(b/199236198): check this is unnecessary or need to migrate after remove legacy split.
@Test
+ @Ignore
public void testShouldBeVisible_SplitScreen() {
// task not supporting split should be fullscreen for this test.
final Task notSupportingSplitTask = createTaskForShouldBeVisibleTest(
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 4069f0f..f4abf88 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -21,8 +21,8 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.TYPE_VIRTUAL;
@@ -458,7 +458,7 @@
final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
.getDefaultTaskDisplayArea();
final Task task = defaultTaskDisplayArea.createRootTask(
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, true /* onTop */);
final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
// Created tasks are focusable by default.
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index c722b0a..b815c38 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -77,6 +77,7 @@
import android.content.pm.ActivityInfo.ScreenOrientation;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
@@ -2182,6 +2183,29 @@
.computeAspectRatio(sizeCompatAppBounds), delta);
}
+ @Test
+ public void testClearSizeCompat_resetOverrideConfig() {
+ final int origDensity = 480;
+ final int newDensity = 520;
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, 600, 800)
+ .setDensityDpi(origDensity)
+ .build();
+ setUpApp(display);
+ prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+
+ // Activity should enter size compat with old density after display density change.
+ display.setForcedDensity(newDensity, UserHandle.USER_CURRENT);
+
+ assertScaled();
+ assertEquals(origDensity, mActivity.getConfiguration().densityDpi);
+
+ // Activity should exit size compat with new density.
+ mActivity.clearSizeCompatMode();
+
+ assertFitted();
+ assertEquals(newDensity, mActivity.getConfiguration().densityDpi);
+ }
+
private void assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity(
float letterboxHorizontalPositionMultiplier) {
// Set up a display in landscape and ignoring orientation request.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index cdf6b59..80f6bce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -25,8 +25,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
@@ -355,14 +353,10 @@
true /* reuseCandidate */);
assertGetOrCreateRootTask(WINDOWING_MODE_UNDEFINED, type, candidateTask,
true /* reuseCandidate */);
- assertGetOrCreateRootTask(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, type, candidateTask,
- true /* reuseCandidate */);
assertGetOrCreateRootTask(WINDOWING_MODE_FREEFORM, type, candidateTask,
true /* reuseCandidate */);
assertGetOrCreateRootTask(WINDOWING_MODE_MULTI_WINDOW, type, candidateTask,
true /* reuseCandidate */);
- assertGetOrCreateRootTask(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, type, candidateTask,
- false /* reuseCandidate */);
assertGetOrCreateRootTask(WINDOWING_MODE_PINNED, type, candidateTask,
true /* reuseCandidate */);
@@ -388,7 +382,7 @@
final Task primarySplitTask = new TaskBuilder(mSupervisor)
.setTaskDisplayArea(defaultTaskDisplayArea)
- .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
+ .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW)
.setActivityType(ACTIVITY_TYPE_STANDARD)
.setOnTop(true)
.setCreateActivity(true)
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
index f138475..64959f2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
@@ -17,8 +17,7 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -50,11 +49,8 @@
@Test
public void testDockedDividerPosition() {
final WindowState splitScreenWindow = createWindow(null,
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
+ WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
mDisplayContent, "splitScreenWindow");
- final WindowState splitScreenSecondaryWindow = createWindow(null,
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
- TYPE_BASE_APPLICATION, mDisplayContent, "splitScreenSecondaryWindow");
mDisplayContent.setImeLayeringTarget(splitScreenWindow);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 75a87ba..4d5fb6d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -24,8 +24,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
@@ -520,16 +518,16 @@
DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- dc, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null);
+ dc, WINDOWING_MODE_FULLSCREEN, null);
RunningTaskInfo info1 = task1.getTaskInfo();
- assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
+ assertEquals(WINDOWING_MODE_FULLSCREEN,
info1.configuration.windowConfiguration.getWindowingMode());
assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType);
Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- dc, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
+ dc, WINDOWING_MODE_MULTI_WINDOW, null);
RunningTaskInfo info2 = task2.getTaskInfo();
- assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW,
info2.configuration.windowConfiguration.getWindowingMode());
assertEquals(ACTIVITY_TYPE_UNDEFINED, info2.topActivityType);
@@ -539,7 +537,7 @@
assertTrue(mWm.mAtmService.mTaskOrganizerController.deleteRootTask(info1.token));
infos = getTasksCreatedByOrganizer(dc);
assertEquals(1, infos.size());
- assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, infos.get(0).getWindowingMode());
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, infos.get(0).getWindowingMode());
}
@Test
@@ -577,7 +575,7 @@
final StubOrganizer listener = new StubOrganizer();
mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
+ mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
RunningTaskInfo info1 = task.getTaskInfo();
final Task rootTask = createTask(
@@ -626,7 +624,7 @@
};
mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
+ mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
RunningTaskInfo info1 = task.getTaskInfo();
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
@@ -684,10 +682,10 @@
mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null);
+ mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
RunningTaskInfo info1 = task1.getTaskInfo();
Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
+ mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
RunningTaskInfo info2 = task2.getTaskInfo();
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
@@ -1056,7 +1054,7 @@
public void testReparentToOrganizedTask() {
final ITaskOrganizer organizer = registerMockOrganizer();
Task rootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null);
+ mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
final Task task1 = createRootTask();
final Task task2 = createTask(rootTask, false /* fakeDraw */);
WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -1223,7 +1221,7 @@
final Task rootTask = activity.getRootTask();
rootTask.setResizeMode(activity.info.resizeMode);
final Task splitPrimaryRootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null);
+ mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(),
splitPrimaryRootTask.mRemoteToken.toWindowContainerToken(), true /* onTop */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index ec8ec2b..80192f7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -20,7 +20,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
@@ -83,6 +82,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
import android.view.Gravity;
import android.view.InputWindowHandle;
import android.view.InsetsSource;
@@ -265,22 +265,19 @@
assertFalse(appWindow.canBeImeTarget());
assertFalse(imeWindow.canBeImeTarget());
- // Simulate the window is in split screen primary root task and the current state is
- // minimized and home root task is resizable, so that we should ignore input for the
- // root task.
+ // Simulate the window is in split screen root task.
final DockedTaskDividerController controller =
mDisplayContent.getDockedDividerController();
final Task rootTask = createTask(mDisplayContent,
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+ WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
spyOn(appWindow);
spyOn(controller);
spyOn(rootTask);
rootTask.setFocusable(false);
doReturn(rootTask).when(appWindow).getRootTask();
- // Make sure canBeImeTarget is false due to shouldIgnoreInput is true;
+ // Make sure canBeImeTarget is false;
assertFalse(appWindow.canBeImeTarget());
- assertTrue(rootTask.shouldIgnoreInput());
}
@Test
@@ -727,8 +724,9 @@
@Test
public void testCantReceiveTouchWhenNotFocusable() {
final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
- win0.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- win0.mActivityRecord.getRootTask().setFocusable(false);
+ final Task rootTask = win0.mActivityRecord.getRootTask();
+ spyOn(rootTask);
+ when(rootTask.shouldIgnoreInput()).thenReturn(true);
assertFalse(win0.canReceiveTouchInput());
}
@@ -928,8 +926,8 @@
mDisplayContent.setImeLayeringTarget(mAppWindow);
// Simulate entering multi-window mode and verify if the IME control target is remote.
- app.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, app.getWindowingMode());
+ app.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, app.getWindowingMode());
assertEquals(mDisplayContent.mRemoteInsetsControlTarget,
mDisplayContent.computeImeControlTarget());
@@ -944,14 +942,13 @@
@UseTestDisplay(addWindows = { W_ACTIVITY, W_INPUT_METHOD, W_NOTIFICATION_SHADE })
@Test
- public void testNotificationShadeHasImeInsetsWhenSplitscreenActivated() {
+ public void testNotificationShadeHasImeInsetsWhenMultiWindow() {
WindowState app = createWindow(null, TYPE_BASE_APPLICATION,
mAppWindow.mToken, "app");
- // Simulate entering multi-window mode and verify if the split-screen is activated.
- app.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, app.getWindowingMode());
- assertTrue(mDisplayContent.getDefaultTaskDisplayArea().isSplitScreenModeActivated());
+ // Simulate entering multi-window mode and windowing mode is multi-window.
+ app.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, app.getWindowingMode());
// Simulate notificationShade is shown and being IME layering target.
mNotificationShadeWindow.setHasSurface(true);
@@ -965,7 +962,7 @@
mDisplayContent.getInsetsStateController().getRawInsetsState()
.setSourceVisible(ITYPE_IME, true);
- // Verify notificationShade can still get IME insets even the split-screen is activated.
+ // Verify notificationShade can still get IME insets even windowing mode is multi-window.
InsetsState state = mDisplayContent.getInsetsStateController().getInsetsForWindow(
mNotificationShadeWindow);
assertNotNull(state.peekSource(ITYPE_IME));
@@ -986,4 +983,40 @@
assertFalse(app.isVisible());
assertTrue(app.isVisibleRequested());
}
+
+ @Test
+ public void testKeepClearAreas() {
+ final WindowState window = createWindow(null, TYPE_APPLICATION, "window");
+ makeWindowVisible(window);
+
+ final Rect keepClearArea1 = new Rect(0, 0, 10, 10);
+ final Rect keepClearArea2 = new Rect(5, 10, 15, 20);
+ final List<Rect> keepClearAreas = Arrays.asList(keepClearArea1, keepClearArea2);
+ window.setKeepClearAreas(keepClearAreas);
+
+ // Test that the keep-clear rects are stored and returned
+ assertEquals(new ArraySet(keepClearAreas), new ArraySet(window.getKeepClearAreas()));
+
+ // Test that keep-clear rects are overwritten
+ window.setKeepClearAreas(Arrays.asList());
+ assertEquals(0, window.getKeepClearAreas().size());
+
+ // Move the window position
+ final SurfaceControl.Transaction t = spy(StubTransaction.class);
+ window.mSurfaceControl = mock(SurfaceControl.class);
+ final Rect frame = window.getFrame();
+ frame.set(10, 20, 60, 80);
+ window.updateSurfacePosition(t);
+ assertEquals(new Point(frame.left, frame.top), window.mLastSurfacePosition);
+
+ // Test that the returned keep-clear rects are translated to display space
+ window.setKeepClearAreas(keepClearAreas);
+ Rect expectedArea1 = new Rect(keepClearArea1);
+ expectedArea1.offset(frame.left, frame.top);
+ Rect expectedArea2 = new Rect(keepClearArea2);
+ expectedArea2.offset(frame.left, frame.top);
+
+ assertEquals(new ArraySet(Arrays.asList(expectedArea1, expectedArea2)),
+ new ArraySet(window.getKeepClearAreas()));
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 4dffe7e..0f223ca 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -22,7 +22,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
@@ -363,7 +362,7 @@
ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent,
"pinnedStackWindow");
final WindowState dockedStackWindow = createWindow(null,
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
+ WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
mDisplayContent, "dockedStackWindow");
final WindowState assistantStackWindow = createWindow(null,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
diff --git a/services/usb/Android.bp b/services/usb/Android.bp
index 01feacd..3b50fa4 100644
--- a/services/usb/Android.bp
+++ b/services/usb/Android.bp
@@ -29,6 +29,7 @@
"android.hardware.usb-V1.1-java",
"android.hardware.usb-V1.2-java",
"android.hardware.usb-V1.3-java",
+ "android.hardware.usb-V1-java",
"android.hardware.usb.gadget-V1.0-java",
"android.hardware.usb.gadget-V1.1-java",
"android.hardware.usb.gadget-V1.2-java",
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
index 9d4db00..85b1de5 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
@@ -41,6 +41,7 @@
private final boolean mIsInputHeadset;
private final boolean mIsOutputHeadset;
+ private final boolean mIsDock;
private boolean mSelected = false;
private int mOutputState;
@@ -53,7 +54,7 @@
public UsbAlsaDevice(IAudioService audioService, int card, int device, String deviceAddress,
boolean hasOutput, boolean hasInput,
- boolean isInputHeadset, boolean isOutputHeadset) {
+ boolean isInputHeadset, boolean isOutputHeadset, boolean isDock) {
mAudioService = audioService;
mCardNum = card;
mDeviceNum = device;
@@ -62,31 +63,32 @@
mHasInput = hasInput;
mIsInputHeadset = isInputHeadset;
mIsOutputHeadset = isOutputHeadset;
+ mIsDock = isDock;
}
/**
- * @returns the ALSA card number associated with this peripheral.
+ * @return the ALSA card number associated with this peripheral.
*/
public int getCardNum() {
return mCardNum;
}
/**
- * @returns the ALSA device number associated with this peripheral.
+ * @return the ALSA device number associated with this peripheral.
*/
public int getDeviceNum() {
return mDeviceNum;
}
/**
- * @returns the USB device device address associated with this peripheral.
+ * @return the USB device device address associated with this peripheral.
*/
public String getDeviceAddress() {
return mDeviceAddress;
}
/**
- * @returns the ALSA card/device address string.
+ * @return the ALSA card/device address string.
*/
public String getAlsaCardDeviceString() {
if (mCardNum < 0 || mDeviceNum < 0) {
@@ -98,35 +100,42 @@
}
/**
- * @returns true if the device supports output.
+ * @return true if the device supports output.
*/
public boolean hasOutput() {
return mHasOutput;
}
/**
- * @returns true if the device supports input (recording).
+ * @return true if the device supports input (recording).
*/
public boolean hasInput() {
return mHasInput;
}
/**
- * @returns true if the device is a headset for purposes of input.
+ * @return true if the device is a headset for purposes of input.
*/
public boolean isInputHeadset() {
return mIsInputHeadset;
}
/**
- * @returns true if the device is a headset for purposes of output.
+ * @return true if the device is a headset for purposes of output.
*/
public boolean isOutputHeadset() {
return mIsOutputHeadset;
}
/**
- * @returns true if input jack is detected or jack detection is not supported.
+ * @return true if the device is a USB dock.
+ */
+ public boolean isDock() {
+ return mIsDock;
+ }
+
+ /**
+ * @return true if input jack is detected or jack detection is not supported.
*/
private synchronized boolean isInputJackConnected() {
if (mJackDetector == null) {
@@ -136,7 +145,7 @@
}
/**
- * @returns true if input jack is detected or jack detection is not supported.
+ * @return true if input jack is detected or jack detection is not supported.
*/
private synchronized boolean isOutputJackConnected() {
if (mJackDetector == null) {
@@ -190,9 +199,10 @@
try {
// Output Device
if (mHasOutput) {
- int device = mIsOutputHeadset
- ? AudioSystem.DEVICE_OUT_USB_HEADSET
- : AudioSystem.DEVICE_OUT_USB_DEVICE;
+ int device = mIsDock ? AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET
+ : (mIsOutputHeadset
+ ? AudioSystem.DEVICE_OUT_USB_HEADSET
+ : AudioSystem.DEVICE_OUT_USB_DEVICE);
if (DEBUG) {
Slog.d(TAG, "pre-call device:0x" + Integer.toHexString(device)
+ " addr:" + alsaCardDeviceString
@@ -231,7 +241,7 @@
/**
* @Override
- * @returns a string representation of the object.
+ * @return a string representation of the object.
*/
public synchronized String toString() {
return "UsbAlsaDevice: [card: " + mCardNum
@@ -273,7 +283,7 @@
/**
* @Override
- * @returns true if the objects are equivalent.
+ * @return true if the objects are equivalent.
*/
public boolean equals(Object obj) {
if (!(obj instanceof UsbAlsaDevice)) {
@@ -285,12 +295,13 @@
&& mHasOutput == other.mHasOutput
&& mHasInput == other.mHasInput
&& mIsInputHeadset == other.mIsInputHeadset
- && mIsOutputHeadset == other.mIsOutputHeadset);
+ && mIsOutputHeadset == other.mIsOutputHeadset
+ && mIsDock == other.mIsDock);
}
/**
* @Override
- * @returns a hash code generated from the object contents.
+ * @return a hash code generated from the object contents.
*/
public int hashCode() {
final int prime = 31;
@@ -301,6 +312,7 @@
result = prime * result + (mHasInput ? 0 : 1);
result = prime * result + (mIsInputHeadset ? 0 : 1);
result = prime * result + (mIsOutputHeadset ? 0 : 1);
+ result = prime * result + (mIsDock ? 0 : 1);
return result;
}
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 1c72eb8..fd9b995 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -237,6 +237,7 @@
if (hasInput || hasOutput) {
boolean isInputHeadset = parser.isInputHeadset();
boolean isOutputHeadset = parser.isOutputHeadset();
+ boolean isDock = parser.isDock();
if (mAudioService == null) {
Slog.e(TAG, "no AudioService");
@@ -246,7 +247,7 @@
UsbAlsaDevice alsaDevice =
new UsbAlsaDevice(mAudioService, cardRec.getCardNum(), 0 /*device*/,
deviceAddress, hasOutput, hasInput,
- isInputHeadset, isOutputHeadset);
+ isInputHeadset, isOutputHeadset, isDock);
if (alsaDevice != null) {
alsaDevice.setDeviceNameAndDescription(
cardRec.getCardName(), cardRec.getCardDescription());
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 9ac270f..94cc826 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -165,7 +165,7 @@
pw.println("manfacturer:0x" + Integer.toHexString(deviceDescriptor.getVendorID())
+ " product:" + Integer.toHexString(deviceDescriptor.getProductID()));
pw.println("isHeadset[in: " + parser.isInputHeadset()
- + " , out: " + parser.isOutputHeadset() + "]");
+ + " , out: " + parser.isOutputHeadset() + "], isDock: " + parser.isDock());
} else {
pw.println(formatTime() + " Disconnect " + mDeviceAddress);
}
@@ -179,9 +179,8 @@
UsbDescriptorsTree descriptorTree = new UsbDescriptorsTree();
descriptorTree.parse(parser);
descriptorTree.report(new TextReportCanvas(parser, stringBuilder));
-
stringBuilder.append("isHeadset[in: " + parser.isInputHeadset()
- + " , out: " + parser.isOutputHeadset() + "]");
+ + " , out: " + parser.isOutputHeadset() + "], isDock: " + parser.isDock());
pw.println(stringBuilder.toString());
} else {
pw.println(formatTime() + " Disconnect " + mDeviceAddress);
@@ -198,9 +197,8 @@
descriptor.report(canvas);
}
pw.println(stringBuilder.toString());
-
pw.println("isHeadset[in: " + parser.isInputHeadset()
- + " , out: " + parser.isOutputHeadset() + "]");
+ + " , out: " + parser.isOutputHeadset() + "], isDock: " + parser.isDock());
} else {
pw.println(formatTime() + " Disconnect " + mDeviceAddress);
}
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index ec28040..98173ad 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -16,6 +16,8 @@
package com.android.server.usb;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_PORT_MISMATCH;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL;
import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
import static android.hardware.usb.UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
@@ -25,6 +27,12 @@
import static android.hardware.usb.UsbPortStatus.MODE_UFP;
import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_POWER_ROLE_SOURCE;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_POWER_ROLE_SINK;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_DATA_ROLE_HOST;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_DATA_ROLE_DEVICE;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_MODE_DFP;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_MODE_UFP;
import static com.android.internal.usb.DumpUtils.writePort;
import static com.android.internal.usb.DumpUtils.writePortStatus;
@@ -38,6 +46,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
+import android.hardware.usb.IUsbOperationInternal;
import android.hardware.usb.ParcelableUsbPort;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
@@ -74,9 +83,13 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.FgThread;
+import com.android.server.usb.hal.port.RawPortInfo;
+import com.android.server.usb.hal.port.UsbPortHal;
+import com.android.server.usb.hal.port.UsbPortHalInstance;
import java.util.ArrayList;
import java.util.NoSuchElementException;
+import java.util.Objects;
/**
* Allows trusted components to control the properties of physical USB ports
@@ -109,16 +122,9 @@
// The system context.
private final Context mContext;
- // Proxy object for the usb hal daemon.
- @GuardedBy("mLock")
- private IUsb mProxy = null;
-
// Callback when the UsbPort status is changed by the kernel.
// Mostly due a command sent by the remote Usb device.
- private HALCallback mHALCallback = new HALCallback(null, this);
-
- // Cookie sent for usb hal death notification.
- private static final int USB_HAL_DEATH_COOKIE = 1000;
+ //private HALCallback mHALCallback = new HALCallback(null, this);
// Used as the key while sending the bundle to Main thread.
private static final String PORT_INFO = "port_info";
@@ -156,36 +162,23 @@
*/
private int mIsPortContaminatedNotificationId;
- private boolean mEnableUsbDataSignaling;
- protected int mCurrentUsbHalVersion;
+ private UsbPortHal mUsbPortHal;
+
+ private long mTransactionId;
public UsbPortManager(Context context) {
mContext = context;
- try {
- ServiceNotification serviceNotification = new ServiceNotification();
-
- boolean ret = IServiceManager.getService()
- .registerForNotifications("android.hardware.usb@1.0::IUsb",
- "", serviceNotification);
- if (!ret) {
- logAndPrint(Log.ERROR, null,
- "Failed to register service start notification");
- }
- } catch (RemoteException e) {
- logAndPrintException(null,
- "Failed to register service start notification", e);
- return;
- }
- connectToProxy(null);
+ mUsbPortHal = UsbPortHalInstance.getInstance(this, null);
+ logAndPrint(Log.DEBUG, null, "getInstance done");
}
public void systemReady() {
- mSystemReady = true;
- if (mProxy != null) {
+ mSystemReady = true;
+ if (mUsbPortHal != null) {
+ mUsbPortHal.systemReady();
try {
- mProxy.queryPortStatus();
- mEnableUsbDataSignaling = true;
- } catch (RemoteException e) {
+ mUsbPortHal.queryPortStatus(++mTransactionId);
+ } catch (Exception e) {
logAndPrintException(null,
"ServiceStart: Failed to query port status", e);
}
@@ -233,6 +226,7 @@
intent.setComponent(ComponentName.unflattenFromString(r.getString(
com.android.internal.R.string.config_usbContaminantActivity)));
intent.putExtra(UsbManager.EXTRA_PORT, ParcelableUsbPort.of(currentPortInfo.mUsbPort));
+ intent.putExtra(UsbManager.EXTRA_PORT_STATUS, currentPortInfo.mUsbPortStatus);
// Simple notification clicks are immutable
PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0,
@@ -340,13 +334,52 @@
}
try {
- // Oneway call into the hal. Use the castFrom method from HIDL.
- android.hardware.usb.V1_2.IUsb proxy = android.hardware.usb.V1_2.IUsb.castFrom(mProxy);
- proxy.enableContaminantPresenceDetection(portId, enable);
- } catch (RemoteException e) {
+ mUsbPortHal.enableContaminantPresenceDetection(portId, enable, ++mTransactionId);
+ } catch (Exception e) {
logAndPrintException(pw, "Failed to set contaminant detection", e);
- } catch (ClassCastException e) {
- logAndPrintException(pw, "Method only applicable to V1.2 or above implementation", e);
+ }
+ }
+
+ /**
+ * Limits power transfer in/out of USB-C port.
+ *
+ * @param portId port identifier.
+ * @param limit limit power transfer when true.
+ */
+ public void enableLimitPowerTransfer(@NonNull String portId, boolean limit, long transactionId,
+ IUsbOperationInternal callback, IndentingPrintWriter pw) {
+ Objects.requireNonNull(portId);
+ final PortInfo portInfo = mPorts.get(portId);
+ if (portInfo == null) {
+ logAndPrint(Log.ERROR, pw, "enableLimitPowerTransfer: No such port: " + portId
+ + " opId:" + transactionId);
+ try {
+ if (callback != null) {
+ callback.onOperationComplete(USB_OPERATION_ERROR_PORT_MISMATCH);
+ }
+ } catch (RemoteException e) {
+ logAndPrintException(pw,
+ "enableLimitPowerTransfer: Failed to call OperationComplete. opId:"
+ + transactionId, e);
+ }
+ return;
+ }
+
+ try {
+ try {
+ mUsbPortHal.enableLimitPowerTransfer(portId, limit, transactionId, callback);
+ } catch (Exception e) {
+ logAndPrintException(pw,
+ "enableLimitPowerTransfer: Failed to limit power transfer. opId:"
+ + transactionId , e);
+ if (callback != null) {
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ }
+ }
+ } catch (RemoteException e) {
+ logAndPrintException(pw,
+ "enableLimitPowerTransfer:Failed to call onOperationComplete. opId:"
+ + transactionId, e);
}
}
@@ -355,46 +388,79 @@
*
* @param enable enable or disable USB data signaling
*/
- public boolean enableUsbDataSignal(boolean enable) {
- try {
- mEnableUsbDataSignaling = enable;
- // Call into the hal. Use the castFrom method from HIDL.
- android.hardware.usb.V1_3.IUsb proxy = android.hardware.usb.V1_3.IUsb.castFrom(mProxy);
- return proxy.enableUsbDataSignal(enable);
- } catch (RemoteException e) {
- logAndPrintException(null, "Failed to set USB data signaling", e);
- return false;
- } catch (ClassCastException e) {
- logAndPrintException(null, "Method only applicable to V1.3 or above implementation", e);
+ public boolean enableUsbData(@NonNull String portId, boolean enable, int transactionId,
+ @NonNull IUsbOperationInternal callback, IndentingPrintWriter pw) {
+ Objects.requireNonNull(callback);
+ Objects.requireNonNull(portId);
+ final PortInfo portInfo = mPorts.get(portId);
+ if (portInfo == null) {
+ logAndPrint(Log.ERROR, pw, "enableUsbData: No such port: " + portId
+ + " opId:" + transactionId);
+ try {
+ callback.onOperationComplete(USB_OPERATION_ERROR_PORT_MISMATCH);
+ } catch (RemoteException e) {
+ logAndPrintException(pw,
+ "enableUsbData: Failed to call OperationComplete. opId:"
+ + transactionId, e);
+ }
return false;
}
+
+ try {
+ try {
+ return mUsbPortHal.enableUsbData(portId, enable, transactionId, callback);
+ } catch (Exception e) {
+ logAndPrintException(pw,
+ "enableUsbData: Failed to invoke enableUsbData. opId:"
+ + transactionId , e);
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ }
+ } catch (RemoteException e) {
+ logAndPrintException(pw,
+ "enableUsbData: Failed to call onOperationComplete. opId:"
+ + transactionId, e);
+ }
+
+ return false;
}
/**
* Get USB HAL version
*
* @param none
+ * @return {@link UsbManager#USB_HAL_RETRY} returned when hal version
+ * is yet to be determined.
*/
public int getUsbHalVersion() {
- return mCurrentUsbHalVersion;
+ if (mUsbPortHal != null) {
+ try {
+ return mUsbPortHal.getUsbHalVersion();
+ } catch (RemoteException e) {
+ return UsbManager.USB_HAL_RETRY;
+ }
+ }
+ return UsbManager.USB_HAL_RETRY;
}
- /**
- * update USB HAL version
- *
- * @param none
- */
- private void updateUsbHalVersion() {
- if (android.hardware.usb.V1_3.IUsb.castFrom(mProxy) != null) {
- mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_3;
- } else if (android.hardware.usb.V1_2.IUsb.castFrom(mProxy) != null) {
- mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_2;
- } else if (android.hardware.usb.V1_1.IUsb.castFrom(mProxy) != null) {
- mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_1;
- } else {
- mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_0;
- }
- logAndPrint(Log.INFO, null, "USB HAL version: " + mCurrentUsbHalVersion);
+ private int toHalUsbDataRole(int usbDataRole) {
+ if (usbDataRole == DATA_ROLE_DEVICE)
+ return HAL_DATA_ROLE_DEVICE;
+ else
+ return HAL_DATA_ROLE_HOST;
+ }
+
+ private int toHalUsbPowerRole(int usbPowerRole) {
+ if (usbPowerRole == POWER_ROLE_SINK)
+ return HAL_POWER_ROLE_SINK;
+ else
+ return HAL_POWER_ROLE_SOURCE;
+ }
+
+ private int toHalUsbMode(int usbMode) {
+ if (usbMode == MODE_UFP)
+ return HAL_MODE_UFP;
+ else
+ return HAL_MODE_DFP;
}
public void setPortRoles(String portId, int newPowerRole, int newDataRole,
@@ -473,7 +539,7 @@
sim.currentPowerRole = newPowerRole;
sim.currentDataRole = newDataRole;
updatePortsLocked(pw, null);
- } else if (mProxy != null) {
+ } else if (mUsbPortHal != null) {
if (currentMode != newMode) {
// Changing the mode will have the side-effect of also changing
// the power and data roles but it might take some time to apply
@@ -485,44 +551,37 @@
logAndPrint(Log.ERROR, pw, "Trying to set the USB port mode: "
+ "portId=" + portId
+ ", newMode=" + UsbPort.modeToString(newMode));
- PortRole newRole = new PortRole();
- newRole.type = PortRoleType.MODE;
- newRole.role = newMode;
try {
- mProxy.switchRole(portId, newRole);
- } catch (RemoteException e) {
+ mUsbPortHal.switchMode(portId, toHalUsbMode(newMode), ++mTransactionId);
+ } catch (Exception e) {
logAndPrintException(pw, "Failed to set the USB port mode: "
+ "portId=" + portId
- + ", newMode=" + UsbPort.modeToString(newRole.role), e);
+ + ", newMode=" + UsbPort.modeToString(newMode), e);
}
} else {
// Change power and data role independently as needed.
if (currentPowerRole != newPowerRole) {
- PortRole newRole = new PortRole();
- newRole.type = PortRoleType.POWER_ROLE;
- newRole.role = newPowerRole;
try {
- mProxy.switchRole(portId, newRole);
- } catch (RemoteException e) {
+ mUsbPortHal.switchPowerRole(portId, toHalUsbPowerRole(newPowerRole),
+ ++mTransactionId);
+ } catch (Exception e) {
logAndPrintException(pw, "Failed to set the USB port power role: "
+ "portId=" + portId
+ ", newPowerRole=" + UsbPort.powerRoleToString
- (newRole.role),
+ (newPowerRole),
e);
return;
}
}
if (currentDataRole != newDataRole) {
- PortRole newRole = new PortRole();
- newRole.type = PortRoleType.DATA_ROLE;
- newRole.role = newDataRole;
try {
- mProxy.switchRole(portId, newRole);
- } catch (RemoteException e) {
+ mUsbPortHal.switchDataRole(portId, toHalUsbDataRole(newDataRole),
+ ++mTransactionId);
+ } catch (Exception e) {
logAndPrintException(pw, "Failed to set the USB port data role: "
+ "portId=" + portId
- + ", newDataRole=" + UsbPort.dataRoleToString(newRole
- .role),
+ + ", newDataRole=" + UsbPort.dataRoleToString
+ (newDataRole),
e);
}
}
@@ -531,6 +590,15 @@
}
}
+ public void updatePorts(ArrayList<RawPortInfo> newPortInfo) {
+ Message message = mHandler.obtainMessage();
+ Bundle bundle = new Bundle();
+ bundle.putParcelableArrayList(PORT_INFO, newPortInfo);
+ message.what = MSG_UPDATE_PORTS;
+ message.setData(bundle);
+ mHandler.sendMessage(message);
+ }
+
public void addSimulatedPort(String portId, int supportedModes, IndentingPrintWriter pw) {
synchronized (mLock) {
if (mSimulatedPorts.containsKey(portId)) {
@@ -662,191 +730,12 @@
portInfo.dump(dump, "usb_ports", UsbPortManagerProto.USB_PORTS);
}
- dump.write("enable_usb_data_signaling", UsbPortManagerProto.ENABLE_USB_DATA_SIGNALING,
- mEnableUsbDataSignaling);
+ dump.write("usb_hal_version", UsbPortManagerProto.HAL_VERSION, getUsbHalVersion());
}
dump.end(token);
}
- private static class HALCallback extends IUsbCallback.Stub {
- public IndentingPrintWriter pw;
- public UsbPortManager portManager;
-
- HALCallback(IndentingPrintWriter pw, UsbPortManager portManager) {
- this.pw = pw;
- this.portManager = portManager;
- }
-
- public void notifyPortStatusChange(
- ArrayList<android.hardware.usb.V1_0.PortStatus> currentPortStatus, int retval) {
- if (!portManager.mSystemReady) {
- return;
- }
-
- if (retval != Status.SUCCESS) {
- logAndPrint(Log.ERROR, pw, "port status enquiry failed");
- return;
- }
-
- ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
-
- for (android.hardware.usb.V1_0.PortStatus current : currentPortStatus) {
- RawPortInfo temp = new RawPortInfo(current.portName,
- current.supportedModes, CONTAMINANT_PROTECTION_NONE,
- current.currentMode,
- current.canChangeMode, current.currentPowerRole,
- current.canChangePowerRole,
- current.currentDataRole, current.canChangeDataRole,
- false, CONTAMINANT_PROTECTION_NONE,
- false, CONTAMINANT_DETECTION_NOT_SUPPORTED);
- newPortInfo.add(temp);
- logAndPrint(Log.INFO, pw, "ClientCallback V1_0: " + current.portName);
- }
-
- Message message = portManager.mHandler.obtainMessage();
- Bundle bundle = new Bundle();
- bundle.putParcelableArrayList(PORT_INFO, newPortInfo);
- message.what = MSG_UPDATE_PORTS;
- message.setData(bundle);
- portManager.mHandler.sendMessage(message);
- }
-
-
- public void notifyPortStatusChange_1_1(ArrayList<PortStatus_1_1> currentPortStatus,
- int retval) {
- if (!portManager.mSystemReady) {
- return;
- }
-
- if (retval != Status.SUCCESS) {
- logAndPrint(Log.ERROR, pw, "port status enquiry failed");
- return;
- }
-
- ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
-
- int numStatus = currentPortStatus.size();
- for (int i = 0; i < numStatus; i++) {
- PortStatus_1_1 current = currentPortStatus.get(i);
- RawPortInfo temp = new RawPortInfo(current.status.portName,
- current.supportedModes, CONTAMINANT_PROTECTION_NONE,
- current.currentMode,
- current.status.canChangeMode, current.status.currentPowerRole,
- current.status.canChangePowerRole,
- current.status.currentDataRole, current.status.canChangeDataRole,
- false, CONTAMINANT_PROTECTION_NONE,
- false, CONTAMINANT_DETECTION_NOT_SUPPORTED);
- newPortInfo.add(temp);
- logAndPrint(Log.INFO, pw, "ClientCallback V1_1: " + current.status.portName);
- }
-
- Message message = portManager.mHandler.obtainMessage();
- Bundle bundle = new Bundle();
- bundle.putParcelableArrayList(PORT_INFO, newPortInfo);
- message.what = MSG_UPDATE_PORTS;
- message.setData(bundle);
- portManager.mHandler.sendMessage(message);
- }
-
- public void notifyPortStatusChange_1_2(
- ArrayList<PortStatus> currentPortStatus, int retval) {
- if (!portManager.mSystemReady) {
- return;
- }
-
- if (retval != Status.SUCCESS) {
- logAndPrint(Log.ERROR, pw, "port status enquiry failed");
- return;
- }
-
- ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
-
- int numStatus = currentPortStatus.size();
- for (int i = 0; i < numStatus; i++) {
- PortStatus current = currentPortStatus.get(i);
- RawPortInfo temp = new RawPortInfo(current.status_1_1.status.portName,
- current.status_1_1.supportedModes,
- current.supportedContaminantProtectionModes,
- current.status_1_1.currentMode,
- current.status_1_1.status.canChangeMode,
- current.status_1_1.status.currentPowerRole,
- current.status_1_1.status.canChangePowerRole,
- current.status_1_1.status.currentDataRole,
- current.status_1_1.status.canChangeDataRole,
- current.supportsEnableContaminantPresenceProtection,
- current.contaminantProtectionStatus,
- current.supportsEnableContaminantPresenceDetection,
- current.contaminantDetectionStatus);
- newPortInfo.add(temp);
- logAndPrint(Log.INFO, pw, "ClientCallback V1_2: "
- + current.status_1_1.status.portName);
- }
-
- Message message = portManager.mHandler.obtainMessage();
- Bundle bundle = new Bundle();
- bundle.putParcelableArrayList(PORT_INFO, newPortInfo);
- message.what = MSG_UPDATE_PORTS;
- message.setData(bundle);
- portManager.mHandler.sendMessage(message);
- }
-
- public void notifyRoleSwitchStatus(String portName, PortRole role, int retval) {
- if (retval == Status.SUCCESS) {
- logAndPrint(Log.INFO, pw, portName + " role switch successful");
- } else {
- logAndPrint(Log.ERROR, pw, portName + " role switch failed");
- }
- }
- }
-
- final class DeathRecipient implements HwBinder.DeathRecipient {
- public IndentingPrintWriter pw;
-
- DeathRecipient(IndentingPrintWriter pw) {
- this.pw = pw;
- }
-
- @Override
- public void serviceDied(long cookie) {
- if (cookie == USB_HAL_DEATH_COOKIE) {
- logAndPrint(Log.ERROR, pw, "Usb hal service died cookie: " + cookie);
- synchronized (mLock) {
- mProxy = null;
- }
- }
- }
- }
-
- final class ServiceNotification extends IServiceNotification.Stub {
- @Override
- public void onRegistration(String fqName, String name, boolean preexisting) {
- logAndPrint(Log.INFO, null, "Usb hal service started " + fqName + " " + name);
- connectToProxy(null);
- }
- }
-
- private void connectToProxy(IndentingPrintWriter pw) {
- synchronized (mLock) {
- if (mProxy != null) {
- return;
- }
-
- try {
- mProxy = IUsb.getService();
- mProxy.linkToDeath(new DeathRecipient(pw), USB_HAL_DEATH_COOKIE);
- mProxy.setCallback(mHALCallback);
- mProxy.queryPortStatus();
- updateUsbHalVersion();
- } catch (NoSuchElementException e) {
- logAndPrintException(pw, "connectToProxy: usb hal service not found."
- + " Did the service fail to start?", e);
- } catch (RemoteException e) {
- logAndPrintException(pw, "connectToProxy: usb hal service not responding", e);
- }
- }
- }
-
/**
* Simulated ports directly add the new roles to mSimulatedPorts before calling.
* USB hal callback populates and sends the newPortInfo.
@@ -869,7 +758,9 @@
portInfo.supportsEnableContaminantPresenceProtection,
portInfo.contaminantProtectionStatus,
portInfo.supportsEnableContaminantPresenceDetection,
- portInfo.contaminantDetectionStatus, pw);
+ portInfo.contaminantDetectionStatus,
+ portInfo.usbDataEnabled,
+ portInfo.powerTransferLimited, pw);
}
} else {
for (RawPortInfo currentPortInfo : newPortInfo) {
@@ -881,7 +772,9 @@
currentPortInfo.supportsEnableContaminantPresenceProtection,
currentPortInfo.contaminantProtectionStatus,
currentPortInfo.supportsEnableContaminantPresenceDetection,
- currentPortInfo.contaminantDetectionStatus, pw);
+ currentPortInfo.contaminantDetectionStatus,
+ currentPortInfo.usbDataEnabled,
+ currentPortInfo.powerTransferLimited, pw);
}
}
@@ -917,6 +810,8 @@
int contaminantProtectionStatus,
boolean supportsEnableContaminantPresenceDetection,
int contaminantDetectionStatus,
+ boolean usbDataEnabled,
+ boolean powerTransferLimited,
IndentingPrintWriter pw) {
// Only allow mode switch capability for dual role ports.
// Validate that the current mode matches the supported modes we expect.
@@ -975,7 +870,8 @@
currentPowerRole, canChangePowerRole,
currentDataRole, canChangeDataRole,
supportedRoleCombinations, contaminantProtectionStatus,
- contaminantDetectionStatus);
+ contaminantDetectionStatus, usbDataEnabled,
+ powerTransferLimited);
mPorts.put(portId, portInfo);
} else {
// Validate that ports aren't changing definition out from under us.
@@ -1012,7 +908,8 @@
currentPowerRole, canChangePowerRole,
currentDataRole, canChangeDataRole,
supportedRoleCombinations, contaminantProtectionStatus,
- contaminantDetectionStatus)) {
+ contaminantDetectionStatus, usbDataEnabled,
+ powerTransferLimited)) {
portInfo.mDisposition = PortInfo.DISPOSITION_CHANGED;
} else {
portInfo.mDisposition = PortInfo.DISPOSITION_READY;
@@ -1034,6 +931,7 @@
private void handlePortChangedLocked(PortInfo portInfo, IndentingPrintWriter pw) {
logAndPrint(Log.INFO, pw, "USB port changed: " + portInfo);
enableContaminantDetectionIfNeeded(portInfo, pw);
+ disableLimitPowerTransferIfNeeded(portInfo, pw);
handlePortLocked(portInfo, pw);
}
@@ -1090,6 +988,19 @@
}
}
+ private void disableLimitPowerTransferIfNeeded(PortInfo portInfo, IndentingPrintWriter pw) {
+ if (!mConnected.containsKey(portInfo.mUsbPort.getId())) {
+ return;
+ }
+
+ if (mConnected.get(portInfo.mUsbPort.getId())
+ && !portInfo.mUsbPortStatus.isConnected()
+ && portInfo.mUsbPortStatus.isPowerTransferLimited()) {
+ // Relax enableLimitPowerTransfer upon unplug.
+ enableLimitPowerTransfer(portInfo.mUsbPort.getId(), false, ++mTransactionId, null, pw);
+ }
+ }
+
private void logToStatsd(PortInfo portInfo, IndentingPrintWriter pw) {
// Port is removed
if (portInfo.mUsbPortStatus == null) {
@@ -1141,14 +1052,14 @@
}
}
- private static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) {
+ public static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) {
Slog.println(priority, TAG, msg);
if (pw != null) {
pw.println(msg);
}
}
- private static void logAndPrintException(IndentingPrintWriter pw, String msg, Exception e) {
+ public static void logAndPrintException(IndentingPrintWriter pw, String msg, Exception e) {
Slog.e(TAG, msg, e);
if (pw != null) {
pw.println(msg + e);
@@ -1179,7 +1090,7 @@
/**
* Describes a USB port.
*/
- private static final class PortInfo {
+ public static final class PortInfo {
public static final int DISPOSITION_ADDED = 0;
public static final int DISPOSITION_CHANGED = 1;
public static final int DISPOSITION_READY = 2;
@@ -1224,7 +1135,7 @@
!= supportedRoleCombinations) {
mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
supportedRoleCombinations, UsbPortStatus.CONTAMINANT_PROTECTION_NONE,
- UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED);
+ UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED, true, false);
dispositionChanged = true;
}
@@ -1243,7 +1154,8 @@
int currentPowerRole, boolean canChangePowerRole,
int currentDataRole, boolean canChangeDataRole,
int supportedRoleCombinations, int contaminantProtectionStatus,
- int contaminantDetectionStatus) {
+ int contaminantDetectionStatus, boolean usbDataEnabled,
+ boolean powerTransferLimited) {
boolean dispositionChanged = false;
mCanChangeMode = canChangeMode;
@@ -1258,10 +1170,15 @@
|| mUsbPortStatus.getContaminantProtectionStatus()
!= contaminantProtectionStatus
|| mUsbPortStatus.getContaminantDetectionStatus()
- != contaminantDetectionStatus) {
+ != contaminantDetectionStatus
+ || mUsbPortStatus.getUsbDataStatus()
+ != usbDataEnabled
+ || mUsbPortStatus.isPowerTransferLimited()
+ != powerTransferLimited) {
mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
supportedRoleCombinations, contaminantProtectionStatus,
- contaminantDetectionStatus);
+ contaminantDetectionStatus, usbDataEnabled,
+ powerTransferLimited);
dispositionChanged = true;
}
@@ -1290,7 +1207,6 @@
UsbPortInfoProto.CONNECTED_AT_MILLIS, mConnectedAtMillis);
dump.write("last_connect_duration_millis",
UsbPortInfoProto.LAST_CONNECT_DURATION_MILLIS, mLastConnectDurationMillis);
-
dump.end(token);
}
@@ -1304,115 +1220,4 @@
+ ", lastConnectDurationMillis=" + mLastConnectDurationMillis;
}
}
-
- /**
- * Used for storing the raw data from the kernel
- * Values of the member variables mocked directly incase of emulation.
- */
- private static final class RawPortInfo implements Parcelable {
- public final String portId;
- public final int supportedModes;
- public final int supportedContaminantProtectionModes;
- public int currentMode;
- public boolean canChangeMode;
- public int currentPowerRole;
- public boolean canChangePowerRole;
- public int currentDataRole;
- public boolean canChangeDataRole;
- public boolean supportsEnableContaminantPresenceProtection;
- public int contaminantProtectionStatus;
- public boolean supportsEnableContaminantPresenceDetection;
- public int contaminantDetectionStatus;
-
- RawPortInfo(String portId, int supportedModes) {
- this.portId = portId;
- this.supportedModes = supportedModes;
- this.supportedContaminantProtectionModes = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
- this.supportsEnableContaminantPresenceProtection = false;
- this.contaminantProtectionStatus = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
- this.supportsEnableContaminantPresenceDetection = false;
- this.contaminantDetectionStatus = UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
- }
-
- RawPortInfo(String portId, int supportedModes, int supportedContaminantProtectionModes,
- int currentMode, boolean canChangeMode,
- int currentPowerRole, boolean canChangePowerRole,
- int currentDataRole, boolean canChangeDataRole,
- boolean supportsEnableContaminantPresenceProtection,
- int contaminantProtectionStatus,
- boolean supportsEnableContaminantPresenceDetection,
- int contaminantDetectionStatus) {
- this.portId = portId;
- this.supportedModes = supportedModes;
- this.supportedContaminantProtectionModes = supportedContaminantProtectionModes;
- this.currentMode = currentMode;
- this.canChangeMode = canChangeMode;
- this.currentPowerRole = currentPowerRole;
- this.canChangePowerRole = canChangePowerRole;
- this.currentDataRole = currentDataRole;
- this.canChangeDataRole = canChangeDataRole;
- this.supportsEnableContaminantPresenceProtection =
- supportsEnableContaminantPresenceProtection;
- this.contaminantProtectionStatus = contaminantProtectionStatus;
- this.supportsEnableContaminantPresenceDetection =
- supportsEnableContaminantPresenceDetection;
- this.contaminantDetectionStatus = contaminantDetectionStatus;
- }
-
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(portId);
- dest.writeInt(supportedModes);
- dest.writeInt(supportedContaminantProtectionModes);
- dest.writeInt(currentMode);
- dest.writeByte((byte) (canChangeMode ? 1 : 0));
- dest.writeInt(currentPowerRole);
- dest.writeByte((byte) (canChangePowerRole ? 1 : 0));
- dest.writeInt(currentDataRole);
- dest.writeByte((byte) (canChangeDataRole ? 1 : 0));
- dest.writeBoolean(supportsEnableContaminantPresenceProtection);
- dest.writeInt(contaminantProtectionStatus);
- dest.writeBoolean(supportsEnableContaminantPresenceDetection);
- dest.writeInt(contaminantDetectionStatus);
- }
-
- public static final Parcelable.Creator<RawPortInfo> CREATOR =
- new Parcelable.Creator<RawPortInfo>() {
- @Override
- public RawPortInfo createFromParcel(Parcel in) {
- String id = in.readString();
- int supportedModes = in.readInt();
- int supportedContaminantProtectionModes = in.readInt();
- int currentMode = in.readInt();
- boolean canChangeMode = in.readByte() != 0;
- int currentPowerRole = in.readInt();
- boolean canChangePowerRole = in.readByte() != 0;
- int currentDataRole = in.readInt();
- boolean canChangeDataRole = in.readByte() != 0;
- boolean supportsEnableContaminantPresenceProtection = in.readBoolean();
- int contaminantProtectionStatus = in.readInt();
- boolean supportsEnableContaminantPresenceDetection = in.readBoolean();
- int contaminantDetectionStatus = in.readInt();
- return new RawPortInfo(id, supportedModes,
- supportedContaminantProtectionModes, currentMode, canChangeMode,
- currentPowerRole, canChangePowerRole,
- currentDataRole, canChangeDataRole,
- supportsEnableContaminantPresenceProtection,
- contaminantProtectionStatus,
- supportsEnableContaminantPresenceDetection,
- contaminantDetectionStatus);
- }
-
- @Override
- public RawPortInfo[] newArray(int size) {
- return new RawPortInfo[size];
- }
- };
- }
}
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 3d3538d..51643e7 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -16,6 +16,7 @@
package com.android.server.usb;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL;
import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
import static android.hardware.usb.UsbPortStatus.MODE_DFP;
@@ -35,6 +36,7 @@
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.hardware.usb.IUsbManager;
+import android.hardware.usb.IUsbOperationInternal;
import android.hardware.usb.ParcelableUsbPort;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
@@ -44,6 +46,7 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.service.usb.UsbServiceDumpProto;
@@ -731,6 +734,28 @@
}
@Override
+ public void enableLimitPowerTransfer(String portId, boolean limit, int operationId,
+ IUsbOperationInternal callback) {
+ Objects.requireNonNull(portId, "portId must not be null. opID:" + operationId);
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (mPortManager != null) {
+ mPortManager.enableLimitPowerTransfer(portId, limit, operationId, callback, null);
+ } else {
+ try {
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "enableLimitPowerTransfer: Failed to call onOperationComplete", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
public void enableContaminantDetection(String portId, boolean enable) {
Objects.requireNonNull(portId, "portId must not be null");
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
@@ -762,19 +787,30 @@
}
@Override
- public boolean enableUsbDataSignal(boolean enable) {
+ public boolean enableUsbData(String portId, boolean enable, int operationId,
+ IUsbOperationInternal callback) {
+ Objects.requireNonNull(portId, "enableUsbData: portId must not be null. opId:"
+ + operationId);
+ Objects.requireNonNull(callback, "enableUsbData: callback must not be null. opId:"
+ + operationId);
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-
final long ident = Binder.clearCallingIdentity();
+ boolean wait;
try {
if (mPortManager != null) {
- return mPortManager.enableUsbDataSignal(enable);
+ wait = mPortManager.enableUsbData(portId, enable, operationId, callback, null);
} else {
- return false;
+ wait = false;
+ try {
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "enableUsbData: Failed to call onOperationComplete", e);
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
}
+ return wait;
}
@Override
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index 3412a6f..6e68a91 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -870,4 +870,35 @@
return getOutputHeadsetProbability() >= OUT_HEADSET_TRIGGER;
}
+ /**
+ * isDock() indicates if the connected USB output peripheral is a docking station with
+ * audio output.
+ * A valid audio dock must declare only one audio output control terminal of type
+ * TERMINAL_EXTERN_DIGITAL.
+ */
+ public boolean isDock() {
+ if (hasMIDIInterface() || hasHIDInterface()) {
+ return false;
+ }
+
+ ArrayList<UsbDescriptor> acDescriptors =
+ getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL,
+ UsbACInterface.AUDIO_AUDIOCONTROL);
+
+ if (acDescriptors.size() != 1) {
+ return false;
+ }
+
+ if (acDescriptors.get(0) instanceof UsbACTerminal) {
+ UsbACTerminal outDescr = (UsbACTerminal) acDescriptors.get(0);
+ if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_EXTERN_DIGITAL) {
+ return true;
+ }
+ } else {
+ Log.w(TAG, "Undefined Audio Output terminal l: " + acDescriptors.get(0).getLength()
+ + " t:0x" + Integer.toHexString(acDescriptors.get(0).getType()));
+ }
+ return false;
+ }
+
}
diff --git a/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java b/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java
new file mode 100644
index 0000000..8dfc859
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2021 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.usb.hal.port;
+
+import android.hardware.usb.UsbPortStatus;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Used for storing the raw data from the HAL.
+ * Values of the member variables mocked directly in case of emulation.
+ */
+public final class RawPortInfo implements Parcelable {
+ public final String portId;
+ public final int supportedModes;
+ public final int supportedContaminantProtectionModes;
+ public int currentMode;
+ public boolean canChangeMode;
+ public int currentPowerRole;
+ public boolean canChangePowerRole;
+ public int currentDataRole;
+ public boolean canChangeDataRole;
+ public boolean supportsEnableContaminantPresenceProtection;
+ public int contaminantProtectionStatus;
+ public boolean supportsEnableContaminantPresenceDetection;
+ public int contaminantDetectionStatus;
+ public boolean usbDataEnabled;
+ public boolean powerTransferLimited;
+
+ public RawPortInfo(String portId, int supportedModes) {
+ this.portId = portId;
+ this.supportedModes = supportedModes;
+ this.supportedContaminantProtectionModes = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+ this.supportsEnableContaminantPresenceProtection = false;
+ this.contaminantProtectionStatus = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+ this.supportsEnableContaminantPresenceDetection = false;
+ this.contaminantDetectionStatus = UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
+ this.usbDataEnabled = true;
+ this.powerTransferLimited = false;
+ }
+
+ public RawPortInfo(String portId, int supportedModes, int supportedContaminantProtectionModes,
+ int currentMode, boolean canChangeMode,
+ int currentPowerRole, boolean canChangePowerRole,
+ int currentDataRole, boolean canChangeDataRole,
+ boolean supportsEnableContaminantPresenceProtection,
+ int contaminantProtectionStatus,
+ boolean supportsEnableContaminantPresenceDetection,
+ int contaminantDetectionStatus,
+ boolean usbDataEnabled,
+ boolean powerTransferLimited) {
+ this.portId = portId;
+ this.supportedModes = supportedModes;
+ this.supportedContaminantProtectionModes = supportedContaminantProtectionModes;
+ this.currentMode = currentMode;
+ this.canChangeMode = canChangeMode;
+ this.currentPowerRole = currentPowerRole;
+ this.canChangePowerRole = canChangePowerRole;
+ this.currentDataRole = currentDataRole;
+ this.canChangeDataRole = canChangeDataRole;
+ this.supportsEnableContaminantPresenceProtection =
+ supportsEnableContaminantPresenceProtection;
+ this.contaminantProtectionStatus = contaminantProtectionStatus;
+ this.supportsEnableContaminantPresenceDetection =
+ supportsEnableContaminantPresenceDetection;
+ this.contaminantDetectionStatus = contaminantDetectionStatus;
+ this.usbDataEnabled = usbDataEnabled;
+ this.powerTransferLimited = powerTransferLimited;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(portId);
+ dest.writeInt(supportedModes);
+ dest.writeInt(supportedContaminantProtectionModes);
+ dest.writeInt(currentMode);
+ dest.writeByte((byte) (canChangeMode ? 1 : 0));
+ dest.writeInt(currentPowerRole);
+ dest.writeByte((byte) (canChangePowerRole ? 1 : 0));
+ dest.writeInt(currentDataRole);
+ dest.writeByte((byte) (canChangeDataRole ? 1 : 0));
+ dest.writeBoolean(supportsEnableContaminantPresenceProtection);
+ dest.writeInt(contaminantProtectionStatus);
+ dest.writeBoolean(supportsEnableContaminantPresenceDetection);
+ dest.writeInt(contaminantDetectionStatus);
+ dest.writeBoolean(usbDataEnabled);
+ dest.writeBoolean(powerTransferLimited);
+ }
+
+ public static final Parcelable.Creator<RawPortInfo> CREATOR =
+ new Parcelable.Creator<RawPortInfo>() {
+ @Override
+ public RawPortInfo createFromParcel(Parcel in) {
+ String id = in.readString();
+ int supportedModes = in.readInt();
+ int supportedContaminantProtectionModes = in.readInt();
+ int currentMode = in.readInt();
+ boolean canChangeMode = in.readByte() != 0;
+ int currentPowerRole = in.readInt();
+ boolean canChangePowerRole = in.readByte() != 0;
+ int currentDataRole = in.readInt();
+ boolean canChangeDataRole = in.readByte() != 0;
+ boolean supportsEnableContaminantPresenceProtection = in.readBoolean();
+ int contaminantProtectionStatus = in.readInt();
+ boolean supportsEnableContaminantPresenceDetection = in.readBoolean();
+ int contaminantDetectionStatus = in.readInt();
+ boolean usbDataEnabled = in.readBoolean();
+ boolean powerTransferLimited = in.readBoolean();
+ return new RawPortInfo(id, supportedModes,
+ supportedContaminantProtectionModes, currentMode, canChangeMode,
+ currentPowerRole, canChangePowerRole,
+ currentDataRole, canChangeDataRole,
+ supportsEnableContaminantPresenceProtection,
+ contaminantProtectionStatus,
+ supportsEnableContaminantPresenceDetection,
+ contaminantDetectionStatus, usbDataEnabled,
+ powerTransferLimited);
+ }
+
+ @Override
+ public RawPortInfo[] newArray(int size) {
+ return new RawPortInfo[size];
+ }
+ };
+}
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
new file mode 100644
index 0000000..2460242
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
@@ -0,0 +1,533 @@
+/*
+ * Copyright (C) 2021 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.usb.hal.port;
+
+import static android.hardware.usb.UsbManager.USB_HAL_V2_0;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_SUCCESS;
+
+import static com.android.server.usb.UsbPortManager.logAndPrint;
+import static com.android.server.usb.UsbPortManager.logAndPrintException;
+
+import android.annotation.Nullable;
+import android.hardware.usb.ContaminantProtectionStatus;
+import android.hardware.usb.IUsb;
+import android.hardware.usb.IUsbOperationInternal;
+import android.hardware.usb.UsbManager.UsbHalVersion;
+import android.hardware.usb.UsbPort;
+import android.hardware.usb.UsbPortStatus;
+import android.hardware.usb.PortMode;
+import android.hardware.usb.Status;
+import android.hardware.usb.IUsbCallback;
+import android.hardware.usb.PortRole;
+import android.hardware.usb.PortStatus;
+import android.os.ServiceManager;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.LongSparseArray;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.usb.UsbPortManager;
+import com.android.server.usb.hal.port.RawPortInfo;
+
+import java.util.ArrayList;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+
+/**
+ * Implements the methods to interact with AIDL USB HAL.
+ */
+public final class UsbPortAidl implements UsbPortHal {
+ private static final String TAG = UsbPortAidl.class.getSimpleName();
+ private static final String USB_AIDL_SERVICE =
+ "android.hardware.usb.IUsb/default";
+ private static final LongSparseArray<IUsbOperationInternal>
+ sCallbacks = new LongSparseArray<>();
+ // Proxy object for the usb hal daemon.
+ @GuardedBy("mLock")
+ private IUsb mProxy;
+ private UsbPortManager mPortManager;
+ public IndentingPrintWriter mPw;
+ // Mutex for all mutable shared state.
+ private final Object mLock = new Object();
+ // Callback when the UsbPort status is changed by the kernel.
+ private HALCallback mHALCallback;
+ private IBinder mBinder;
+ private boolean mSystemReady;
+ private long mTransactionId;
+
+ public @UsbHalVersion int getUsbHalVersion() throws RemoteException {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ throw new RemoteException("IUsb not initialized yet");
+ }
+ }
+ logAndPrint(Log.INFO, null, "USB HAL AIDL version: USB_HAL_V2_0");
+ return USB_HAL_V2_0;
+ }
+
+ @Override
+ public void systemReady() {
+ mSystemReady = true;
+ }
+
+ public void serviceDied() {
+ logAndPrint(Log.ERROR, mPw, "Usb AIDL hal service died");
+ synchronized (mLock) {
+ mProxy = null;
+ }
+ connectToProxy(null);
+ }
+
+ private void connectToProxy(IndentingPrintWriter pw) {
+ synchronized (mLock) {
+ if (mProxy != null) {
+ return;
+ }
+
+ try {
+ mBinder = ServiceManager.waitForService(USB_AIDL_SERVICE);
+ mProxy = IUsb.Stub.asInterface(mBinder);
+ mBinder.linkToDeath(this::serviceDied, 0);
+ mProxy.setCallback(mHALCallback);
+ mProxy.queryPortStatus(++mTransactionId);
+ } catch (NoSuchElementException e) {
+ logAndPrintException(pw, "connectToProxy: usb hal service not found."
+ + " Did the service fail to start?", e);
+ } catch (RemoteException e) {
+ logAndPrintException(pw, "connectToProxy: usb hal service not responding", e);
+ }
+ }
+ }
+
+ static boolean isServicePresent(IndentingPrintWriter pw) {
+ try {
+ return ServiceManager.isDeclared(USB_AIDL_SERVICE);
+ } catch (NoSuchElementException e) {
+ logAndPrintException(pw, "connectToProxy: usb Aidl hal service not found.", e);
+ }
+
+ return false;
+ }
+
+ public UsbPortAidl(UsbPortManager portManager, IndentingPrintWriter pw) {
+ mPortManager = Objects.requireNonNull(portManager);
+ mPw = pw;
+ mHALCallback = new HALCallback(null, mPortManager, this);
+ connectToProxy(mPw);
+ }
+
+ @Override
+ public void enableContaminantPresenceDetection(String portName, boolean enable,
+ long operationID) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID: "
+ + operationID);
+ return;
+ }
+
+ try {
+ // Oneway call into the hal. Use the castFrom method from HIDL.
+ mProxy.enableContaminantPresenceDetection(portName, enable, operationID);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set contaminant detection. opID:"
+ + operationID, e);
+ }
+ }
+ }
+
+ @Override
+ public void queryPortStatus(long operationID) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID:"
+ + operationID);
+ return;
+ }
+
+ try {
+ mProxy.queryPortStatus(operationID);
+ } catch (RemoteException e) {
+ logAndPrintException(null, "ServiceStart: Failed to query port status. opID:"
+ + operationID, e);
+ }
+ }
+ }
+
+ @Override
+ public void switchMode(String portId, @HalUsbPortMode int newMode, long operationID) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID:"
+ + operationID);
+ return;
+ }
+
+ PortRole newRole = new PortRole();
+ newRole.setMode((byte)newMode);
+ try {
+ mProxy.switchRole(portId, newRole, operationID);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set the USB port mode: "
+ + "portId=" + portId
+ + ", newMode=" + UsbPort.modeToString(newMode)
+ + "opID:" + operationID, e);
+ }
+ }
+ }
+
+ @Override
+ public void switchPowerRole(String portId, @HalUsbPowerRole int newPowerRole,
+ long operationID) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID:"
+ + operationID);
+ return;
+ }
+
+ PortRole newRole = new PortRole();
+ newRole.setPowerRole((byte)newPowerRole);
+ try {
+ mProxy.switchRole(portId, newRole, operationID);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set the USB power role: portId=" + portId
+ + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole)
+ + "opID:" + operationID, e);
+ }
+ }
+ }
+
+ @Override
+ public void switchDataRole(String portId, @HalUsbDataRole int newDataRole, long operationID) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID:"
+ + operationID);
+ return;
+ }
+
+ PortRole newRole = new PortRole();
+ newRole.setDataRole((byte)newDataRole);
+ try {
+ mProxy.switchRole(portId, newRole, operationID);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set the USB data role: portId=" + portId
+ + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole)
+ + "opID:" + operationID, e);
+ }
+ }
+ }
+
+ @Override
+ public boolean enableUsbData(String portName, boolean enable, long operationID,
+ IUsbOperationInternal callback) {
+ Objects.requireNonNull(portName);
+ Objects.requireNonNull(callback);
+ long key = operationID;
+ synchronized (mLock) {
+ try {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw,
+ "enableUsbData: Proxy is null. Retry !opID:"
+ + operationID);
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ return false;
+ }
+ while (sCallbacks.get(key) != null) {
+ key = ThreadLocalRandom.current().nextInt();
+ }
+ if (key != operationID) {
+ logAndPrint(Log.INFO, mPw, "enableUsbData: operationID exists ! opID:"
+ + operationID + " key:" + key);
+ }
+ try {
+ sCallbacks.put(key, callback);
+ mProxy.enableUsbData(portName, enable, key);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw,
+ "enableUsbData: Failed to invoke enableUsbData: portID="
+ + portName + "opID:" + operationID, e);
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ sCallbacks.remove(key);
+ return false;
+ }
+ } catch (RemoteException e) {
+ logAndPrintException(mPw,
+ "enableUsbData: Failed to call onOperationComplete portID="
+ + portName + "opID:" + operationID, e);
+ sCallbacks.remove(key);
+ return false;
+ }
+ return true;
+ }
+ }
+
+ @Override
+ public void enableLimitPowerTransfer(String portName, boolean limit, long operationID,
+ IUsbOperationInternal callback) {
+ Objects.requireNonNull(portName);
+ long key = operationID;
+ synchronized (mLock) {
+ try {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw,
+ "enableLimitPowerTransfer: Proxy is null. Retry !opID:"
+ + operationID);
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ return;
+ }
+ while (sCallbacks.get(key) != null) {
+ key = ThreadLocalRandom.current().nextInt();
+ }
+ if (key != operationID) {
+ logAndPrint(Log.INFO, mPw, "enableUsbData: operationID exists ! opID:"
+ + operationID + " key:" + key);
+ }
+ try {
+ sCallbacks.put(key, callback);
+ mProxy.limitPowerTransfer(portName, limit, key);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw,
+ "enableLimitPowerTransfer: Failed while invoking AIDL HAL"
+ + " portID=" + portName + " opID:" + operationID, e);
+ if (callback != null) {
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ }
+ sCallbacks.remove(key);
+ }
+ } catch (RemoteException e) {
+ logAndPrintException(mPw,
+ "enableLimitPowerTransfer: Failed to call onOperationComplete portID="
+ + portName + " opID:" + operationID, e);
+ }
+ }
+ }
+
+ private static class HALCallback extends IUsbCallback.Stub {
+ public IndentingPrintWriter mPw;
+ public UsbPortManager mPortManager;
+ public UsbPortAidl mUsbPortAidl;
+
+ HALCallback(IndentingPrintWriter pw, UsbPortManager portManager, UsbPortAidl usbPortAidl) {
+ this.mPw = pw;
+ this.mPortManager = portManager;
+ this.mUsbPortAidl = usbPortAidl;
+ }
+
+ /**
+ * Converts from AIDL defined mode constants to UsbPortStatus constants.
+ * AIDL does not gracefully support bitfield when combined with enums.
+ */
+ private int toPortMode(byte aidlPortMode) {
+ switch (aidlPortMode) {
+ case PortMode.NONE:
+ return UsbPortStatus.MODE_NONE;
+ case PortMode.UFP:
+ return UsbPortStatus.MODE_UFP;
+ case PortMode.DFP:
+ return UsbPortStatus.MODE_DFP;
+ case PortMode.DRP:
+ return UsbPortStatus.MODE_DUAL;
+ case PortMode.AUDIO_ACCESSORY:
+ return UsbPortStatus.MODE_AUDIO_ACCESSORY;
+ case PortMode.DEBUG_ACCESSORY:
+ return UsbPortStatus.MODE_DEBUG_ACCESSORY;
+ default:
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, "Unrecognized aidlPortMode:"
+ + aidlPortMode);
+ return UsbPortStatus.MODE_NONE;
+ }
+ }
+
+ private int toSupportedModes(byte[] aidlPortModes) {
+ int supportedModes = UsbPortStatus.MODE_NONE;
+
+ for (byte aidlPortMode : aidlPortModes) {
+ supportedModes |= toPortMode(aidlPortMode);
+ }
+
+ return supportedModes;
+ }
+
+ /**
+ * Converts from AIDL defined contaminant protection constants to UsbPortStatus constants.
+ * AIDL does not gracefully support bitfield when combined with enums.
+ * Common to both ContaminantProtectionMode and ContaminantProtectionStatus.
+ */
+ private int toContaminantProtectionStatus(byte aidlContaminantProtection) {
+ switch (aidlContaminantProtection) {
+ case ContaminantProtectionStatus.NONE:
+ return UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+ case ContaminantProtectionStatus.FORCE_SINK:
+ return UsbPortStatus.CONTAMINANT_PROTECTION_SINK;
+ case ContaminantProtectionStatus.FORCE_SOURCE:
+ return UsbPortStatus.CONTAMINANT_PROTECTION_SOURCE;
+ case ContaminantProtectionStatus.FORCE_DISABLE:
+ return UsbPortStatus.CONTAMINANT_PROTECTION_FORCE_DISABLE;
+ case ContaminantProtectionStatus.DISABLED:
+ return UsbPortStatus.CONTAMINANT_PROTECTION_DISABLED;
+ default:
+ UsbPortManager.logAndPrint(Log.ERROR, mPw,
+ "Unrecognized aidlContaminantProtection:"
+ + aidlContaminantProtection);
+ return UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+ }
+ }
+
+ private int toSupportedContaminantProtectionModes(byte[] aidlModes) {
+ int supportedContaminantProtectionModes = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+
+ for (byte aidlMode : aidlModes) {
+ supportedContaminantProtectionModes |= toContaminantProtectionStatus(aidlMode);
+ }
+
+ return supportedContaminantProtectionModes;
+ }
+
+ @Override
+ public void notifyPortStatusChange(
+ android.hardware.usb.PortStatus[] currentPortStatus, int retval) {
+ if (!mUsbPortAidl.mSystemReady) {
+ return;
+ }
+
+ if (retval != Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, "port status enquiry failed");
+ return;
+ }
+
+ ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
+
+ int numStatus = currentPortStatus.length;
+ for (int i = 0; i < numStatus; i++) {
+ PortStatus current = currentPortStatus[i];
+ RawPortInfo temp = new RawPortInfo(current.portName,
+ toSupportedModes(current.supportedModes),
+ toSupportedContaminantProtectionModes(current
+ .supportedContaminantProtectionModes),
+ toPortMode(current.currentMode),
+ current.canChangeMode,
+ current.currentPowerRole,
+ current.canChangePowerRole,
+ current.currentDataRole,
+ current.canChangeDataRole,
+ current.supportsEnableContaminantPresenceProtection,
+ toContaminantProtectionStatus(current.contaminantProtectionStatus),
+ current.supportsEnableContaminantPresenceDetection,
+ current.contaminantDetectionStatus,
+ current.usbDataEnabled,
+ current.powerTransferLimited);
+ newPortInfo.add(temp);
+ UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback AIDL V1: "
+ + current.portName);
+ }
+ mPortManager.updatePorts(newPortInfo);
+ }
+
+ @Override
+ public void notifyRoleSwitchStatus(String portName, PortRole role, int retval,
+ long operationID) {
+ if (retval == Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.INFO, mPw, portName
+ + " role switch successful. opID:"
+ + operationID);
+ } else {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, portName + " role switch failed. err:"
+ + retval
+ + "opID:" + operationID);
+ }
+ }
+
+ @Override
+ public void notifyQueryPortStatus(String portName, int retval, long operationID) {
+ if (retval == Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.INFO, mPw, portName + ": opID:"
+ + operationID + " successful");
+ } else {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, portName + ": opID:"
+ + operationID + " failed. err:" + retval);
+ }
+ }
+
+ @Override
+ public void notifyEnableUsbDataStatus(String portName, boolean enable, int retval,
+ long operationID) {
+ if (retval == Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.INFO, mPw, "notifyEnableUsbDataStatus:"
+ + portName + ": opID:"
+ + operationID + " enable:" + enable);
+ } else {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, portName
+ + "notifyEnableUsbDataStatus: opID:"
+ + operationID + " failed. err:" + retval);
+ }
+ try {
+ sCallbacks.get(operationID).onOperationComplete(retval == Status.SUCCESS
+ ? USB_OPERATION_SUCCESS
+ : USB_OPERATION_ERROR_INTERNAL);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw,
+ "notifyEnableUsbDataStatus: Failed to call onOperationComplete",
+ e);
+ }
+ }
+
+ @Override
+ public void notifyContaminantEnabledStatus(String portName, boolean enable, int retval,
+ long operationID) {
+ if (retval == Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.INFO, mPw, "notifyContaminantEnabledStatus:"
+ + portName + ": opID:"
+ + operationID + " enable:" + enable);
+ } else {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, portName
+ + "notifyContaminantEnabledStatus: opID:"
+ + operationID + " failed. err:" + retval);
+ }
+ }
+
+ @Override
+ public void notifyLimitPowerTransferStatus(String portName, boolean limit, int retval,
+ long operationID) {
+ if (retval == Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.INFO, mPw, portName + ": opID:"
+ + operationID + " successful");
+ } else {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, portName
+ + "notifyLimitPowerTransferStatus: opID:"
+ + operationID + " failed. err:" + retval);
+ }
+ try {
+ IUsbOperationInternal callback = sCallbacks.get(operationID);
+ if (callback != null) {
+ sCallbacks.get(operationID).onOperationComplete(retval == Status.SUCCESS
+ ? USB_OPERATION_SUCCESS
+ : USB_OPERATION_ERROR_INTERNAL);
+ }
+ } catch (RemoteException e) {
+ logAndPrintException(mPw,
+ "enableLimitPowerTransfer: Failed to call onOperationComplete",
+ e);
+ }
+ }
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortHal.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortHal.java
new file mode 100644
index 0000000..90c8909
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHal.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2021 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.usb.hal.port;
+
+import android.annotation.IntDef;
+import android.hardware.usb.IUsbOperationInternal;
+import android.hardware.usb.UsbManager.UsbHalVersion;
+import android.os.RemoteException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.String;
+
+/**
+ * @hide
+ */
+public interface UsbPortHal {
+ /**
+ * Power role: This USB port can act as a source (provide power).
+ * @hide
+ */
+ public static final int HAL_POWER_ROLE_SOURCE = 1;
+
+ /**
+ * Power role: This USB port can act as a sink (receive power).
+ * @hide
+ */
+ public static final int HAL_POWER_ROLE_SINK = 2;
+
+ @IntDef(prefix = { "HAL_POWER_ROLE_" }, value = {
+ HAL_POWER_ROLE_SOURCE,
+ HAL_POWER_ROLE_SINK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface HalUsbPowerRole{}
+
+ /**
+ * Data role: This USB port can act as a host (access data services).
+ * @hide
+ */
+ public static final int HAL_DATA_ROLE_HOST = 1;
+
+ /**
+ * Data role: This USB port can act as a device (offer data services).
+ * @hide
+ */
+ public static final int HAL_DATA_ROLE_DEVICE = 2;
+
+ @IntDef(prefix = { "HAL_DATA_ROLE_" }, value = {
+ HAL_DATA_ROLE_HOST,
+ HAL_DATA_ROLE_DEVICE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface HalUsbDataRole{}
+
+ /**
+ * This USB port can act as a downstream facing port (host).
+ *
+ * @hide
+ */
+ public static final int HAL_MODE_DFP = 1;
+
+ /**
+ * This USB port can act as an upstream facing port (device).
+ *
+ * @hide
+ */
+ public static final int HAL_MODE_UFP = 2;
+ @IntDef(prefix = { "HAL_MODE_" }, value = {
+ HAL_MODE_DFP,
+ HAL_MODE_UFP,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface HalUsbPortMode{}
+
+ /**
+ * UsbPortManager would call this when the system is done booting.
+ */
+ public void systemReady();
+
+ /**
+ * Invoked to enable/disable contaminant presence detection on the USB port.
+ *
+ * @param portName Port Identifier.
+ * @param enable Enable contaminant presence detection when true.
+ * Disable when false.
+ * @param transactionId Used for tracking the current request and is passed down to the HAL
+ * implementation as needed.
+ */
+ public void enableContaminantPresenceDetection(String portName, boolean enable,
+ long transactionId);
+
+ /**
+ * Invoked to query port status of all the ports.
+ *
+ * @param transactionId Used for tracking the current request and is passed down to the HAL
+ * implementation as needed.
+ */
+ public void queryPortStatus(long transactionId);
+
+ /**
+ * Invoked to switch USB port mode.
+ *
+ * @param portName Port Identifier.
+ * @param mode New mode that the port is switching into.
+ * @param transactionId Used for tracking the current request and is passed down to the HAL
+ * implementation as needed.
+ */
+ public void switchMode(String portName, @HalUsbPortMode int mode, long transactionId);
+
+ /**
+ * Invoked to switch USB port power role.
+ *
+ * @param portName Port Identifier.
+ * @param powerRole New power role that the port is switching into.
+ * @param transactionId Used for tracking the current request and is passed down to the HAL
+ * implementation as needed.
+ */
+ public void switchPowerRole(String portName, @HalUsbPowerRole int powerRole,
+ long transactionId);
+
+ /**
+ * Invoked to switch USB port data role.
+ *
+ * @param portName Port Identifier.
+ * @param dataRole New data role that the port is switching into.
+ * @param transactionId Used for tracking the current request and is passed down to the HAL
+ * implementation as needed.
+ */
+ public void switchDataRole(String portName, @HalUsbDataRole int dataRole, long transactionId);
+
+ /**
+ * Invoked to query the version of current hal implementation.
+ */
+ public @UsbHalVersion int getUsbHalVersion() throws RemoteException;
+
+ /**
+ * Invoked to enable/disable UsbData on the specified port.
+ *
+ * @param portName Port Identifier.
+ * @param enable Enable USB data when true.
+ * Disable when false.
+ * @param transactionId Used for tracking the current request and is passed down to the HAL
+ * implementation as needed.
+ * @param callback callback object to be invoked to invoke the status of the operation upon
+ * completion.
+ * @param callback callback object to be invoked when the operation is complete.
+ * @return True when the operation is asynchronous. The caller of
+ * {@link UsbOperationCallbackInternal} must therefore call
+ * {@link UsbOperationCallbackInternal#waitForOperationComplete} for processing
+ * the result.
+ * False when the operation is synchronous. Caller can proceed reading the result
+ * through {@link UsbOperationCallbackInternal#getStatus}
+ */
+ public boolean enableUsbData(String portName, boolean enable, long transactionId,
+ IUsbOperationInternal callback);
+
+ /**
+ * Invoked to enableLimitPowerTransfer on the specified port.
+ *
+ * @param portName Port Identifier.
+ * @param limit limit power transfer when true. Port wouldn't charge or power USB accessoried
+ * when set.
+ * Lift power transfer restrictions when false.
+ * @param transactionId Used for tracking the current request and is passed down to the HAL
+ * implementation as needed.
+ * @param callback callback object to be invoked to invoke the status of the operation upon
+ * completion.
+ */
+ public void enableLimitPowerTransfer(String portName, boolean limit, long transactionId,
+ IUsbOperationInternal callback);
+}
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java
new file mode 100644
index 0000000..41f9fae
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 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.usb.hal.port;
+
+import static com.android.server.usb.UsbPortManager.logAndPrint;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.usb.hal.port.UsbPortHidl;
+import com.android.server.usb.hal.port.UsbPortAidl;
+import com.android.server.usb.UsbPortManager;
+
+import android.util.Log;
+/**
+ * Helper class that queries the underlying hal layer to populate UsbPortHal instance.
+ */
+public final class UsbPortHalInstance {
+
+ public static UsbPortHal getInstance(UsbPortManager portManager, IndentingPrintWriter pw) {
+
+ logAndPrint(Log.DEBUG, null, "Querying USB HAL version");
+ if (UsbPortHidl.isServicePresent(null)) {
+ logAndPrint(Log.INFO, null, "USB HAL HIDL present");
+ return new UsbPortHidl(portManager, pw);
+ }
+ if (UsbPortAidl.isServicePresent(null)) {
+ logAndPrint(Log.INFO, null, "USB HAL AIDL present");
+ return new UsbPortAidl(portManager, pw);
+ }
+
+ return null;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java
new file mode 100644
index 0000000..8a0370a
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2021 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.usb.hal.port;
+
+import static android.hardware.usb.UsbManager.USB_HAL_NOT_SUPPORTED;
+import static android.hardware.usb.UsbManager.USB_HAL_V1_0;
+import static android.hardware.usb.UsbManager.USB_HAL_V1_1;
+import static android.hardware.usb.UsbManager.USB_HAL_V1_2;
+import static android.hardware.usb.UsbManager.USB_HAL_V1_3;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_NOT_SUPPORTED;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_SUCCESS;
+import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
+import static android.hardware.usb.UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+
+import static com.android.server.usb.UsbPortManager.logAndPrint;
+import static com.android.server.usb.UsbPortManager.logAndPrintException;
+
+import android.annotation.Nullable;
+import android.hardware.usb.IUsbOperationInternal;
+import android.hardware.usb.UsbManager.UsbHalVersion;
+import android.hardware.usb.UsbPort;
+import android.hardware.usb.V1_0.IUsb;
+import android.hardware.usb.V1_0.PortRoleType;
+import android.hardware.usb.V1_0.Status;
+import android.hardware.usb.V1_1.PortStatus_1_1;
+import android.hardware.usb.V1_2.IUsbCallback;
+import android.hardware.usb.V1_0.PortRole;
+import android.hardware.usb.V1_2.PortStatus;
+import android.hidl.manager.V1_0.IServiceManager;
+import android.hidl.manager.V1_0.IServiceNotification;
+import android.os.IHwBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.usb.UsbPortManager;
+import com.android.server.usb.hal.port.RawPortInfo;
+
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+/**
+ *
+ */
+public final class UsbPortHidl implements UsbPortHal {
+ private static final String TAG = UsbPortHidl.class.getSimpleName();
+ // Cookie sent for usb hal death notification.
+ private static final int USB_HAL_DEATH_COOKIE = 1000;
+ // Proxy object for the usb hal daemon.
+ @GuardedBy("mLock")
+ private IUsb mProxy;
+ private UsbPortManager mPortManager;
+ public IndentingPrintWriter mPw;
+ // Mutex for all mutable shared state.
+ private final Object mLock = new Object();
+ // Callback when the UsbPort status is changed by the kernel.
+ private HALCallback mHALCallback;
+ private boolean mSystemReady;
+ // Workaround since HIDL HAL versions report UsbDataEnabled status in UsbPortStatus;
+ private static boolean sUsbDataEnabled = true;
+
+ public @UsbHalVersion int getUsbHalVersion() throws RemoteException {
+ int version;
+ synchronized(mLock) {
+ if (mProxy == null) {
+ throw new RemoteException("IUsb not initialized yet");
+ }
+ if (android.hardware.usb.V1_3.IUsb.castFrom(mProxy) != null) {
+ version = USB_HAL_V1_3;
+ } else if (android.hardware.usb.V1_2.IUsb.castFrom(mProxy) != null) {
+ version = USB_HAL_V1_2;
+ } else if (android.hardware.usb.V1_1.IUsb.castFrom(mProxy) != null) {
+ version = USB_HAL_V1_1;
+ } else {
+ version = USB_HAL_V1_0;
+ }
+ logAndPrint(Log.INFO, null, "USB HAL HIDL version: " + version);
+ return version;
+ }
+ }
+
+ final class DeathRecipient implements IHwBinder.DeathRecipient {
+ public IndentingPrintWriter pw;
+
+ DeathRecipient(IndentingPrintWriter pw) {
+ this.pw = pw;
+ }
+
+ @Override
+ public void serviceDied(long cookie) {
+ if (cookie == USB_HAL_DEATH_COOKIE) {
+ logAndPrint(Log.ERROR, pw, "Usb hal service died cookie: " + cookie);
+ synchronized (mLock) {
+ mProxy = null;
+ }
+ }
+ }
+ }
+
+ final class ServiceNotification extends IServiceNotification.Stub {
+ @Override
+ public void onRegistration(String fqName, String name, boolean preexisting) {
+ logAndPrint(Log.INFO, null, "Usb hal service started " + fqName + " " + name);
+ connectToProxy(null);
+ }
+ }
+
+ private void connectToProxy(IndentingPrintWriter pw) {
+ synchronized (mLock) {
+ if (mProxy != null) {
+ return;
+ }
+
+ try {
+ mProxy = IUsb.getService();
+ mProxy.linkToDeath(new DeathRecipient(pw), USB_HAL_DEATH_COOKIE);
+ mProxy.setCallback(mHALCallback);
+ mProxy.queryPortStatus();
+ //updateUsbHalVersion();
+ } catch (NoSuchElementException e) {
+ logAndPrintException(pw, "connectToProxy: usb hal service not found."
+ + " Did the service fail to start?", e);
+ } catch (RemoteException e) {
+ logAndPrintException(pw, "connectToProxy: usb hal service not responding", e);
+ }
+ }
+ }
+
+ @Override
+ public void systemReady() {
+ mSystemReady = true;
+ }
+
+ static boolean isServicePresent(IndentingPrintWriter pw) {
+ try {
+ IUsb.getService(true);
+ } catch (NoSuchElementException e) {
+ logAndPrintException(pw, "connectToProxy: usb hidl hal service not found.", e);
+ return false;
+ } catch (RemoteException e) {
+ logAndPrintException(pw, "IUSB hal service present but failed to get service", e);
+ }
+
+ return true;
+ }
+
+ public UsbPortHidl(UsbPortManager portManager, IndentingPrintWriter pw) {
+ mPortManager = Objects.requireNonNull(portManager);
+ mPw = pw;
+ mHALCallback = new HALCallback(null, mPortManager, this);
+ try {
+ ServiceNotification serviceNotification = new ServiceNotification();
+
+ boolean ret = IServiceManager.getService()
+ .registerForNotifications("android.hardware.usb@1.0::IUsb",
+ "", serviceNotification);
+ if (!ret) {
+ logAndPrint(Log.ERROR, null,
+ "Failed to register service start notification");
+ }
+ } catch (RemoteException e) {
+ logAndPrintException(null,
+ "Failed to register service start notification", e);
+ return;
+ }
+ connectToProxy(mPw);
+ }
+
+ @Override
+ public void enableContaminantPresenceDetection(String portName, boolean enable,
+ long transactionId) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !");
+ return;
+ }
+
+ try {
+ // Oneway call into the hal. Use the castFrom method from HIDL.
+ android.hardware.usb.V1_2.IUsb proxy =
+ android.hardware.usb.V1_2.IUsb.castFrom(mProxy);
+ proxy.enableContaminantPresenceDetection(portName, enable);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set contaminant detection", e);
+ } catch (ClassCastException e) {
+ logAndPrintException(mPw, "Method only applicable to V1.2 or above implementation",
+ e);
+ }
+ }
+ }
+
+ @Override
+ public void queryPortStatus(long transactionId) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !");
+ return;
+ }
+
+ try {
+ mProxy.queryPortStatus();
+ } catch (RemoteException e) {
+ logAndPrintException(null, "ServiceStart: Failed to query port status", e);
+ }
+ }
+ }
+
+ @Override
+ public void switchMode(String portId, @HalUsbPortMode int newMode, long transactionId) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !");
+ return;
+ }
+
+ PortRole newRole = new PortRole();
+ newRole.type = PortRoleType.MODE;
+ newRole.role = newMode;
+ try {
+ mProxy.switchRole(portId, newRole);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set the USB port mode: "
+ + "portId=" + portId
+ + ", newMode=" + UsbPort.modeToString(newRole.role), e);
+ }
+ }
+ }
+
+ @Override
+ public void switchPowerRole(String portId, @HalUsbPowerRole int newPowerRole,
+ long transactionId) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !");
+ return;
+ }
+
+ PortRole newRole = new PortRole();
+ newRole.type = PortRoleType.POWER_ROLE;
+ newRole.role = newPowerRole;
+ try {
+ mProxy.switchRole(portId, newRole);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set the USB power role: portId=" + portId
+ + ", newPowerRole=" + UsbPort.powerRoleToString(newRole.role), e);
+ }
+ }
+ }
+
+ @Override
+ public void enableLimitPowerTransfer(String portName, boolean limit, long transactionId,
+ IUsbOperationInternal callback) {
+ /* Not supported in HIDL hals*/
+ try {
+ callback.onOperationComplete(USB_OPERATION_ERROR_NOT_SUPPORTED);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to call onOperationComplete", e);
+ }
+ }
+
+ @Override
+ public void switchDataRole(String portId, @HalUsbDataRole int newDataRole, long transactionId) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !");
+ return;
+ }
+
+ PortRole newRole = new PortRole();
+ newRole.type = PortRoleType.DATA_ROLE;
+ newRole.role = newDataRole;
+ try {
+ mProxy.switchRole(portId, newRole);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set the USB data role: portId=" + portId
+ + ", newDataRole=" + UsbPort.dataRoleToString(newRole.role), e);
+ }
+ }
+ }
+
+ @Override
+ public boolean enableUsbData(String portName, boolean enable, long transactionId,
+ IUsbOperationInternal callback) {
+ int halVersion;
+
+ try {
+ halVersion = getUsbHalVersion();
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to query USB HAL version. opID:"
+ + transactionId
+ + " portId:" + portName, e);
+ return false;
+ }
+
+ if (halVersion != USB_HAL_V1_3) {
+ try {
+ callback.onOperationComplete(USB_OPERATION_ERROR_NOT_SUPPORTED);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to call onOperationComplete. opID:"
+ + transactionId
+ + " portId:" + portName, e);
+ }
+ return false;
+ }
+
+ boolean success;
+ synchronized(mLock) {
+ try {
+ android.hardware.usb.V1_3.IUsb proxy
+ = android.hardware.usb.V1_3.IUsb.castFrom(mProxy);
+ success = proxy.enableUsbDataSignal(enable);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed enableUsbData: opId:" + transactionId
+ + " portId=" + portName , e);
+ try {
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ } catch (RemoteException r) {
+ logAndPrintException(mPw, "Failed to call onOperationComplete. opID:"
+ + transactionId
+ + " portId:" + portName, r);
+ }
+ return false;
+ }
+ }
+ if (success) {
+ sUsbDataEnabled = enable;
+ }
+
+ try {
+ callback.onOperationComplete(success
+ ? USB_OPERATION_SUCCESS
+ : USB_OPERATION_ERROR_INTERNAL);
+ } catch (RemoteException r) {
+ logAndPrintException(mPw, "Failed to call onOperationComplete. opID:"
+ + transactionId
+ + " portId:" + portName, r);
+ }
+ return false;
+ }
+
+ private static class HALCallback extends IUsbCallback.Stub {
+ public IndentingPrintWriter mPw;
+ public UsbPortManager mPortManager;
+ public UsbPortHidl mUsbPortHidl;
+
+ HALCallback(IndentingPrintWriter pw, UsbPortManager portManager, UsbPortHidl usbPortHidl) {
+ this.mPw = pw;
+ this.mPortManager = portManager;
+ this.mUsbPortHidl = usbPortHidl;
+ }
+
+ public void notifyPortStatusChange(
+ ArrayList<android.hardware.usb.V1_0.PortStatus> currentPortStatus, int retval) {
+ if (!mUsbPortHidl.mSystemReady) {
+ return;
+ }
+
+ if (retval != Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, "port status enquiry failed");
+ return;
+ }
+
+ ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
+
+ for (android.hardware.usb.V1_0.PortStatus current : currentPortStatus) {
+ RawPortInfo temp = new RawPortInfo(current.portName,
+ current.supportedModes, CONTAMINANT_PROTECTION_NONE,
+ current.currentMode,
+ current.canChangeMode, current.currentPowerRole,
+ current.canChangePowerRole,
+ current.currentDataRole, current.canChangeDataRole,
+ false, CONTAMINANT_PROTECTION_NONE,
+ false, CONTAMINANT_DETECTION_NOT_SUPPORTED, sUsbDataEnabled,
+ false);
+ newPortInfo.add(temp);
+ UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_0: "
+ + current.portName);
+ }
+
+ mPortManager.updatePorts(newPortInfo);
+ }
+
+
+ public void notifyPortStatusChange_1_1(ArrayList<PortStatus_1_1> currentPortStatus,
+ int retval) {
+ if (!mUsbPortHidl.mSystemReady) {
+ return;
+ }
+
+ if (retval != Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, "port status enquiry failed");
+ return;
+ }
+
+ ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
+
+ int numStatus = currentPortStatus.size();
+ for (int i = 0; i < numStatus; i++) {
+ PortStatus_1_1 current = currentPortStatus.get(i);
+ RawPortInfo temp = new RawPortInfo(current.status.portName,
+ current.supportedModes, CONTAMINANT_PROTECTION_NONE,
+ current.currentMode,
+ current.status.canChangeMode, current.status.currentPowerRole,
+ current.status.canChangePowerRole,
+ current.status.currentDataRole, current.status.canChangeDataRole,
+ false, CONTAMINANT_PROTECTION_NONE,
+ false, CONTAMINANT_DETECTION_NOT_SUPPORTED, sUsbDataEnabled,
+ false);
+ newPortInfo.add(temp);
+ UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_1: "
+ + current.status.portName);
+ }
+ mPortManager.updatePorts(newPortInfo);
+ }
+
+ public void notifyPortStatusChange_1_2(
+ ArrayList<PortStatus> currentPortStatus, int retval) {
+ if (!mUsbPortHidl.mSystemReady) {
+ return;
+ }
+
+ if (retval != Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, "port status enquiry failed");
+ return;
+ }
+
+ ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
+
+ int numStatus = currentPortStatus.size();
+ for (int i = 0; i < numStatus; i++) {
+ PortStatus current = currentPortStatus.get(i);
+ RawPortInfo temp = new RawPortInfo(current.status_1_1.status.portName,
+ current.status_1_1.supportedModes,
+ current.supportedContaminantProtectionModes,
+ current.status_1_1.currentMode,
+ current.status_1_1.status.canChangeMode,
+ current.status_1_1.status.currentPowerRole,
+ current.status_1_1.status.canChangePowerRole,
+ current.status_1_1.status.currentDataRole,
+ current.status_1_1.status.canChangeDataRole,
+ current.supportsEnableContaminantPresenceProtection,
+ current.contaminantProtectionStatus,
+ current.supportsEnableContaminantPresenceDetection,
+ current.contaminantDetectionStatus,
+ sUsbDataEnabled,
+ false);
+ newPortInfo.add(temp);
+ UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_2: "
+ + current.status_1_1.status.portName);
+ }
+ mPortManager.updatePorts(newPortInfo);
+ }
+
+ public void notifyRoleSwitchStatus(String portName, PortRole role, int retval) {
+ if (retval == Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.INFO, mPw, portName + " role switch successful");
+ } else {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, portName + " role switch failed");
+ }
+ }
+ }
+}
diff --git a/services/wallpapereffectsgeneration/OWNERS b/services/wallpapereffectsgeneration/OWNERS
new file mode 100644
index 0000000..d2d3e2c0
--- /dev/null
+++ b/services/wallpapereffectsgeneration/OWNERS
@@ -0,0 +1,4 @@
+susharon@google.com
+shanh@google.com
+huiwu@google.com
+srazdan@google.com
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index e1be973..d5c846d 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -175,7 +175,10 @@
/**
* This flag specifies whether VoLTE availability is based on provisioning. By default this is
* false.
+ * Used for UCE to determine if EAB provisioning checks should be based on provisioning.
+ * @deprecated Use {@link Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE} instead.
*/
+ @Deprecated
public static final String
KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
@@ -879,7 +882,12 @@
/**
* Flag specifying whether provisioning is required for VoLTE, Video Telephony, and WiFi
* Calling.
+
+ * Combines VoLTE, VT, VoWiFI calling provisioning into one parameter.
+ * @deprecated Use {@link Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE} instead for
+ * finer-grained control.
*/
+ @Deprecated
public static final String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
= "carrier_volte_provisioning_required_bool";
@@ -893,7 +901,11 @@
* and enable the UT over IMS capability for the subscription when the subscription is loaded.
*
* The default value for this key is {@code false}.
+ *
+ * @deprecated Use {@link Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE} instead for
+ * determining if UT requires provisioning.
*/
+ @Deprecated
public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL =
"carrier_ut_provisioning_required_bool";
@@ -5173,6 +5185,95 @@
/** E911 RTP inactivity occurred when call is connected. */
public static final int E911_RTP_INACTIVITY_ON_CONNECTED = 4;
+ /**
+ * A bundle which specifies the MMTEL capability and registration technology
+ * that requires provisioning. If a tuple is not present, the
+ * framework will not require that the tuple requires provisioning before
+ * enabling the capability.
+ * <p> Possible keys in this bundle are
+ * <ul>
+ * <li>{@link #KEY_CAPABILITY_TYPE_VOICE_INT_ARRAY}</li>
+ * <li>{@link #KEY_CAPABILITY_TYPE_VIDEO_INT_ARRAY}</li>
+ * <li>{@link #KEY_CAPABILITY_TYPE_UT_INT_ARRAY}</li>
+ * <li>{@link #KEY_CAPABILITY_TYPE_SMS_INT_ARRAY}</li>
+ * <li>{@link #KEY_CAPABILITY_CALL_COMPOSER_INT_ARRAY}</li>
+ * </ul>
+ * <p> The values are defined in
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech}
+ */
+ public static final String KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE =
+ KEY_PREFIX + "mmtel_requires_provisioning_bundle";
+
+ /**
+ * This MmTelFeature supports Voice calling (IR.92)
+ * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE
+ */
+ public static final String KEY_CAPABILITY_TYPE_VOICE_INT_ARRAY =
+ KEY_PREFIX + "key_capability_type_voice_int_array";
+
+ /**
+ * This MmTelFeature supports Video (IR.94)
+ * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO
+ */
+ public static final String KEY_CAPABILITY_TYPE_VIDEO_INT_ARRAY =
+ KEY_PREFIX + "key_capability_type_video_int_array";
+
+ /**
+ * This MmTelFeature supports XCAP over Ut for supplementary services. (IR.92)
+ * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT
+ */
+ public static final String KEY_CAPABILITY_TYPE_UT_INT_ARRAY =
+ KEY_PREFIX + "key_capability_type_ut_int_array";
+
+ /**
+ * This MmTelFeature supports SMS (IR.92)
+ * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS
+ */
+ public static final String KEY_CAPABILITY_TYPE_SMS_INT_ARRAY =
+ KEY_PREFIX + "key_capability_type_sms_int_array";
+
+ /**
+ * This MmTelFeature supports Call Composer (section 2.4 of RCC.20)
+ * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_CALL_COMPOSER
+ */
+ public static final String KEY_CAPABILITY_CALL_COMPOSER_INT_ARRAY =
+ KEY_PREFIX + "key_capability_type_call_composer_int_array";
+
+ /**
+ * A bundle which specifies the RCS capability and registration technology
+ * that requires provisioning. If a tuple is not present, the
+ * framework will not require that the tuple requires provisioning before
+ * enabling the capability.
+ * <p> Possible keys in this bundle are
+ * <ul>
+ * <li>{@link #KEY_CAPABILITY_TYPE_OPTIONS_UCE_INT_ARRAY}</li>
+ * <li>{@link #KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY}</li>
+ * </ul>
+ * <p> The values are defined in
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech}
+ */
+ public static final String KEY_RCS_REQUIRES_PROVISIONING_BUNDLE =
+ KEY_PREFIX + "rcs_requires_provisioning_bundle";
+
+ /**
+ * This carrier supports User Capability Exchange using SIP OPTIONS as defined by the
+ * framework. If set, the RcsFeature should support capability exchange using SIP OPTIONS.
+ * If not set, this RcsFeature should not service capability requests.
+ * @see RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE
+ */
+ public static final String KEY_CAPABILITY_TYPE_OPTIONS_UCE_INT_ARRAY =
+ KEY_PREFIX + "key_capability_type_options_uce_int_array";
+
+ /**
+ * This carrier supports User Capability Exchange using a presence server as defined by the
+ * framework. If set, the RcsFeature should support capability exchange using a presence
+ * server. If not set, this RcsFeature should not publish capabilities or service capability
+ * requests using presence.
+ * @see RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE
+ */
+ public static final String KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY =
+ KEY_PREFIX + "key_capability_type_presence_uce_int_array";
+
private Ims() {}
private static PersistableBundle getDefaults() {
@@ -5209,6 +5310,20 @@
"+g.gsma.rcs.botversion=\"#=1,#=2\"",
"+g.gsma.rcs.cpimext"});
+ /**
+ * @see #KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE
+ */
+ PersistableBundle mmtel_requires_provisioning_int_array = new PersistableBundle();
+ defaults.putPersistableBundle(
+ KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE, mmtel_requires_provisioning_int_array);
+
+ /**
+ * @see #KEY_RCS_REQUIRES_PROVISIONING_BUNDLE
+ */
+ PersistableBundle rcs_requires_provisioning_int_array = new PersistableBundle();
+ defaults.putPersistableBundle(
+ KEY_RCS_REQUIRES_PROVISIONING_BUNDLE, rcs_requires_provisioning_int_array);
+
defaults.putBoolean(KEY_GRUU_ENABLED_BOOL, true);
defaults.putBoolean(KEY_SIP_OVER_IPSEC_ENABLED_BOOL, true);
defaults.putBoolean(KEY_KEEP_PDN_UP_IN_NO_VOPS_BOOL, false);
@@ -7572,6 +7687,10 @@
/** Specifies the PCO id for IPv4 Epdg server address */
public static final String KEY_EPDG_PCO_ID_IPV4_INT = KEY_PREFIX + "epdg_pco_id_ipv4_int";
+ /** Controls if the IKE tunnel setup supports EAP-AKA fast reauth */
+ public static final String KEY_SUPPORTS_EAP_AKA_FAST_REAUTH_BOOL =
+ KEY_PREFIX + "supports_eap_aka_fast_reauth_bool";
+
/** @hide */
@IntDef({AUTHENTICATION_METHOD_EAP_ONLY, AUTHENTICATION_METHOD_CERT})
public @interface AuthenticationMethodType {}
@@ -7718,6 +7837,7 @@
defaults.putBoolean(KEY_ADD_KE_TO_CHILD_SESSION_REKEY_BOOL, false);
defaults.putInt(KEY_EPDG_PCO_ID_IPV6_INT, 0);
defaults.putInt(KEY_EPDG_PCO_ID_IPV4_INT, 0);
+ defaults.putBoolean(KEY_SUPPORTS_EAP_AKA_FAST_REAUTH_BOOL, false);
return defaults;
}
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index 2758e12..b0ff949 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -163,6 +163,26 @@
return new SipDelegateManager(mContext, subscriptionId, sRcsCache, sTelephonyCache);
}
+
+ /**
+ * Create an instance of {@link ProvisioningManager} for the subscription id specified.
+ * <p>
+ * Provides a ProvisioningManager instance to carrier apps to update carrier provisioning
+ * information, as well as provides a callback so that apps can listen for changes
+ * in MMTEL/RCS provisioning
+ * @param subscriptionId The ID of the subscription that this ProvisioningManager will use.
+ * @throws IllegalArgumentException if the subscription is invalid.
+ * @return a ProvisioningManager instance with the specific subscription ID.
+ */
+ @NonNull
+ public ProvisioningManager getProvisioningManager(int subscriptionId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
+ throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
+ }
+
+ return new ProvisioningManager(subscriptionId);
+ }
+
private static IImsRcsController getIImsRcsControllerInterface() {
return IImsRcsController.Stub.asInterface(
TelephonyFrameworkInitializer
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index dbf4c99..677c1a9 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -34,6 +34,7 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.TelephonyManager;
+import android.telephony.ims.aidl.IFeatureProvisioningCallback;
import android.telephony.ims.aidl.IImsConfigCallback;
import android.telephony.ims.aidl.IRcsConfigCallback;
import android.telephony.ims.feature.MmTelFeature;
@@ -54,18 +55,12 @@
* IMS provisioning keys are defined per carrier or OEM using OMA-DM or other provisioning
* applications and may vary. It is up to the carrier and OEM applications to ensure that the
* correct provisioning keys are being used when integrating with a vendor's ImsService.
- *
- * Note: For compatibility purposes, the integer values [0 - 99] used in
- * {@link #setProvisioningIntValue(int, int)} have been reserved for existing provisioning keys
- * previously defined in the Android framework. Please do not redefine new provisioning keys in this
- * range or it may generate collisions with existing keys. Some common constants have also been
- * defined in this class to make integrating with other system apps easier.
- * @hide
*/
-@SystemApi
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
public class ProvisioningManager {
+ private static final String TAG = "ProvisioningManager";
+
/**@hide*/
@StringDef(prefix = "STRING_QUERY_RESULT_ERROR_", value = {
STRING_QUERY_RESULT_ERROR_GENERIC,
@@ -76,14 +71,18 @@
/**
* The query from {@link #getProvisioningStringValue(int)} has resulted in an unspecified error.
+ * @hide
*/
+ @SystemApi
public static final String STRING_QUERY_RESULT_ERROR_GENERIC =
"STRING_QUERY_RESULT_ERROR_GENERIC";
/**
* The query from {@link #getProvisioningStringValue(int)} has resulted in an error because the
* ImsService implementation was not ready for provisioning queries.
+ * @hide
*/
+ @SystemApi
public static final String STRING_QUERY_RESULT_ERROR_NOT_READY =
"STRING_QUERY_RESULT_ERROR_NOT_READY";
@@ -95,12 +94,16 @@
/**
* The integer result of provisioning for the queried key is disabled.
+ * @hide
*/
+ @SystemApi
public static final int PROVISIONING_VALUE_DISABLED = 0;
/**
* The integer result of provisioning for the queried key is enabled.
+ * @hide
*/
+ @SystemApi
public static final int PROVISIONING_VALUE_ENABLED = 1;
@@ -445,27 +448,31 @@
/**
* Override the user-defined WiFi Roaming enabled setting for this subscription, defined in
- * {@link SubscriptionManager#WFC_ROAMING_ENABLED_CONTENT_URI}, for the purposes of provisioning
- * the subscription for WiFi Calling.
+ * {@link android.telephony.SubscriptionManager#WFC_ROAMING_ENABLED_CONTENT_URI},
+ * for the purposes of provisioning the subscription for WiFi Calling.
*
- * @see #getProvisioningIntValue(int)
* @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
*/
+ @SystemApi
public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26;
/**
* Override the user-defined WiFi mode for this subscription, defined in
- * {@link SubscriptionManager#WFC_MODE_CONTENT_URI}, for the purposes of provisioning
- * this subscription for WiFi Calling.
+ * {@link android.telephony.SubscriptionManager#WFC_MODE_CONTENT_URI},
+ * for the purposes of provisioning this subscription for WiFi Calling.
*
* Valid values for this key are:
* {@link ImsMmTelManager#WIFI_MODE_WIFI_ONLY},
* {@link ImsMmTelManager#WIFI_MODE_CELLULAR_PREFERRED}, or
* {@link ImsMmTelManager#WIFI_MODE_WIFI_PREFERRED}.
*
- * @see #getProvisioningIntValue(int)
* @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
*/
+ @SystemApi
public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27;
/**
@@ -864,7 +871,9 @@
* <p>Value is in String format.
* @see #setProvisioningStringValue(int, String)
* @see #getProvisioningStringValue(int)
+ * @hide
*/
+ @SystemApi
public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67;
/**
@@ -886,7 +895,9 @@
/**
* Callback for IMS provisioning changes.
+ * @hide
*/
+ @SystemApi
public static class Callback {
private static class CallbackBinder extends IImsConfigCallback.Stub {
@@ -956,11 +967,105 @@
}
}
+ /**
+ * Callback for IMS provisioning feature changes.
+ */
+ public static class FeatureProvisioningCallback {
+
+ private static class CallbackBinder extends IFeatureProvisioningCallback.Stub {
+
+ private final FeatureProvisioningCallback mFeatureProvisioningCallback;
+ private Executor mExecutor;
+
+ private CallbackBinder(FeatureProvisioningCallback featureProvisioningCallback) {
+ mFeatureProvisioningCallback = featureProvisioningCallback;
+ }
+
+ @Override
+ public final void onFeatureProvisioningChanged(
+ int capability, int tech, boolean isProvisioned) {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() ->
+ mFeatureProvisioningCallback.onFeatureProvisioningChanged(
+ capability, tech, isProvisioned));
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override
+ public final void onRcsFeatureProvisioningChanged(
+ int capability, int tech, boolean isProvisioned) {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() ->
+ mFeatureProvisioningCallback.onRcsFeatureProvisioningChanged(
+ capability, tech, isProvisioned));
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ private void setExecutor(Executor executor) {
+ mExecutor = executor;
+ }
+ }
+
+ private final CallbackBinder mBinder = new CallbackBinder(this);
+
+ /**
+ * The IMS MMTEL provisioning has changed for a specific capability and IMS
+ * registration technology.
+ * @param capability The MMTEL capability that provisioning has changed for.
+ * @param tech The IMS registration technology associated with the MMTEL capability that
+ * provisioning has changed for.
+ * @param isProvisioned {@code true} if the capability is provisioned for the technology
+ * specified, or {@code false} if the capability is not provisioned for the technology
+ * specified.
+ */
+ public void onFeatureProvisioningChanged(
+ @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int tech,
+ boolean isProvisioned) {
+ // Base Implementation
+ }
+
+ /**
+ * The IMS RCS provisioning has changed for a specific capability and IMS
+ * registration technology.
+ * @param capability The RCS capability that provisioning has changed for.
+ * @param tech The IMS registration technology associated with the RCS capability that
+ * provisioning has changed for.
+ * @param isProvisioned {@code true} if the capability is provisioned for the technology
+ * specified, or {@code false} if the capability is not provisioned for the technology
+ * specified.
+ */
+ public void onRcsFeatureProvisioningChanged(
+ @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int tech,
+ boolean isProvisioned) {
+ // Base Implementation
+ }
+
+ /**@hide*/
+ public final IFeatureProvisioningCallback getBinder() {
+ return mBinder;
+ }
+
+ /**@hide*/
+ public void setExecutor(Executor executor) {
+ mBinder.setExecutor(executor);
+ }
+ }
+
private int mSubId;
/**
* The callback for RCS provisioning changes.
+ * @hide
*/
+ @SystemApi
public static class RcsProvisioningCallback {
private static class CallbackBinder extends IRcsConfigCallback.Stub {
@@ -1098,7 +1203,9 @@
* @param subId The ID of the subscription that this ProvisioningManager will use.
* @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
* @throws IllegalArgumentException if the subscription is invalid.
+ * @hide
*/
+ @SystemApi
public static @NonNull ProvisioningManager createForSubscriptionId(int subId) {
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
throw new IllegalArgumentException("Invalid subscription ID");
@@ -1107,7 +1214,9 @@
return new ProvisioningManager(subId);
}
- private ProvisioningManager(int subId) {
+ /**@hide*/
+ //@SystemApi
+ public ProvisioningManager(int subId) {
mSubId = subId;
}
@@ -1116,6 +1225,12 @@
*
* When the subscription associated with this callback is removed (SIM removed, ESIM swap,
* etc...), this callback will automatically be removed.
+ *
+ * <p> Requires Permission:
+ * <ul>
+ * <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li>
+ * </ul>
+ *
* @param executor The {@link Executor} to call the callback methods on
* @param callback The provisioning callbackto be registered.
* @see #unregisterProvisioningChangedCallback(Callback)
@@ -1126,7 +1241,9 @@
* the {@link ImsService} associated with the subscription is not available. This can happen if
* the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
* reason.
+ * @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void registerProvisioningChangedCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull Callback callback) throws ImsException {
@@ -1144,12 +1261,20 @@
* Unregister an existing {@link Callback}. When the subscription associated with this
* callback is removed (SIM removed, ESIM swap, etc...), this callback will automatically be
* removed. If this method is called for an inactive subscription, it will result in a no-op.
+ *
+ * <p> Requires Permission:
+ * <ul>
+ * <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li>
+ * </ul>
+ *
* @param callback The existing {@link Callback} to be removed.
* @see #registerProvisioningChangedCallback(Executor, Callback)
*
* @throws IllegalArgumentException if the subscription associated with this callback is
* invalid.
+ * @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void unregisterProvisioningChangedCallback(@NonNull Callback callback) {
try {
@@ -1160,6 +1285,62 @@
}
/**
+ * Register a new {@link FeatureProvisioningCallback}, which is used to listen for
+ * IMS feature provisioning updates.
+ * <p>
+ * When the subscription associated with this callback is removed (SIM removed,
+ * ESIM swap,etc...), this callback will automatically be removed.
+ *
+ * <p> Requires Permission:
+ * <ul>
+ * <li> android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE,</li>
+ * <li>{@link android.Manifest.permission#READ_PRECISE_PHONE_STATE},</li>
+ * <li>or that the caller has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+ * </ul>
+ *
+ * @param executor The executor that the callback methods will be called on.
+ * @param callback The callback instance being registered.
+ * @throws ImsException if the subscription associated with this callback is
+ * valid, but the {@link ImsService the service crashed, for example. See
+ * {@link ImsException#getCode()} for a more detailed reason.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public void registerFeatureProvisioningChangedCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull FeatureProvisioningCallback callback) throws ImsException {
+ callback.setExecutor(executor);
+ try {
+ getITelephony().registerFeatureProvisioningChangedCallback(mSubId,
+ callback.getBinder());
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
+ } catch (RemoteException | IllegalStateException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Unregisters a previously registered {@link FeatureProvisioningCallback}
+ * instance. When the subscription associated with this
+ * callback is removed (SIM removed, ESIM swap, etc...), this callback will
+ * automatically be removed. If this method is called for an inactive
+ * subscription, it will result in a no-op.
+ *
+ * @param callback The existing {@link FeatureProvisioningCallback} to be removed.
+ * @see #registerFeatureProvisioningChangedCallback(Executor, FeatureProvisioningCallback)
+ */
+ public void unregisterFeatureProvisioningChangedCallback(
+ @NonNull FeatureProvisioningCallback callback) {
+ try {
+ getITelephony().unregisterFeatureProvisioningChangedCallback(mSubId,
+ callback.getBinder());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
* Query for the integer value associated with the provided key.
*
* This operation is blocking and should not be performed on the UI thread.
@@ -1168,7 +1349,9 @@
* @return an integer value for the provided key, or
* {@link ImsConfigImplBase#CONFIG_RESULT_UNKNOWN} if the key doesn't exist.
* @throws IllegalArgumentException if the key provided was invalid.
+ * @hide
*/
+ @SystemApi
@WorkerThread
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public int getProvisioningIntValue(int key) {
@@ -1188,7 +1371,9 @@
* @return a String value for the provided key, {@code null} if the key doesn't exist, or
* {@link StringResultError} if there was an error getting the value for the provided key.
* @throws IllegalArgumentException if the key provided was invalid.
+ * @hide
*/
+ @SystemApi
@WorkerThread
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public @Nullable @StringResultError String getProvisioningStringValue(int key) {
@@ -1209,7 +1394,15 @@
* @param key An integer that represents the provisioning key, which is defined by the OEM.
* @param value a integer value for the provided key.
* @return the result of setting the configuration value.
+ * @hide
+ *
+ * Note: For compatibility purposes, the integer values [0 - 99] used in
+ * {@link #setProvisioningIntValue(int, int)} have been reserved for existing provisioning keys
+ * previously defined in the Android framework. Please do not redefine new provisioning keys
+ * in this range or it may generate collisions with existing keys. Some common constants have
+ * also been defined in this class to make integrating with other system apps easier.
*/
+ @SystemApi
@WorkerThread
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public @ImsConfigImplBase.SetConfigResult int setProvisioningIntValue(int key, int value) {
@@ -1229,7 +1422,9 @@
* should be appropriately namespaced to avoid collision.
* @param value a String value for the provided key.
* @return the result of setting the configuration value.
+ * @hide
*/
+ @SystemApi
@WorkerThread
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public @ImsConfigImplBase.SetConfigResult int setProvisioningStringValue(int key,
@@ -1249,8 +1444,14 @@
* does not support the capability/technology combination specified, this operation will be a
* no-op.
*
- * @see CarrierConfigManager#KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL
- * @see CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+ * <p>Requires Permission:
+ * <ul>
+ * <li>{@link android.Manifest.permission#MODIFY_PHONE_STATE},</li>
+ * <li>or that the calling app has carrier privileges (see</li>
+ * <li>{@link TelephonyManager#hasCarrierPrivileges}).</li>
+ * </ul>
+ *
+ * @see CarrierConfigManager.Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE
* @param isProvisioned true if the device is provisioned for UT over IMS, false otherwise.
*/
@WorkerThread
@@ -1258,9 +1459,10 @@
public void setProvisioningStatusForCapability(
@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int tech, boolean isProvisioned) {
+
try {
getITelephony().setImsProvisioningStatusForCapability(mSubId, capability, tech,
- isProvisioned);
+ isProvisioned);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -1274,14 +1476,21 @@
* {@link ImsRegistrationImplBase.ImsRegistrationTech} combination specified, this method will
* always return {@code true}.
*
- * @see CarrierConfigManager#KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL
- * @see CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+ * <p> Requires Permission:
+ * <ul>
+ * <li>android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE,</li>
+ * <li>{@link android.Manifest.permission#READ_PRECISE_PHONE_STATE},</li>
+ * <li>or that the caller has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+ * </ul>
+ *
+ * @see CarrierConfigManager.Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE
* @return true if the device is provisioned for the capability or does not require
* provisioning, false if the capability does require provisioning and has not been
* provisioned yet.
*/
@WorkerThread
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
public boolean getProvisioningStatusForCapability(
@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int tech) {
@@ -1299,17 +1508,93 @@
* {@link RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag} this method will always return
* {@code true}.
*
- * @see CarrierConfigManager#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
+ * @see CarrierConfigManager.Ims#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
* @return true if the device is provisioned for the capability or does not require
* provisioning, false if the capability does require provisioning and has not been
* provisioned yet.
+ * @deprecated Use {@link #getRcsProvisioningStatusForCapability(int, int)} instead,
+ * as this only retrieves provisioning information for
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
+ * @hide
*/
+ @Deprecated
+ @SystemApi
@WorkerThread
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean getRcsProvisioningStatusForCapability(
@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
try {
- return getITelephony().getRcsProvisioningStatusForCapability(mSubId, capability);
+ return getITelephony().getRcsProvisioningStatusForCapability(mSubId, capability,
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Get the provisioning status for the IMS RCS capability specified.
+ *
+ * If provisioning is not required for the queried
+ * {@link RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag} this method
+ * will always return {@code true}.
+ *
+ * <p> Requires Permission:
+ * <ul>
+ * <li>{@link android.Manifest.permission#READ_PRECISE_PHONE_STATE},</li>
+ * <li>or that the caller has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+ * </ul>
+ *
+ * @see CarrierConfigManager.Ims#KEY_RCS_REQUIRES_PROVISIONING_BUNDLE
+ * @return true if the device is provisioned for the capability or does not require
+ * provisioning, false if the capability does require provisioning and has not been
+ * provisioned yet.
+ */
+ @WorkerThread
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public boolean getRcsProvisioningStatusForCapability(
+ @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int tech) {
+ try {
+ return getITelephony().getRcsProvisioningStatusForCapability(mSubId, capability, tech);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Set the provisioning status for the IMS RCS capability using the specified subscription.
+ *
+ * <p> Requires Permission:
+ * <ul>
+ * <li>{@link android.Manifest.permission#MODIFY_PHONE_STATE}</li>
+ * <li>or that the caller has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+ * </ul>
+
+ * Provisioning may or may not be required, depending on the carrier configuration. If
+ * provisioning is not required for the carrier associated with this subscription or the device
+ * does not support the capability/technology combination specified, this operation will be a
+ * no-op.
+ *
+ * @see CarrierConfigManager#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
+ * @param isProvisioned true if the device is provisioned for the RCS capability specified,
+ * false otherwise.
+ * @deprecated Use {@link #setRcsProvisioningStatusForCapability(int, int, boolean)} instead,
+ * as this method only sets provisioning information for
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
+ * @hide
+ */
+ @Deprecated
+ @SystemApi
+ @WorkerThread
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setRcsProvisioningStatusForCapability(
+ @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+ boolean isProvisioned) {
+ try {
+ getITelephony().setRcsProvisioningStatusForCapability(mSubId, capability,
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE, isProvisioned);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -1323,7 +1608,14 @@
* does not support the capability/technology combination specified, this operation will be a
* no-op.
*
- * @see CarrierConfigManager#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
+ * <p> Requires Permission:
+ * <ul>
+ * <li>{@link android.Manifest.permission#MODIFY_PHONE_STATE},</li>
+ * <li>or that the caller has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+ * </ul>
+ *
+ * @see CarrierConfigManager.Ims#KEY_RCS_REQUIRES_PROVISIONING_BUNDLE
* @param isProvisioned true if the device is provisioned for the RCS capability specified,
* false otherwise.
*/
@@ -1331,31 +1623,92 @@
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setRcsProvisioningStatusForCapability(
@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
- boolean isProvisioned) {
+ @ImsRegistrationImplBase.ImsRegistrationTech int tech, boolean isProvisioned) {
try {
getITelephony().setRcsProvisioningStatusForCapability(mSubId, capability,
- isProvisioned);
+ tech, isProvisioned);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
/**
+ * Indicates whether provisioning for the MMTEL capability and IMS registration technology
+ * specified is required or not
+ *
+ * <p> Requires Permission:
+ * <ul>
+ * <li> android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE,</li>
+ * <li>{@link android.Manifest.permission#READ_PRECISE_PHONE_STATE},</li>
+ * <li> or that the caller has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+ * </ul>
+ *
+ * @return true if provisioning is required for the MMTEL capability and IMS
+ * registration technology specified, false if it is not required.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public boolean isProvisioningRequiredForCapability(
+ @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int tech) {
+ try {
+ return getITelephony().isProvisioningRequiredForCapability(mSubId, capability, tech);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+
+ /**
+ * Indicates whether provisioning for the RCS capability and IMS registration technology
+ * specified is required or not
+ *
+ * <p> Requires Permission:
+ * <ul>
+ * <li> android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE,</li>
+ * <li>{@link android.Manifest.permission#READ_PRECISE_PHONE_STATE},</li>
+ * <li> or that the caller has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+ * </ul>
+ *
+ * @return true if provisioning is required for the RCS capability and IMS
+ * registration technology specified, false if it is not required.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public boolean isRcsProvisioningRequiredForCapability(
+ @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int tech) {
+ try {
+ return getITelephony().isRcsProvisioningRequiredForCapability(mSubId, capability, tech);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+
+ /**
* 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 TelephonyManager#hasCarrierPrivileges}).
+ *
+ * <p>Requires Permission:
+ * <ul>
+ * <li>{@link Manifest.permission#MODIFY_PHONE_STATE},</li>
+ * <li>or that the calling app has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+ * </ul>
+ *
* @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
*/
+ @SystemApi
@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.");
}
+
try {
getITelephony().notifyRcsAutoConfigurationReceived(mSubId, config, isCompressed);
} catch (RemoteException e) {
@@ -1374,7 +1727,9 @@
* <p>Contains {@link #EXTRA_SUBSCRIPTION_ID} to specify the subscription index for which
* the intent is valid. and {@link #EXTRA_STATUS} to specify RCS VoLTE single registration
* status.
+ * @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION)
@SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE =
@@ -1382,7 +1737,9 @@
/**
* Integer extra to specify subscription index.
+ * @hide
*/
+ @SystemApi
public static final String EXTRA_SUBSCRIPTION_ID =
"android.telephony.ims.extra.SUBSCRIPTION_ID";
@@ -1392,22 +1749,30 @@
* <p>The value can be {@link #STATUS_CAPABLE}, {@link #STATUS_DEVICE_NOT_CAPABLE},
* {@link #STATUS_CARRIER_NOT_CAPABLE}, or bitwise OR of
* {@link #STATUS_DEVICE_NOT_CAPABLE} and {@link #STATUS_CARRIER_NOT_CAPABLE}.
+ * @hide
*/
+ @SystemApi
public static final String EXTRA_STATUS = "android.telephony.ims.extra.STATUS";
/**
* RCS VoLTE single registration is supported by the device and carrier.
+ * @hide
*/
+ @SystemApi
public static final int STATUS_CAPABLE = 0;
/**
* RCS VoLTE single registration is not supported by the device.
+ * @hide
*/
+ @SystemApi
public static final int STATUS_DEVICE_NOT_CAPABLE = 0x01;
/**
* RCS VoLTE single registration is not supported by the carrier
+ * @hide
*/
+ @SystemApi
public static final int STATUS_CARRIER_NOT_CAPABLE = 0x01 << 1;
/**
@@ -1417,11 +1782,14 @@
* provisioning is done using autoconfiguration, then these parameters shall be
* sent in the HTTP get request to fetch the RCS provisioning. RCS client
* configuration must be provided by the application before registering for the
- * provisioning status events {@link #registerRcsProvisioningCallback()}
+ * provisioning status events
+ * {@link #registerRcsProvisioningCallback(Executor, RcsProvisioningCallback)}
* When the IMS/RCS service receives the RCS client configuration, it will detect
* the change in the configuration, and trigger the auto-configuration as needed.
* @param rcc RCS client configuration {@link RcsClientConfiguration}
+ * @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION)
public void setRcsClientConfiguration(
@NonNull RcsClientConfiguration rcc) throws ImsException {
@@ -1442,18 +1810,21 @@
* <ul>
* <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li>
* <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li>
- * <li>or that the caller has carrier privileges (see
+ * <li>or that the calling app has carrier privileges (see
* {@link TelephonyManager#hasCarrierPrivileges()}).</li>
* </ul>
+ *
* @return true if IMS single registration is capable at this time, or false otherwise
- * @throws ImsException If the remote ImsService is not available for
- * any reason or the subscription associated with this instance is no
- * longer active. See {@link ImsException#getCode()} for more
- * information.
+ * @throws ImsException If the remote ImsService is not available for any reason or
+ * the subscription associated with this instance is no longer active.
+ * See {@link ImsException#getCode()} for more information.
* @see PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION for whether or not this
* device supports IMS single registration.
+ * @hide
*/
- @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
public boolean isRcsVolteSingleRegistrationCapable() throws ImsException {
try {
@@ -1480,8 +1851,6 @@
* <ul>
* <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li>
* <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li>
- * <li>or that the caller has carrier privileges (see
- * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
* </ul>
*
* @param executor The {@link Executor} to call the callback methods on
@@ -1499,8 +1868,11 @@
* params (See {@link #setRcsClientConfiguration}) and re register the
* callback.
* See {@link ImsException#getCode()} for a more detailed reason.
+ * @hide
*/
- @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
public void registerRcsProvisioningCallback(
@NonNull @CallbackExecutor Executor executor,
@@ -1527,8 +1899,6 @@
* <ul>
* <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li>
* <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li>
- * <li>or that the caller has carrier privileges (see
- * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
* </ul>
*
* @param callback The existing {@link RcsProvisioningCallback} to be
@@ -1536,8 +1906,11 @@
* @see #registerRcsProvisioningCallback(Executor, RcsProvisioningCallback)
* @throws IllegalArgumentException if the subscription associated with
* this callback is invalid.
+ * @hide
*/
- @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
public void unregisterRcsProvisioningCallback(
@NonNull RcsProvisioningCallback callback) {
@@ -1558,9 +1931,10 @@
* {@link RcsProvisioningCallback} may expect to receive
* {@link RcsProvisioningCallback#onConfigurationReset}, then
* {@link RcsProvisioningCallback#onConfigurationChanged} when the new
- * RCS configuration is received and notified by
- * {@link #notifyRcsAutoConfigurationReceived}
+ * RCS configuration is received and notified by {@link #notifyRcsAutoConfigurationReceived}
+ * @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION)
public void triggerRcsReconfiguration() {
try {
diff --git a/telephony/java/android/telephony/ims/aidl/IFeatureProvisioningCallback.aidl b/telephony/java/android/telephony/ims/aidl/IFeatureProvisioningCallback.aidl
new file mode 100644
index 0000000..63ec4aa
--- /dev/null
+++ b/telephony/java/android/telephony/ims/aidl/IFeatureProvisioningCallback.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.telephony.ims.aidl;
+
+/**
+ * Provides callback interface for FeatureProvisioning when a value has changed.
+ *
+ * {@hide}
+ */
+oneway interface IFeatureProvisioningCallback {
+ void onFeatureProvisioningChanged(int capability, int tech, boolean isProvisioned);
+ void onRcsFeatureProvisioningChanged(int capability, int tech, boolean isProvisioned);
+}
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 7fdf21b..ad2e9e1 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -394,6 +394,13 @@
public @interface MmTelCapability {}
/**
+ * Undefined capability type for initialization
+ * This is used to check the upper range of MmTel capability
+ * {@hide}
+ */
+ public static final int CAPABILITY_TYPE_NONE = 0;
+
+ /**
* This MmTelFeature supports Voice calling (IR.92)
*/
public static final int CAPABILITY_TYPE_VOICE = 1 << 0;
@@ -419,6 +426,12 @@
public static final int CAPABILITY_TYPE_CALL_COMPOSER = 1 << 4;
/**
+ * This is used to check the upper range of MmTel capability
+ * {@hide}
+ */
+ public static final int CAPABILITY_TYPE_MAX = CAPABILITY_TYPE_CALL_COMPOSER + 1;
+
+ /**
* @hide
*/
@Override
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 11cf0e3..af7373b 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -59,9 +59,7 @@
/**
* Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
* this class and provide implementations of the RcsFeature methods that they support.
- * @hide
*/
-@SystemApi
public class RcsFeature extends ImsFeature {
private static final String LOG_TAG = "RcsFeature";
@@ -186,14 +184,14 @@
* Contains the capabilities defined and supported by a {@link RcsFeature} in the
* form of a bitmask. The capabilities that are used in the RcsFeature are
* defined as:
- * {@link RcsUceAdatper.RcsImsCapabilityFlag#CAPABILITY_TYPE_OPTIONS_UCE}
- * {@link RceUceAdapter.RcsImsCapabilityFlag#CAPABILITY_TYPE_PRESENCE_UCE}
+ * {RcsUceAdapter.RcsImsCapabilityFlag#CAPABILITY_TYPE_OPTIONS_UCE}
+ * {RcsUceAdapter.RcsImsCapabilityFlag#CAPABILITY_TYPE_PRESENCE_UCE}
*
* The enabled capabilities of this RcsFeature will be set by the framework
- * using {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}.
+ * using {#changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}.
* After the capabilities have been set, the RcsFeature may then perform the necessary bring up
* of the capability and notify the capability status as true using
- * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the
+ * {#notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the
* framework that the capability is available for usage.
*/
public static class RcsImsCapabilities extends Capabilities {
@@ -227,10 +225,18 @@
public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1;
/**
+ * This is used to check the upper range of RCS capability
+ * {@hide}
+ */
+ public static final int CAPABILITY_TYPE_MAX = CAPABILITY_TYPE_PRESENCE_UCE + 1;
+
+ /**
* Create a new {@link RcsImsCapabilities} instance with the provided capabilities.
* @param capabilities The capabilities that are supported for RCS in the form of a
* bitfield.
+ * @hide
*/
+ @SystemApi
public RcsImsCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
super(capabilities);
}
@@ -243,17 +249,29 @@
super(capabilities.getMask());
}
+ /**
+ * @hide
+ */
@Override
+ @SystemApi
public void addCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
super.addCapabilities(capabilities);
}
+ /**
+ * @hide
+ */
@Override
+ @SystemApi
public void removeCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
super.removeCapabilities(capabilities);
}
+ /**
+ * @hide
+ */
@Override
+ @SystemApi
public boolean isCapable(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
return super.isCapable(capabilities);
}
@@ -270,7 +288,9 @@
* Method stubs called from the framework will be called asynchronously. To specify the
* {@link Executor} that the methods stubs will be called, use
* {@link RcsFeature#RcsFeature(Executor)} instead.
+ * @hide
*/
+ @SystemApi
public RcsFeature() {
super();
// Run on the Binder threads that call them.
@@ -282,7 +302,9 @@
* framework.
* @param executor The executor for the framework to use when executing the methods overridden
* by the implementation of RcsFeature.
+ * @hide
*/
+ @SystemApi
public RcsFeature(@NonNull Executor executor) {
super();
if (executor == null) {
@@ -301,7 +323,7 @@
* @hide
*/
@Override
- public void initialize(Context context, int slotId) {
+ public void initialize(@NonNull Context context, @NonNull int slotId) {
super.initialize(context, slotId);
// Notify that the RcsFeature is ready.
mExecutor.execute(() -> onFeatureReady());
@@ -313,8 +335,10 @@
* requests. To change the status of the capabilities
* {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called.
* @return A copy of the current RcsFeature capability status.
+ * @hide
*/
@Override
+ @SystemApi
public @NonNull final RcsImsCapabilities queryCapabilityStatus() {
return new RcsImsCapabilities(super.queryCapabilityStatus());
}
@@ -324,7 +348,9 @@
* this signals to the framework that the capability has been initialized and is ready.
* Call {@link #queryCapabilityStatus()} to return the current capability status.
* @param capabilities The current capability status of the RcsFeature.
+ * @hide
*/
+ @SystemApi
public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities capabilities) {
if (capabilities == null) {
throw new IllegalArgumentException("RcsImsCapabilities must be non-null!");
@@ -341,7 +367,9 @@
* @param capability The capability that we are querying the configuration for.
* @param radioTech The radio technology type that we are querying.
* @return true if the capability is enabled, false otherwise.
+ * @hide
*/
+ @SystemApi
public boolean queryCapabilityConfiguration(
@RcsUceAdapter.RcsImsCapabilityFlag int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
@@ -364,8 +392,10 @@
* be called for each capability change that resulted in an error.
* @param request The request to change the capability.
* @param callback To notify the framework that the result of the capability changes.
+ * @hide
*/
@Override
+ @SystemApi
public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
@NonNull CapabilityCallbackProxy callback) {
// Base Implementation - Override to provide functionality
@@ -385,7 +415,9 @@
* event to the framework.
* @return An instance of {@link RcsCapabilityExchangeImplBase} that implements capability
* exchange if it is supported by the device.
+ * @hide
*/
+ @SystemApi
public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(
@NonNull CapabilityExchangeEventListener listener) {
// Base Implementation, override to implement functionality
@@ -395,20 +427,28 @@
/**
* Remove the given CapabilityExchangeImplBase instance.
* @param capExchangeImpl The {@link RcsCapabilityExchangeImplBase} instance to be destroyed.
+ * @hide
*/
+ @SystemApi
public void destroyCapabilityExchangeImpl(
@NonNull RcsCapabilityExchangeImplBase capExchangeImpl) {
// Override to implement the process of destroying RcsCapabilityExchangeImplBase instance.
}
- /**{@inheritDoc}*/
+ /**{@inheritDoc}
+ * @hide
+ */
@Override
+ @SystemApi
public void onFeatureRemoved() {
}
- /**{@inheritDoc}*/
+ /**{@inheritDoc}
+ * @hide
+ */
@Override
+ @SystemApi
public void onFeatureReady() {
}
@@ -448,7 +488,9 @@
* has already been created in the framework.
* @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange
* event to the framework.
+ * @hide
*/
+ @SystemApi
private void initRcsCapabilityExchangeImplBase(
@NonNull CapabilityExchangeEventListener listener) {
synchronized (mLock) {
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 3b151a4..ac5565b 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -34,7 +34,6 @@
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.internal.util.ArrayUtils;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.CancellationException;
@@ -51,9 +50,7 @@
* <p>
* Note: There is no guarantee on the thread that the calls from the framework will be called on. It
* is the implementors responsibility to handle moving the calls to a working thread if required.
- * @hide
*/
-@SystemApi
public class ImsRegistrationImplBase {
private static final String LOG_TAG = "ImsRegistrationImplBase";
@@ -94,6 +91,12 @@
*/
public static final int REGISTRATION_TECH_NR = 3;
+ /**
+ * This is used to check the upper range of registration tech
+ * {@hide}
+ */
+ public static final int REGISTRATION_TECH_MAX = REGISTRATION_TECH_NR + 1;
+
// Registration states, used to notify new ImsRegistrationImplBase#Callbacks of the current
// state.
// The unknown state is set as the initialization state. This is so that we do not call back
@@ -109,7 +112,9 @@
* Method stubs called from the framework will be called asynchronously. To specify the
* {@link Executor} that the methods stubs will be called, use
* {@link ImsRegistrationImplBase#ImsRegistrationImplBase(Executor)} instead.
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public ImsRegistrationImplBase() {
super();
}
@@ -119,7 +124,9 @@
* framework.
* @param executor The executor for the framework to use when executing the methods overridden
* by the implementation of ImsRegistration.
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public ImsRegistrationImplBase(@NonNull Executor executor) {
super();
mExecutor = executor;
@@ -250,7 +257,9 @@
* If the SIP delegate feature tag configuration has changed, then this method will be
* called in order to let the ImsService know that it can pick up these changes in the IMS
* registration.
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public void updateSipDelegateRegistration() {
// Stub implementation, ImsService should implement this
}
@@ -266,7 +275,9 @@
* <p>
* This should not affect the registration of features managed by the ImsService itself, such as
* feature tags related to MMTEL registration.
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public void triggerSipDelegateDeregistration() {
// Stub implementation, ImsService should implement this
}
@@ -284,7 +295,9 @@
* be carrier specific.
* @param sipReason The reason associated with the SIP error code. {@code null} if there was no
* reason associated with the error.
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public void triggerFullNetworkRegistration(@IntRange(from = 100, to = 699) int sipCode,
@Nullable String sipReason) {
// Stub implementation, ImsService should implement this
@@ -295,7 +308,9 @@
* Notify the framework that the device is connected to the IMS network.
*
* @param imsRadioTech the radio access technology.
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public final void onRegistered(@ImsRegistrationTech int imsRadioTech) {
onRegistered(new ImsRegistrationAttributes.Builder(imsRadioTech).build());
}
@@ -304,7 +319,9 @@
* Notify the framework that the device is connected to the IMS network.
*
* @param attributes The attributes associated with the IMS registration.
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public final void onRegistered(@NonNull ImsRegistrationAttributes attributes) {
updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERED);
mCallbacks.broadcastAction((c) -> {
@@ -320,7 +337,9 @@
* Notify the framework that the device is trying to connect the IMS network.
*
* @param imsRadioTech the radio access technology.
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public final void onRegistering(@ImsRegistrationTech int imsRadioTech) {
onRegistering(new ImsRegistrationAttributes.Builder(imsRadioTech).build());
}
@@ -329,7 +348,9 @@
* Notify the framework that the device is trying to connect the IMS network.
*
* @param attributes The attributes associated with the IMS registration.
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public final void onRegistering(@NonNull ImsRegistrationAttributes attributes) {
updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERING);
mCallbacks.broadcastAction((c) -> {
@@ -356,7 +377,9 @@
* result.
*
* @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public final void onDeregistered(ImsReasonInfo info) {
updateToDisconnectedState(info);
// ImsReasonInfo should never be null.
@@ -377,7 +400,9 @@
* {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and
* {@link #REGISTRATION_TECH_CROSS_SIM}.
* @param info The {@link ImsReasonInfo} for the failure to change technology.
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
ImsReasonInfo info) {
final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
@@ -396,7 +421,9 @@
*
* The {@link Uri}s are not guaranteed to be different between subsequent calls.
* @param uris changed uris
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public final void onSubscriberAssociatedUriChanged(Uri[] uris) {
synchronized (mLock) {
mUris = ArrayUtils.cloneOrNull(uris);
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index be54cec..bce7a24 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -55,6 +55,7 @@
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.RcsClientConfiguration;
import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.aidl.IFeatureProvisioningCallback;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsConfigCallback;
@@ -1992,6 +1993,18 @@
void unregisterImsProvisioningChangedCallback(int subId, IImsConfigCallback callback);
/**
+ * Register an IMS provisioning change callback with Telephony.
+ */
+ void registerFeatureProvisioningChangedCallback(int subId,
+ IFeatureProvisioningCallback callback);
+
+ /**
+ * unregister an existing IMS provisioning change callback.
+ */
+ void unregisterFeatureProvisioningChangedCallback(int subId,
+ IFeatureProvisioningCallback callback);
+
+ /**
* Set the provisioning status for the IMS MmTel capability using the specified subscription.
*/
void setImsProvisioningStatusForCapability(int subId, int capability, int tech,
@@ -2005,19 +2018,12 @@
/**
* Get the provisioning status for the IMS Rcs capability specified.
*/
- boolean getRcsProvisioningStatusForCapability(int subId, int capability);
+ boolean getRcsProvisioningStatusForCapability(int subId, int capability, int tech);
/**
* Set the provisioning status for the IMS Rcs capability using the specified subscription.
*/
- void setRcsProvisioningStatusForCapability(int subId, int capability,
- boolean isProvisioned);
-
- /** Is the capability and tech flagged as provisioned in the cache */
- boolean isMmTelCapabilityProvisionedInCache(int subId, int capability, int tech);
-
- /** Set the provisioning for the capability and tech in the cache */
- void cacheMmTelCapabilityProvisioning(int subId, int capability, int tech,
+ void setRcsProvisioningStatusForCapability(int subId, int capability, int tech,
boolean isProvisioned);
/**
@@ -2523,4 +2529,18 @@
* @return the service name of the modem service which bind to.
*/
String getModemService();
+
+ /**
+ * Is Provisioning required for capability
+ * @return true if provisioning is required for the MMTEL capability and IMS
+ * registration technology specified, false if it is not required.
+ */
+ boolean isProvisioningRequiredForCapability(int subId, int capability, int tech);
+
+ /**
+ * Is RCS Provisioning required for capability
+ * @return true if provisioning is required for the RCS capability and IMS
+ * registration technology specified, false if it is not required.
+ */
+ boolean isRcsProvisioningRequiredForCapability(int subId, int capability, int tech);
}
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index 21c3f76..f518d53 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -24,6 +24,7 @@
import android.graphics.Color;
import android.os.Build;
import android.telephony.UiccPortInfo;
+import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.GsmAlphabet;
@@ -934,6 +935,13 @@
}
/**
+ * Strip all the trailing 'F' characters of a string if exists and compare.
+ */
+ public static boolean compareIgnoreTrailingFs(String a, String b) {
+ return TextUtils.equals(a, b) || TextUtils.equals(stripTrailingFs(a), stripTrailingFs(b));
+ }
+
+ /**
* Converts a character of [0-9a-fA-F] to its hex value in a byte. If the character is not a
* hex number, 0 will be returned.
*/
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index 22acc03..d960e94 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -108,6 +108,15 @@
super.entireScreenCovered()
}
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ // This test doesn't work in shell transitions because of b/215885246
+ assumeFalse(isShellTransitionsEnabled)
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+
companion object {
/**
* Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
index 0b1748a..535612a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
@@ -17,7 +17,9 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
+import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.server.wm.traces.common.FlickerComponentName
import com.android.server.wm.traces.parser.toFlickerComponent
@@ -47,4 +49,28 @@
}
launcherStrategy.launch(appName, expectedPackage)
}
+
+ fun startDialogThemedActivity(wmHelper: WindowManagerStateHelper) {
+ val button = uiDevice.wait(Until.findObject(By.res(getPackage(),
+ "start_dialog_themed_activity_btn")), FIND_TIMEOUT)
+
+ require(button != null) {
+ "Button not found, this usually happens when the device " +
+ "was left in an unknown state (e.g. Screen turned off)"
+ }
+ button.click()
+ wmHelper.waitForAppTransitionIdle()
+ wmHelper.waitForFullScreenApp(
+ ActivityOptions.DIALOG_THEMED_ACTIVITY_COMPONENT_NAME.toFlickerComponent())
+ }
+ fun dismissDialog(wmHelper: WindowManagerStateHelper) {
+ val dialog = uiDevice.wait(
+ Until.findObject(By.text("Dialog for test")), FIND_TIMEOUT)
+
+ // Pressing back key to dismiss the dialog
+ if (dialog != null) {
+ uiDevice.pressBack()
+ wmHelper.waitForAppTransitionIdle()
+ }
+ }
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
new file mode 100644
index 0000000..429541c
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.ime
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Presubmit
+import android.view.Surface
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
+import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test IME snapshot mechanism won't apply when transitioning from non-IME focused dialog activity.
+ * To run this test: `atest FlickerTests:LaunchAppShowImeAndDialogThemeAppTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class LaunchAppShowImeAndDialogThemeAppTest(private val testSpec: FlickerTestParameter) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ setup {
+ eachRun {
+ testApp.launchViaIntent(wmHelper)
+ wmHelper.waitImeShown()
+ testApp.startDialogThemedActivity(wmHelper)
+ }
+ }
+ teardown {
+ eachRun {
+ testApp.exit()
+ }
+ }
+ transitions {
+ testApp.dismissDialog(wmHelper)
+ }
+ }
+ }
+
+ /**
+ * Checks that [FlickerComponentName.IME] layer becomes visible during the transition
+ */
+ @FlakyTest(bugId = 215884488)
+ @Test
+ fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible()
+
+ /**
+ * Checks that [FlickerComponentName.IME] layer is visible at the end of the transition
+ */
+ @Presubmit
+ @Test
+ fun imeLayerExistsEnd() {
+ testSpec.assertLayersEnd {
+ this.isVisible(FlickerComponentName.IME)
+ }
+ }
+
+ /**
+ * Checks that [FlickerComponentName.IME_SNAPSHOT] layer is invisible always.
+ */
+ @Presubmit
+ @Test
+ fun imeSnapshotNotVisible() {
+ testSpec.assertLayers {
+ this.isInvisible(FlickerComponentName.IME_SNAPSHOT)
+ }
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(
+ repetitions = 3,
+ supportedRotations = listOf(Surface.ROTATION_0),
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ )
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index 0ad0a03..5e06f11 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -18,8 +18,8 @@
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
+import android.view.Display
import android.view.Surface
-import android.view.WindowManagerPolicyConstants
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -41,7 +41,9 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.common.ConditionList
import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.common.WindowManagerConditionsFactory
import org.junit.Assume.assumeFalse
import org.junit.Assume.assumeTrue
import org.junit.FixMethodOrder
@@ -63,6 +65,12 @@
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
+ private val waitConditionSetup = ConditionList(listOf(
+ WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY),
+ WindowManagerConditionsFactory.hasLayersAnimating().negate(),
+ WindowManagerConditionsFactory.isHomeActivityVisible()
+ ))
+
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
@@ -73,8 +81,7 @@
}
eachRun {
device.pressRecentApps()
- wmHelper.waitImeGone()
- wmHelper.waitForAppTransitionIdle()
+ wmHelper.waitFor(waitConditionSetup)
this.setRotation(testSpec.startRotation)
}
}
@@ -231,11 +238,8 @@
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 1,
- supportedRotations = listOf(Surface.ROTATION_0),
- supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
- )
+ repetitions = 5,
+ supportedRotations = listOf(Surface.ROTATION_0)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index e07a8f9..5450610 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -122,6 +122,11 @@
@Test
override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 213852103)
+ @Test
+ override fun entireScreenCovered() = super.entireScreenCovered()
+
companion object {
/**
* Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index 6e5c600..a85dcc5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -127,7 +127,7 @@
* The `isAppWindowInvisible` step is optional because we log once per frame, upon logging,
* the window may be visible or not depending on what was processed until that moment.
*/
- @Presubmit
+ @FlakyTest(bugId = 203538234)
@Test
fun appWindowBecomesVisible() {
testSpec.assertWm {
@@ -240,7 +240,7 @@
* it cannot use the regular assertion (check over time), because on lock screen neither
* the app not the launcher are visible, and there is no top visible window.
*/
- @Presubmit
+ @FlakyTest(bugId = 203538234)
@Test
override fun appWindowReplacesLauncherAsTopWindow() {
testSpec.assertWm {
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 0a88f6b..9e371e5 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -97,5 +97,16 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+ <activity android:name=".DialogThemedActivity"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.DialogThemedActivity"
+ android:configChanges="orientation|screenSize"
+ android:theme="@style/DialogTheme"
+ android:label="DialogThemedActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
index 2620ff4..baaf707 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
@@ -31,4 +31,9 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Finish activity" />
+ <Button
+ android:id="@+id/start_dialog_themed_activity_btn"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Start dialog themed activity" />
</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
index 87a61a8..746b0f4c 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
@@ -27,4 +27,15 @@
<style name="CutoutNever">
<item name="android:windowLayoutInDisplayCutoutMode">never</item>
</style>
-</resources>
\ No newline at end of file
+
+ <style name="DialogTheme" parent="@android:style/Theme.DeviceDefault">
+ <item name="android:windowAnimationStyle">@null</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBackground">@null</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowIsFloating">true</item>
+ <item name="android:backgroundDimEnabled">false</item>
+ <item name="android:windowSoftInputMode">stateUnchanged</item>
+ </style>
+</resources>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index baf36ab..13adb68 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -56,4 +56,8 @@
public static final ComponentName LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".LaunchNewTaskActivity");
+ public static final String DIALOG_THEMED_ACTIVITY = "DialogThemedActivity";
+ public static final ComponentName DIALOG_THEMED_ACTIVITY_COMPONENT_NAME =
+ new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".DialogThemedActivity");
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/DialogThemedActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/DialogThemedActivity.java
new file mode 100644
index 0000000..27606d8
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/DialogThemedActivity.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class DialogThemedActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_simple);
+ getWindow().getDecorView().setBackgroundColor(Color.TRANSPARENT);
+ TextView textView = new TextView(this);
+ textView.setText("This is a test dialog");
+ textView.setTextColor(Color.BLACK);
+ LinearLayout layout = new LinearLayout(this);
+ layout.setBackgroundColor(Color.GREEN);
+ layout.addView(textView);
+
+ // Create a dialog with dialog-themed activity
+ AlertDialog dialog = new AlertDialog.Builder(this)
+ .setView(layout)
+ .setTitle("Dialog for test")
+ .create();
+ final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(MATCH_PARENT,
+ MATCH_PARENT);
+ attrs.flags = FLAG_DIM_BEHIND | FLAG_ALT_FOCUSABLE_IM;
+ dialog.getWindow().getDecorView().setLayoutParams(attrs);
+ dialog.setCanceledOnTouchOutside(true);
+ dialog.show();
+ dialog.setOnDismissListener((d) -> finish());
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java
index 05da717..bb200f1 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java
@@ -16,6 +16,8 @@
package com.android.server.wm.flicker.testapp;
+import android.content.Intent;
+import android.widget.Button;
import android.widget.EditText;
public class ImeActivityAutoFocus extends ImeActivity {
@@ -26,5 +28,9 @@
EditText editTextField = findViewById(R.id.plain_text_input);
editTextField.requestFocus();
+
+ Button startThemedActivityButton = findViewById(R.id.start_dialog_themed_activity_btn);
+ startThemedActivityButton.setOnClickListener(
+ button -> startActivity(new Intent(this, DialogThemedActivity.class)));
}
}
diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py
index 3131f56..99f77fe 100755
--- a/tools/fonts/fontchain_linter.py
+++ b/tools/fonts/fontchain_linter.py
@@ -54,6 +54,7 @@
'or': 'Orya',
'pa': 'Guru',
'pt': 'Latn',
+ 'ru': 'Latn',
'sk': 'Latn',
'sl': 'Latn',
'sq': 'Latn',
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt b/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
index 900c214..a6fd9bb 100644
--- a/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
@@ -31,7 +31,9 @@
CallingIdentityTokenDetector.ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK,
CallingIdentityTokenDetector.ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY,
CallingIdentityTokenDetector.ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY,
- CallingSettingsNonUserGetterMethodsDetector.ISSUE_NON_USER_GETTER_CALLED
+ CallingSettingsNonUserGetterMethodsDetector.ISSUE_NON_USER_GETTER_CALLED,
+ EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION,
+ EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION
)
override val api: Int
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt b/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt
new file mode 100644
index 0000000..8011b36
--- /dev/null
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.lint
+
+import com.android.tools.lint.detector.api.AnnotationInfo
+import com.android.tools.lint.detector.api.AnnotationOrigin
+import com.android.tools.lint.detector.api.AnnotationUsageInfo
+import com.android.tools.lint.detector.api.AnnotationUsageType
+import com.android.tools.lint.detector.api.ConstantEvaluator
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiAnnotation
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiMethod
+import org.jetbrains.uast.UElement
+
+/**
+ * Lint Detector that ensures that any method overriding a method annotated
+ * with @EnforcePermission is also annotated with the exact same annotation.
+ * The intent is to surface the effective permission checks to the service
+ * implementations.
+ */
+class EnforcePermissionDetector : Detector(), SourceCodeScanner {
+
+ val ENFORCE_PERMISSION = "android.annotation.EnforcePermission"
+
+ override fun applicableAnnotations(): List<String> {
+ return listOf(ENFORCE_PERMISSION)
+ }
+
+ private fun areAnnotationsEquivalent(
+ context: JavaContext,
+ anno1: PsiAnnotation,
+ anno2: PsiAnnotation
+ ): Boolean {
+ if (anno1.qualifiedName != anno2.qualifiedName) {
+ return false
+ }
+ val attr1 = anno1.parameterList.attributes
+ val attr2 = anno2.parameterList.attributes
+ if (attr1.size != attr2.size) {
+ return false
+ }
+ for (i in attr1.indices) {
+ if (attr1[i].name != attr2[i].name) {
+ return false
+ }
+ val v1 = ConstantEvaluator.evaluate(context, attr1[i].value)
+ val v2 = ConstantEvaluator.evaluate(context, attr2[i].value)
+ if (v1 != v2) {
+ return false
+ }
+ }
+ return true
+ }
+
+ override fun visitAnnotationUsage(
+ context: JavaContext,
+ element: UElement,
+ annotationInfo: AnnotationInfo,
+ usageInfo: AnnotationUsageInfo
+ ) {
+ if (usageInfo.type == AnnotationUsageType.EXTENDS) {
+ val newClass = element.sourcePsi?.parent?.parent as PsiClass
+ val extendedClass: PsiClass = usageInfo.referenced as PsiClass
+ val newAnnotation = newClass.getAnnotation(ENFORCE_PERMISSION)
+ val extendedAnnotation = extendedClass.getAnnotation(ENFORCE_PERMISSION)!!
+
+ val location = context.getLocation(element)
+ val newClassName = newClass.qualifiedName
+ val extendedClassName = extendedClass.qualifiedName
+ if (newAnnotation == null) {
+ val msg = "The class $newClassName extends the class $extendedClassName which " +
+ "is annotated with @EnforcePermission. The same annotation must be used " +
+ "on $newClassName."
+ context.report(ISSUE_MISSING_ENFORCE_PERMISSION, element, location, msg)
+ } else if (!areAnnotationsEquivalent(context, newAnnotation, extendedAnnotation)) {
+ val msg = "The class $newClassName is annotated with ${newAnnotation.text} " +
+ "which differs from the parent class $extendedClassName: " +
+ "${extendedAnnotation.text}. The same annotation must be used for " +
+ "both classes."
+ context.report(ISSUE_MISMATCHING_ENFORCE_PERMISSION, element, location, msg)
+ }
+ } else if (usageInfo.type == AnnotationUsageType.METHOD_OVERRIDE &&
+ annotationInfo.origin == AnnotationOrigin.METHOD) {
+ val overridingMethod = element.sourcePsi as PsiMethod
+ val overriddenMethod = usageInfo.referenced as PsiMethod
+ val overridingAnnotation = overridingMethod.getAnnotation(ENFORCE_PERMISSION)
+ val overriddenAnnotation = overriddenMethod.getAnnotation(ENFORCE_PERMISSION)!!
+
+ val location = context.getLocation(element)
+ val overridingClass = overridingMethod.parent as PsiClass
+ val overriddenClass = overriddenMethod.parent as PsiClass
+ val overridingName = "${overridingClass.name}.${overridingMethod.name}"
+ val overriddenName = "${overriddenClass.name}.${overriddenMethod.name}"
+ if (overridingAnnotation == null) {
+ val msg = "The method $overridingName overrides the method $overriddenName which " +
+ "is annotated with @EnforcePermission. The same annotation must be used " +
+ "on $overridingName"
+ context.report(ISSUE_MISSING_ENFORCE_PERMISSION, element, location, msg)
+ } else if (!areAnnotationsEquivalent(
+ context, overridingAnnotation, overriddenAnnotation)) {
+ val msg = "The method $overridingName is annotated with " +
+ "${overridingAnnotation.text} which differs from the overridden " +
+ "method $overriddenName: ${overriddenAnnotation.text}. The same " +
+ "annotation must be used for both methods."
+ context.report(ISSUE_MISMATCHING_ENFORCE_PERMISSION, element, location, msg)
+ }
+ }
+ }
+
+ companion object {
+ val EXPLANATION = """
+ The @EnforcePermission annotation is used to indicate that the underlying binder code
+ has already verified the caller's permissions before calling the appropriate method. The
+ verification code is usually generated by the AIDL compiler, which also takes care of
+ annotating the generated Java code.
+
+ In order to surface that information to platform developers, the same annotation must be
+ used on the implementation class or methods.
+ """
+
+ val ISSUE_MISSING_ENFORCE_PERMISSION: Issue = Issue.create(
+ id = "MissingEnforcePermissionAnnotation",
+ briefDescription = "Missing @EnforcePermission annotation on Binder method",
+ explanation = EXPLANATION,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.ERROR,
+ implementation = Implementation(
+ EnforcePermissionDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ val ISSUE_MISMATCHING_ENFORCE_PERMISSION: Issue = Issue.create(
+ id = "MismatchingEnforcePermissionAnnotation",
+ briefDescription = "Incorrect @EnforcePermission annotation on Binder method",
+ explanation = EXPLANATION,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.ERROR,
+ implementation = Implementation(
+ EnforcePermissionDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+ }
+}
diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt b/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt
new file mode 100644
index 0000000..f5f4ebe
--- /dev/null
+++ b/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+
+@Suppress("UnstableApiUsage")
+class EnforcePermissionDetectorTest : LintDetectorTest() {
+ override fun getDetector(): Detector = EnforcePermissionDetector()
+
+ override fun getIssues(): List<Issue> = listOf(
+ EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION,
+ EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION
+ )
+
+ override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+ fun testDoesNotDetectIssuesCorrectAnnotationOnClass() {
+ lint().files(java(
+ """
+ package test.pkg;
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public class TestClass1 extends IFoo.Stub {
+ public void testMethod() {}
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ fun testDoesNotDetectIssuesCorrectAnnotationOnMethod() {
+ lint().files(java(
+ """
+ package test.pkg;
+ import android.annotation.EnforcePermission;
+ public class TestClass2 extends IFooMethod.Stub {
+ @Override
+ @EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void testMethod() {}
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ fun testDetectIssuesMismatchingAnnotationOnClass() {
+ lint().files(java(
+ """
+ package test.pkg;
+ @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET)
+ public class TestClass3 extends IFoo.Stub {
+ public void testMethod() {}
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expect("""src/test/pkg/TestClass3.java:3: Error: The class test.pkg.TestClass3 is \
+annotated with @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET) \
+which differs from the parent class IFoo.Stub: \
+@android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE). The \
+same annotation must be used for both classes. [MismatchingEnforcePermissionAnnotation]
+public class TestClass3 extends IFoo.Stub {
+ ~~~~~~~~~
+1 errors, 0 warnings""".addLineContinuation())
+ }
+
+ fun testDetectIssuesMismatchingAnnotationOnMethod() {
+ lint().files(java(
+ """
+ package test.pkg;
+ public class TestClass4 extends IFooMethod.Stub {
+ @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET)
+ public void testMethod() {}
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expect("""src/test/pkg/TestClass4.java:4: Error: The method TestClass4.testMethod is \
+annotated with @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET) \
+which differs from the overridden method Stub.testMethod: \
+@android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE). The same \
+annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+ public void testMethod() {}
+ ~~~~~~~~~~
+1 errors, 0 warnings""".addLineContinuation())
+ }
+
+ fun testDetectIssuesMissingAnnotationOnClass() {
+ lint().files(java(
+ """
+ package test.pkg;
+ public class TestClass5 extends IFoo.Stub {
+ public void testMethod() {}
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expect("""src/test/pkg/TestClass5.java:2: Error: The class test.pkg.TestClass5 extends \
+the class IFoo.Stub which is annotated with @EnforcePermission. The same annotation must be \
+used on test.pkg.TestClass5. [MissingEnforcePermissionAnnotation]
+public class TestClass5 extends IFoo.Stub {
+ ~~~~~~~~~
+1 errors, 0 warnings""".addLineContinuation())
+ }
+
+ fun testDetectIssuesMissingAnnotationOnMethod() {
+ lint().files(java(
+ """
+ package test.pkg;
+ public class TestClass6 extends IFooMethod.Stub {
+ public void testMethod() {}
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expect("""src/test/pkg/TestClass6.java:3: Error: The method TestClass6.testMethod \
+overrides the method Stub.testMethod which is annotated with @EnforcePermission. The same \
+annotation must be used on TestClass6.testMethod [MissingEnforcePermissionAnnotation]
+ public void testMethod() {}
+ ~~~~~~~~~~
+1 errors, 0 warnings""".addLineContinuation())
+ }
+
+ /* Stubs */
+
+ private val interfaceIFooStub: TestFile = java(
+ """
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public interface IFoo {
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public static abstract class Stub implements IFoo {
+ @Override
+ public void testMethod() {}
+ }
+ public void testMethod();
+ }
+ """
+ ).indented()
+
+ private val interfaceIFooMethodStub: TestFile = java(
+ """
+ public interface IFooMethod {
+ public static abstract class Stub implements IFooMethod {
+ @Override
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void testMethod() {}
+ }
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void testMethod();
+ }
+ """
+ ).indented()
+
+ private val manifestPermissionStub: TestFile = java(
+ """
+ package android.Manifest;
+ class permission {
+ public static final String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE";
+ public static final String INTERNET = "android.permission.INTERNET";
+ }
+ """
+ ).indented()
+
+ private val enforcePermissionAnnotationStub: TestFile = java(
+ """
+ package android.annotation;
+ public @interface EnforcePermission {}
+ """
+ ).indented()
+
+ private val stubs = arrayOf(interfaceIFooStub, interfaceIFooMethodStub,
+ manifestPermissionStub, enforcePermissionAnnotationStub)
+
+ // Substitutes "backslash + new line" with an empty string to imitate line continuation
+ private fun String.addLineContinuation(): String = this.trimIndent().replace("\\\n", "")
+}
diff --git a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
index 3b75660..459696e 100644
--- a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -1262,6 +1262,26 @@
}
/**
+ * Notifies the wificond daemon that the WiFi framework has successfully updated the Country
+ * Code of the driver. The wificond daemon needs this notification if the device does not
+ * support the NL80211_CMD_REG_CHANGED (otherwise it will find out on its own). The wificond
+ * updates in internal state in response to this Country Code update.
+ *
+ * @return true on success, false otherwise.
+ */
+ public boolean notifyCountryCodeChanged() {
+ try {
+ if (mWificond != null) {
+ mWificond.notifyCountryCodeChanged();
+ return true;
+ }
+ } catch (RemoteException e1) {
+ Log.e(TAG, "Failed to notify country code changed due to remote exception");
+ }
+ return false;
+ }
+
+ /**
* Register the provided callback handler for SoftAp events. The interface must first be created
* using {@link #setupInterfaceForSoftApMode(String)}. The callback registration is valid until
* the interface is deleted using {@link #tearDownSoftApInterface(String)} (no deregistration
diff --git a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
index 3fb2301..4032a7b 100644
--- a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
@@ -1137,6 +1137,26 @@
assertEquals(capaExpected, capaActual);
}
+ /**
+ * Tests notifyCountryCodeChanged
+ */
+ @Test
+ public void testNotifyCountryCodeChanged() throws Exception {
+ doNothing().when(mWificond).notifyCountryCodeChanged();
+ assertTrue(mWificondControl.notifyCountryCodeChanged());
+ verify(mWificond).notifyCountryCodeChanged();
+ }
+
+ /**
+ * Tests notifyCountryCodeChanged with RemoteException
+ */
+ @Test
+ public void testNotifyCountryCodeChangedRemoteException() throws Exception {
+ doThrow(new RemoteException()).when(mWificond).notifyCountryCodeChanged();
+ assertFalse(mWificondControl.notifyCountryCodeChanged());
+ verify(mWificond).notifyCountryCodeChanged();
+ }
+
// Create a ArgumentMatcher which captures a SingleScanSettings parameter and checks if it
// matches the provided frequency set and ssid set.
private class ScanMatcher implements ArgumentMatcher<SingleScanSettings> {