diff options
428 files changed, 8548 insertions, 4338 deletions
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java index 531436ecaf0c..fd4b1060bcd1 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java @@ -307,6 +307,41 @@ public final class AppSearchSession { } } + /** + * Removes {@link GenericDocument}s from the index by Query. Documents will be removed if they + * match the {@code queryExpression} in given namespaces and schemaTypes which is set via + * {@link SearchSpec.Builder#addNamespace} and {@link SearchSpec.Builder#addSchema}. + * + * <p> An empty {@code queryExpression} matches all documents. + * + * <p> An empty set of namespaces or schemaTypes matches all namespaces or schemaTypes in + * the current database. + * + * @param queryExpression Query String to search. + * @param searchSpec Defines what and how to remove + * @param executor Executor on which to invoke the callback. + * @param callback Callback to receive errors resulting from removing the documents. If the + * operation succeeds, the callback will be invoked with {@code null}. + */ + public void removeByQuery(@NonNull String queryExpression, + @NonNull SearchSpec searchSpec, + @NonNull @CallbackExecutor Executor executor, + @NonNull Consumer<AppSearchResult<Void>> callback) { + Objects.requireNonNull(queryExpression); + Objects.requireNonNull(searchSpec); + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + try { + mService.removeByQuery(mDatabaseName, queryExpression, searchSpec.getBundle(), + new IAppSearchResultCallback.Stub() { + public void onResult(AppSearchResult result) { + executor.execute(() -> callback.accept(result)); + } + }); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + // TODO(b/162450968) port query() and SearchResults.java to platform. - // TODO(b/162450968) port removeByQuery() to platform. } diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl index 4a981022da73..62e60d7f4ab5 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl +++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl @@ -119,13 +119,14 @@ interface IAppSearchManager { * @param databaseName The databaseName this query for. * @param queryExpression String to search for * @param searchSpecBundle SearchSpec bundle - * @param callback {@link AndroidFuture}<{@link AppSearchResult}<{@link SearchResults}>> + * @param callback {@link IAppSearchResultCallback#onResult} will be called with an + * {@link AppSearchResult}<{@link Void}>. */ void removeByQuery( in String databaseName, in String queryExpression, in Bundle searchSpecBundle, - in AndroidFuture<AppSearchResult> callback); + in IAppSearchResultCallback callback); /** * Creates and initializes AppSearchImpl for the calling app. diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java index 8269799d5a0d..c9dd89c3b6d1 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -225,21 +225,21 @@ public class AppSearchManagerService extends SystemService { @NonNull String databaseName, @NonNull String queryExpression, @NonNull Bundle searchSpecBundle, - @NonNull AndroidFuture<AppSearchResult> callback) { + @NonNull IAppSearchResultCallback callback) { Preconditions.checkNotNull(databaseName); Preconditions.checkNotNull(queryExpression); Preconditions.checkNotNull(searchSpecBundle); - Preconditions.checkNotNull(callback); int callingUid = Binder.getCallingUidOrThrow(); int callingUserId = UserHandle.getUserId(callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid); - impl.removeByQuery(databaseName, queryExpression, new SearchSpec(searchSpecBundle)); - callback.complete(AppSearchResult.newSuccessfulResult(/*result= */null)); + impl.removeByQuery(databaseName, queryExpression, + new SearchSpec(searchSpecBundle)); + invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null)); } catch (Throwable t) { - callback.complete(throwableToFailedResult(t)); + invokeCallbackOnError(callback, t); } finally { Binder.restoreCallingIdentity(callingIdentity); } diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java index 2c8a5589ca51..3597c27224bb 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java @@ -462,11 +462,11 @@ public class JobInfo implements Parcelable { public @NetworkType int getNetworkType() { if (networkRequest == null) { return NETWORK_TYPE_NONE; - } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) { + } else if (networkRequest.hasCapability(NET_CAPABILITY_NOT_METERED)) { return NETWORK_TYPE_UNMETERED; - } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING)) { + } else if (networkRequest.hasCapability(NET_CAPABILITY_NOT_ROAMING)) { return NETWORK_TYPE_NOT_ROAMING; - } else if (networkRequest.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) { + } else if (networkRequest.hasTransport(TRANSPORT_CELLULAR)) { return NETWORK_TYPE_CELLULAR; } else { return NETWORK_TYPE_ANY; @@ -1558,7 +1558,7 @@ public class JobInfo implements Parcelable { if (isPersisted) { // We can't serialize network specifiers if (networkRequest != null - && networkRequest.networkCapabilities.getNetworkSpecifier() != null) { + && networkRequest.getNetworkSpecifier() != null) { throw new IllegalArgumentException( "Network specifiers aren't supported for persistent jobs"); } diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index d4ea7af06ed1..20c77da53f2c 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -417,6 +417,9 @@ public class AlarmManagerService extends SystemService { @VisibleForTesting static final String KEY_LAZY_BATCHING = "lazy_batching"; + private static final String KEY_TIME_TICK_ALLOWED_WHILE_IDLE = + "time_tick_allowed_while_idle"; + private static final long DEFAULT_MIN_FUTURITY = 5 * 1000; private static final long DEFAULT_MIN_INTERVAL = 60 * 1000; private static final long DEFAULT_MAX_INTERVAL = 365 * DateUtils.DAY_IN_MILLIS; @@ -440,6 +443,7 @@ public class AlarmManagerService extends SystemService { private static final long DEFAULT_APP_STANDBY_RESTRICTED_WINDOW = MILLIS_IN_DAY; private static final boolean DEFAULT_LAZY_BATCHING = true; + private static final boolean DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE = true; // Minimum futurity of a new alarm public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY; @@ -470,6 +474,7 @@ public class AlarmManagerService extends SystemService { public long APP_STANDBY_RESTRICTED_WINDOW = DEFAULT_APP_STANDBY_RESTRICTED_WINDOW; public boolean LAZY_BATCHING = DEFAULT_LAZY_BATCHING; + public boolean TIME_TICK_ALLOWED_WHILE_IDLE = DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE; private long mLastAllowWhileIdleWhitelistDuration = -1; @@ -557,6 +562,11 @@ public class AlarmManagerService extends SystemService { migrateAlarmsToNewStoreLocked(); } break; + case KEY_TIME_TICK_ALLOWED_WHILE_IDLE: + TIME_TICK_ALLOWED_WHILE_IDLE = properties.getBoolean( + KEY_TIME_TICK_ALLOWED_WHILE_IDLE, + DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE); + break; default: if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) { // The quotas need to be updated in order, so we can't just rely @@ -690,6 +700,9 @@ public class AlarmManagerService extends SystemService { pw.print(KEY_LAZY_BATCHING, LAZY_BATCHING); pw.println(); + pw.print(KEY_TIME_TICK_ALLOWED_WHILE_IDLE, TIME_TICK_ALLOWED_WHILE_IDLE); + pw.println(); + pw.decreaseIndent(); } @@ -3756,9 +3769,14 @@ public class AlarmManagerService extends SystemService { final long tickEventDelay = nextTime - currentTime; final WorkSource workSource = null; // Let system take blame for time tick events. + + int flags = AlarmManager.FLAG_STANDALONE; + flags |= mConstants.TIME_TICK_ALLOWED_WHILE_IDLE ? FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED + : 0; + setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0, - 0, null, mTimeTickTrigger, TIME_TICK_TAG, AlarmManager.FLAG_STANDALONE, - workSource, null, Process.myUid(), "android"); + 0, null, mTimeTickTrigger, TIME_TICK_TAG, flags, workSource, null, + Process.myUid(), "android"); // Finally, remember when we set the tick alarm synchronized (mLock) { diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java index c2ee6dcd13b2..ef1e413a3112 100644 --- a/cmds/sm/src/com/android/commands/sm/Sm.java +++ b/cmds/sm/src/com/android/commands/sm/Sm.java @@ -101,8 +101,6 @@ public final class Sm { runFstrim(); } else if ("set-virtual-disk".equals(op)) { runSetVirtualDisk(); - } else if ("set-isolated-storage".equals(op)) { - runIsolatedStorage(); } else if ("start-checkpoint".equals(op)) { runStartCheckpoint(); } else if ("supports-checkpoint".equals(op)) { @@ -286,28 +284,6 @@ public final class Sm { StorageManager.DEBUG_VIRTUAL_DISK); } - public void runIsolatedStorage() throws RemoteException { - final int value; - final int mask = StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_ON - | StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_OFF; - switch (nextArg()) { - case "on": - case "true": - value = StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_ON; - break; - case "off": - value = StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_OFF; - break; - case "default": - case "false": - value = 0; - break; - default: - return; - } - mSm.setDebugFlags(value, mask); - } - public void runIdleMaint() throws RemoteException { final boolean im_run = "run".equals(nextArg()); if (im_run) { @@ -367,8 +343,6 @@ public final class Sm { System.err.println(""); System.err.println(" sm set-emulate-fbe [true|false]"); System.err.println(""); - System.err.println(" sm set-isolated-storage [on|off|default]"); - System.err.println(""); System.err.println(" sm start-checkpoint <num-retries>"); System.err.println(""); System.err.println(" sm supports-checkpoint"); diff --git a/cmds/statsd/src/OWNERS b/cmds/statsd/src/OWNERS deleted file mode 100644 index 0f3ddf7388d9..000000000000 --- a/cmds/statsd/src/OWNERS +++ /dev/null @@ -1,6 +0,0 @@ -# Temporary OWNERS Block to assist with migration -# bug: 167962588 -per-file *atoms.proto = set noparent -per-file *atom_field_options.proto = set noparent -per-file *atoms.proto = baligh@google.com, yro@google.com, singhtejinder@google.com, jeffreyhuang@google.com -per-file *atom_field_options.proto = baligh@google.com, yro@google.com, singhtejinder@google.com, jeffreyhuang@google.com diff --git a/core/api/current.txt b/core/api/current.txt index e302b25873b4..07f839571c92 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -6968,6 +6968,7 @@ package android.app.admin { method public boolean grantKeyPairToApp(@Nullable android.content.ComponentName, @NonNull String, @NonNull String); method public boolean hasCaCertInstalled(@Nullable android.content.ComponentName, byte[]); method public boolean hasGrantedPolicy(@NonNull android.content.ComponentName, int); + method public boolean hasKeyPair(@NonNull String); method public boolean hasLockdownAdminConfiguredNetworks(@NonNull android.content.ComponentName); method public boolean installCaCert(@Nullable android.content.ComponentName, byte[]); method public boolean installExistingPackage(@NonNull android.content.ComponentName, String); @@ -7343,6 +7344,7 @@ package android.app.admin { field public static final int TAG_MEDIA_UNMOUNT = 210014; // 0x3345e field public static final int TAG_OS_SHUTDOWN = 210010; // 0x3345a field public static final int TAG_OS_STARTUP = 210009; // 0x33459 + field public static final int TAG_PASSWORD_COMPLEXITY_REQUIRED = 210035; // 0x33473 field public static final int TAG_PASSWORD_COMPLEXITY_SET = 210017; // 0x33461 field public static final int TAG_PASSWORD_EXPIRATION_SET = 210016; // 0x33460 field public static final int TAG_PASSWORD_HISTORY_LENGTH_SET = 210018; // 0x33462 @@ -50849,6 +50851,33 @@ package android.view { method public void onActionViewExpanded(); } + public final class ContentInfo { + method @NonNull public android.content.ClipData getClip(); + method @Nullable public android.os.Bundle getExtras(); + method public int getFlags(); + method @Nullable public android.net.Uri getLinkUri(); + method public int getSource(); + method @NonNull public java.util.Map<java.lang.Boolean,android.view.ContentInfo> partition(@NonNull java.util.function.Predicate<android.content.ClipData.Item>); + field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1 + field public static final int SOURCE_APP = 0; // 0x0 + field public static final int SOURCE_AUTOFILL = 4; // 0x4 + field public static final int SOURCE_CLIPBOARD = 1; // 0x1 + field public static final int SOURCE_DRAG_AND_DROP = 3; // 0x3 + field public static final int SOURCE_INPUT_METHOD = 2; // 0x2 + field public static final int SOURCE_PROCESS_TEXT = 5; // 0x5 + } + + public static final class ContentInfo.Builder { + ctor public ContentInfo.Builder(@NonNull android.view.ContentInfo); + ctor public ContentInfo.Builder(@NonNull android.content.ClipData, int); + method @NonNull public android.view.ContentInfo build(); + method @NonNull public android.view.ContentInfo.Builder setClip(@NonNull android.content.ClipData); + method @NonNull public android.view.ContentInfo.Builder setExtras(@Nullable android.os.Bundle); + method @NonNull public android.view.ContentInfo.Builder setFlags(int); + method @NonNull public android.view.ContentInfo.Builder setLinkUri(@Nullable android.net.Uri); + method @NonNull public android.view.ContentInfo.Builder setSource(int); + } + public interface ContextMenu extends android.view.Menu { method public void clearHeader(); method public android.view.ContextMenu setHeaderIcon(@DrawableRes int); @@ -52064,34 +52093,7 @@ package android.view { } public interface OnReceiveContentListener { - method @Nullable public android.view.OnReceiveContentListener.Payload onReceiveContent(@NonNull android.view.View, @NonNull android.view.OnReceiveContentListener.Payload); - } - - public static final class OnReceiveContentListener.Payload { - method @NonNull public android.content.ClipData getClip(); - method @Nullable public android.os.Bundle getExtras(); - method public int getFlags(); - method @Nullable public android.net.Uri getLinkUri(); - method public int getSource(); - method @NonNull public java.util.Map<java.lang.Boolean,android.view.OnReceiveContentListener.Payload> partition(@NonNull java.util.function.Predicate<android.content.ClipData.Item>); - field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1 - field public static final int SOURCE_APP = 0; // 0x0 - field public static final int SOURCE_AUTOFILL = 4; // 0x4 - field public static final int SOURCE_CLIPBOARD = 1; // 0x1 - field public static final int SOURCE_DRAG_AND_DROP = 3; // 0x3 - field public static final int SOURCE_INPUT_METHOD = 2; // 0x2 - field public static final int SOURCE_PROCESS_TEXT = 5; // 0x5 - } - - public static final class OnReceiveContentListener.Payload.Builder { - ctor public OnReceiveContentListener.Payload.Builder(@NonNull android.view.OnReceiveContentListener.Payload); - ctor public OnReceiveContentListener.Payload.Builder(@NonNull android.content.ClipData, int); - method @NonNull public android.view.OnReceiveContentListener.Payload build(); - method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setClip(@NonNull android.content.ClipData); - method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setExtras(@Nullable android.os.Bundle); - method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setFlags(int); - method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setLinkUri(@Nullable android.net.Uri); - method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setSource(int); + method @Nullable public android.view.ContentInfo onReceiveContent(@NonNull android.view.View, @NonNull android.view.ContentInfo); } public abstract class OrientationEventListener { @@ -52842,7 +52844,7 @@ package android.view { method public void onProvideContentCaptureStructure(@NonNull android.view.ViewStructure, int); method public void onProvideStructure(android.view.ViewStructure); method public void onProvideVirtualStructure(android.view.ViewStructure); - method @Nullable public android.view.OnReceiveContentListener.Payload onReceiveContent(@NonNull android.view.OnReceiveContentListener.Payload); + method @Nullable public android.view.ContentInfo onReceiveContent(@NonNull android.view.ContentInfo); method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int); method @CallSuper protected void onRestoreInstanceState(android.os.Parcelable); method public void onRtlPropertiesChanged(int); @@ -52868,7 +52870,7 @@ package android.view { method public boolean performHapticFeedback(int, int); method public boolean performLongClick(); method public boolean performLongClick(float, float); - method @Nullable public android.view.OnReceiveContentListener.Payload performReceiveContent(@NonNull android.view.OnReceiveContentListener.Payload); + method @Nullable public android.view.ContentInfo performReceiveContent(@NonNull android.view.ContentInfo); method public void playSoundEffect(int); method public boolean post(Runnable); method public boolean postDelayed(Runnable, long); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 99179ec89cbc..8ba5a94179ea 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -8420,6 +8420,8 @@ package android.provider { field public static final String NAMESPACE_RUNTIME_NATIVE = "runtime_native"; field public static final String NAMESPACE_RUNTIME_NATIVE_BOOT = "runtime_native_boot"; field public static final String NAMESPACE_SCHEDULER = "scheduler"; + field public static final String NAMESPACE_STATSD_NATIVE = "statsd_native"; + field public static final String NAMESPACE_STATSD_NATIVE_BOOT = "statsd_native_boot"; field @Deprecated public static final String NAMESPACE_STORAGE = "storage"; field public static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot"; field public static final String NAMESPACE_SYSTEMUI = "systemui"; @@ -11432,6 +11434,61 @@ package android.telephony.ims { field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.AudioCodecAttributes> CREATOR; } + public interface DelegateMessageCallback { + method public void onMessageReceived(@NonNull android.telephony.ims.SipMessage); + method public void onMessageSendFailure(@NonNull String, int); + method public void onMessageSent(@NonNull String); + } + + public final class DelegateRegistrationState implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.Set<android.telephony.ims.FeatureTagState> getDeregisteredFeatureTags(); + method @NonNull public java.util.Set<android.telephony.ims.FeatureTagState> getDeregisteringFeatureTags(); + method @NonNull public java.util.Set<java.lang.String> getRegisteredFeatureTags(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.DelegateRegistrationState> CREATOR; + field public static final int DEREGISTERED_REASON_NOT_PROVISIONED = 1; // 0x1 + field public static final int DEREGISTERED_REASON_NOT_REGISTERED = 2; // 0x2 + field public static final int DEREGISTERED_REASON_UNKNOWN = 0; // 0x0 + field public static final int DEREGISTERING_REASON_DESTROY_PENDING = 6; // 0x6 + field public static final int DEREGISTERING_REASON_FEATURE_TAGS_CHANGING = 5; // 0x5 + field public static final int DEREGISTERING_REASON_PDN_CHANGE = 3; // 0x3 + field public static final int DEREGISTERING_REASON_PROVISIONING_CHANGE = 4; // 0x4 + } + + public static final class DelegateRegistrationState.Builder { + ctor public DelegateRegistrationState.Builder(); + method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addDeregisteredFeatureTag(@NonNull String, int); + method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addDeregisteringFeatureTag(@NonNull String, int); + method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addRegisteredFeatureTag(@NonNull String); + method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addRegisteredFeatureTags(@NonNull java.util.Set<java.lang.String>); + method @NonNull public android.telephony.ims.DelegateRegistrationState build(); + } + + public final class DelegateRequest implements android.os.Parcelable { + ctor public DelegateRequest(@NonNull java.util.Set<java.lang.String>); + method public int describeContents(); + method @NonNull public java.util.Set<java.lang.String> getFeatureTags(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.DelegateRequest> CREATOR; + } + + public interface DelegateStateCallback { + method public void onCreated(@NonNull android.telephony.ims.stub.SipDelegate, @Nullable java.util.Set<android.telephony.ims.FeatureTagState>); + method public void onDestroyed(int); + method public void onFeatureTagRegistrationChanged(@NonNull android.telephony.ims.DelegateRegistrationState); + method public void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration); + } + + public final class FeatureTagState implements android.os.Parcelable { + ctor public FeatureTagState(@NonNull String, int); + method public int describeContents(); + method @NonNull public String getFeatureTag(); + method public int getState(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.FeatureTagState> CREATOR; + } + public final class ImsCallForwardInfo implements android.os.Parcelable { ctor public ImsCallForwardInfo(int, int, int, int, @NonNull String, int); method public int describeContents(); @@ -11952,8 +12009,107 @@ package android.telephony.ims { field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RtpHeaderExtensionType> CREATOR; } + public interface SipDelegateConnection { + method public void notifyMessageReceiveError(@NonNull String, int); + method public void notifyMessageReceived(@NonNull String); + method public void sendMessage(@NonNull android.telephony.ims.SipMessage, long); + } + + public final class SipDelegateImsConfiguration implements android.os.Parcelable { + method public boolean containsKey(@NonNull String); + method @NonNull public android.os.PersistableBundle copyBundle(); + method public int describeContents(); + method public boolean getBoolean(@NonNull String, boolean); + method public int getInt(@NonNull String, int); + method @Nullable public String getString(@NonNull String); + method public long getVersion(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR; + field public static final String IPTYPE_IPV4 = "IPV4"; + field public static final String IPTYPE_IPV6 = "IPV6"; + field public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string"; + field public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string"; + field public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string"; + field public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string"; + field public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string"; + field public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool"; + field public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool"; + field public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool"; + field public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool"; + field public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool"; + field public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int"; + field public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string"; + field public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string"; + field public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string"; + field public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string"; + field public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string"; + field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string"; + field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int"; + field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int"; + field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int"; + field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int"; + field public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string"; + field public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string"; + field public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string"; + field public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int"; + field public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int"; + field public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int"; + field public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int"; + field public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string"; + field public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string"; + field public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string"; + field public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int"; + field public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string"; + field public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string"; + field public static final String SIP_TRANSPORT_TCP = "TCP"; + field public static final String SIP_TRANSPORT_UDP = "UDP"; + } + + public static final class SipDelegateImsConfiguration.Builder { + ctor public SipDelegateImsConfiguration.Builder(int); + ctor public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration); + method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean); + method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int); + method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String); + method @NonNull public android.telephony.ims.SipDelegateImsConfiguration build(); + } + public class SipDelegateManager { + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void createSipDelegate(@NonNull android.telephony.ims.DelegateRequest, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.DelegateConnectionStateCallback, @NonNull android.telephony.ims.stub.DelegateConnectionMessageCallback) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void destroySipDelegate(@NonNull android.telephony.ims.SipDelegateConnection, int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSupported() throws android.telephony.ims.ImsException; + field public static final int DENIED_REASON_INVALID = 4; // 0x4 + field public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1; // 0x1 + field public static final int DENIED_REASON_NOT_ALLOWED = 2; // 0x2 + field public static final int DENIED_REASON_SINGLE_REGISTRATION_NOT_ALLOWED = 3; // 0x3 + field public static final int DENIED_REASON_UNKNOWN = 0; // 0x0 + field public static final int MESSAGE_FAILURE_REASON_DELEGATE_CLOSED = 2; // 0x2 + field public static final int MESSAGE_FAILURE_REASON_DELEGATE_DEAD = 1; // 0x1 + field public static final int MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION = 11; // 0xb + field public static final int MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT = 5; // 0x5 + field public static final int MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG = 6; // 0x6 + field public static final int MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS = 4; // 0x4 + field public static final int MESSAGE_FAILURE_REASON_INVALID_START_LINE = 3; // 0x3 + field public static final int MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE = 8; // 0x8 + field public static final int MESSAGE_FAILURE_REASON_NOT_REGISTERED = 9; // 0x9 + field public static final int MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION = 10; // 0xa + field public static final int MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE = 7; // 0x7 + field public static final int MESSAGE_FAILURE_REASON_UNKNOWN = 0; // 0x0 + field public static final int SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP = 2; // 0x2 + field public static final int SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD = 1; // 0x1 + field public static final int SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN = 4; // 0x4 + field public static final int SIP_DELEGATE_DESTROY_REASON_UNKNOWN = 0; // 0x0 + field public static final int SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS = 3; // 0x3 + } + + public final class SipMessage implements android.os.Parcelable { + ctor public SipMessage(@NonNull String, @NonNull String, @NonNull byte[]); + method public int describeContents(); + method @NonNull public byte[] getContent(); + method @NonNull public String getHeaderSection(); + method @NonNull public String getStartLine(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipMessage> CREATOR; } } @@ -12047,6 +12203,19 @@ package android.telephony.ims.feature { package android.telephony.ims.stub { + public interface DelegateConnectionMessageCallback { + method public void onMessageReceived(@NonNull android.telephony.ims.SipMessage); + method public void onMessageSendFailure(@NonNull String, int); + method public void onMessageSent(@NonNull String); + } + + public interface DelegateConnectionStateCallback { + method public void onCreated(@NonNull android.telephony.ims.SipDelegateConnection); + method public void onDestroyed(int); + method public void onFeatureTagStatusChanged(@NonNull android.telephony.ims.DelegateRegistrationState, @NonNull java.util.Set<android.telephony.ims.FeatureTagState>); + method public void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration); + } + public class ImsCallSessionImplBase implements java.lang.AutoCloseable { ctor public ImsCallSessionImplBase(); method public void accept(int, android.telephony.ims.ImsStreamMediaProfile); @@ -12207,8 +12376,17 @@ package android.telephony.ims.stub { method public int updateColr(int); } + public interface SipDelegate { + method public void closeDialog(@NonNull String); + method public void notifyMessageReceiveError(@NonNull String, int); + method public void notifyMessageReceived(@NonNull String); + method public void sendMessage(@NonNull android.telephony.ims.SipMessage, long); + } + public class SipTransportImplBase { ctor public SipTransportImplBase(@NonNull java.util.concurrent.Executor); + method public void createSipDelegate(int, @NonNull android.telephony.ims.DelegateRequest, @NonNull android.telephony.ims.DelegateStateCallback, @NonNull android.telephony.ims.DelegateMessageCallback); + method public void destroySipDelegate(@NonNull android.telephony.ims.stub.SipDelegate, int); } } diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 5434bcca0575..e392ed7b6e2d 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -7,6 +7,7 @@ package android { field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS"; field public static final String BIND_CELL_BROADCAST_SERVICE = "android.permission.BIND_CELL_BROADCAST_SERVICE"; field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE"; + field public static final String BROADCAST_CLOSE_SYSTEM_DIALOGS = "android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"; field public static final String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE"; field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA"; field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"; @@ -1499,7 +1500,6 @@ package android.provider { field public static final String HIDDEN_API_BLACKLIST_EXEMPTIONS = "hidden_api_blacklist_exemptions"; field public static final String HIDDEN_API_POLICY = "hidden_api_policy"; field public static final String HIDE_ERROR_DIALOGS = "hide_error_dialogs"; - field public static final String LOCATION_GLOBAL_KILL_SWITCH = "location_global_kill_switch"; field public static final String LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST = "location_ignore_settings_package_whitelist"; field public static final String LOW_POWER_MODE = "low_power"; field public static final String LOW_POWER_MODE_STICKY = "low_power_sticky"; diff --git a/core/java/android/annotation/RequiresFeature.java b/core/java/android/annotation/RequiresFeature.java index fc93f03d76cf..08861d42be39 100644 --- a/core/java/android/annotation/RequiresFeature.java +++ b/core/java/android/annotation/RequiresFeature.java @@ -30,7 +30,6 @@ import java.lang.annotation.Target; * Denotes that the annotated element requires one or more device features. This * is used to auto-generate documentation. * - * @see PackageManager#hasSystemFeature(String) * @hide */ @Retention(SOURCE) @@ -38,8 +37,16 @@ import java.lang.annotation.Target; public @interface RequiresFeature { /** * The name of the device feature that is required. - * - * @see PackageManager#hasSystemFeature(String) */ String value(); + + /** + * Defines the name of the method that should be called to check whether the feature is + * available, using the same signature format as javadoc. The feature checking method can have + * multiple parameters, but the feature name parameter must be of type String and must also be + * the first String-type parameter. + * <p> + * By default, the enforcement is {@link PackageManager#hasSystemFeature(String)}. + */ + String enforcement() default("android.content.pm.PackageManager#hasSystemFeature"); } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index fc95718cf0e4..1c2f682c9492 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -5189,8 +5189,8 @@ public class Activity extends ContextThemeWrapper * result callbacks including {@link #onRequestPermissionsResult(int, String[], int[])}. * </p> * <p> - * The <a href="https://github.com/googlesamples/android-RuntimePermissions"> - * RuntimePermissions</a> sample app demonstrates how to use this method to + * The <a href="https://github.com/android/permissions-samples"> + * RuntimePermissions</a> sample apps demonstrate how to use this method to * request permissions at run time. * </p> * diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 5537edb8b957..e3048dff1bda 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -1854,16 +1854,12 @@ public class ActivityManager { * the recent tasks. */ @Deprecated - public List<RecentTaskInfo> getRecentTasks(int maxNum, int flags) - throws SecurityException { - try { - if (maxNum < 0) { - throw new IllegalArgumentException("The requested number of tasks should be >= 0"); - } - return getTaskService().getRecentTasks(maxNum, flags, mContext.getUserId()).getList(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + public List<RecentTaskInfo> getRecentTasks(int maxNum, int flags) throws SecurityException { + if (maxNum < 0) { + throw new IllegalArgumentException("The requested number of tasks should be >= 0"); } + return ActivityTaskManager.getInstance().getRecentTasks( + maxNum, flags, mContext.getUserId()); } /** @@ -2084,11 +2080,7 @@ public class ActivityManager { @Deprecated public List<RunningTaskInfo> getRunningTasks(int maxNum) throws SecurityException { - try { - return getTaskService().getTasks(maxNum); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return ActivityTaskManager.getInstance().getTasks(maxNum); } /** diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java index c7b90897c8e7..03c1a011f198 100644 --- a/core/java/android/app/ActivityTaskManager.java +++ b/core/java/android/app/ActivityTaskManager.java @@ -27,7 +27,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; import android.os.Build; -import android.os.Handler; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; @@ -35,6 +34,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.util.DisplayMetrics; import android.util.Singleton; +import android.view.RemoteAnimationDefinition; import java.util.List; @@ -147,7 +147,20 @@ public class ActivityTaskManager { private static int sMaxRecentTasks = -1; - ActivityTaskManager(Context context, Handler handler) { + private static final Singleton<ActivityTaskManager> sInstance = + new Singleton<ActivityTaskManager>() { + @Override + protected ActivityTaskManager create() { + return new ActivityTaskManager(); + } + }; + + private ActivityTaskManager() { + } + + /** @hide */ + public static ActivityTaskManager getInstance() { + return sInstance.get(); } /** @hide */ @@ -444,6 +457,98 @@ public class ActivityTaskManager { } /** + * @return List of running tasks. + * @hide + */ + public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) { + return getTasks(maxNum, false /* filterForVisibleRecents */); + } + + /** + * @return List of running tasks that can be filtered by visibility in recents. + * @hide + */ + public List<ActivityManager.RunningTaskInfo> getTasks( + int maxNum, boolean filterOnlyVisibleRecents) { + try { + return getService().getTasks(maxNum, filterOnlyVisibleRecents); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @return List of recent tasks. + * @hide + */ + public List<ActivityManager.RecentTaskInfo> getRecentTasks( + int maxNum, int flags, int userId) { + try { + return getService().getRecentTasks(maxNum, flags, userId).getList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + public void registerTaskStackListener(TaskStackListener listener) { + try { + getService().registerTaskStackListener(listener); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + public void unregisterTaskStackListener(TaskStackListener listener) { + try { + getService().unregisterTaskStackListener(listener); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + public Rect getTaskBounds(int taskId) { + try { + return getService().getTaskBounds(taskId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Registers remote animations for a display. + * @hide + */ + public void registerRemoteAnimationsForDisplay( + int displayId, RemoteAnimationDefinition definition) { + try { + getService().registerRemoteAnimationsForDisplay(displayId, definition); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + public boolean isInLockTaskMode() { + try { + return getService().isInLockTaskMode(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + public boolean removeTask(int taskId) { + try { + return getService().removeTask(taskId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Information you can retrieve about a root task in the system. * @hide */ diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index bd5913efdecb..ab48baea48e8 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -83,6 +83,15 @@ import java.util.List; * * {@hide} */ +// TODO(b/174040395): Make this interface private to ActivityTaskManager.java and have external +// caller go through that call instead. This would help us better separate and control the API +// surface exposed. +// TODO(b/174041144): Move callback methods from Activity (Things that take param 'IBinder token') +// to a separate interface that is only available to the Activity. +// TODO(b/174041603): Create a builder interface for things like startActivityXXX(...) to reduce +// interface duplication. +// TODO(b/174040691): Clean-up/remove all obsolete or unused interfaces like things that should be +// going through task organizer now. interface IActivityTaskManager { int startActivity(in IApplicationThread caller, in String callingPackage, in String callingFeatureId, in Intent intent, in String resolvedType, @@ -154,9 +163,7 @@ interface IActivityTaskManager { void setFocusedTask(int taskId); boolean removeTask(int taskId); void removeAllVisibleRecentTasks(); - List<ActivityManager.RunningTaskInfo> getTasks(int maxNum); - List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum, - boolean filterOnlyVisibleRecents); + List<ActivityManager.RunningTaskInfo> getTasks(int maxNum, boolean filterOnlyVisibleRecents); boolean shouldUpRecreateTask(in IBinder token, in String destAffinity); boolean navigateUpTo(in IBinder token, in Intent target, int resultCode, in Intent resultData); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index a886beddf64c..d5977e7711fd 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -30,6 +30,7 @@ import android.annotation.IdRes; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.Px; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -4873,6 +4874,7 @@ public class Notification implements Parcelable // Small icon doesn't need to be reset, as it's always set. Resetting would prevent // re-using the drawable when the notification is updated. contentView.setBoolean(R.id.expand_button, "setExpanded", false); + contentView.setViewVisibility(R.id.app_name_text, View.GONE); contentView.setTextViewText(R.id.app_name_text, null); contentView.setViewVisibility(R.id.chronometer, View.GONE); contentView.setViewVisibility(R.id.header_text, View.GONE); @@ -5105,33 +5107,29 @@ public class Notification implements Parcelable if (result == null) { result = new TemplateBindResult(); } - boolean largeIconShown = bindLargeIcon(contentView, p); + final boolean largeIconShown = bindLargeIcon(contentView, p); calculateLargeIconMarginEnd(largeIconShown, result); if (p.mHeaderless) { // views in the headerless (collapsed) state - contentView.setViewLayoutMarginEnd(R.id.notification_standard_view_column, - result.getHeadingExtraMarginEnd()); + result.mHeadingExtraMarginSet.applyToView(contentView, + R.id.notification_headerless_view_column); } else { // views in states with a header (big states) - contentView.setInt(R.id.notification_header, "setTopLineExtraMarginEnd", - result.getHeadingExtraMarginEnd()); - contentView.setViewLayoutMarginEnd(R.id.line1, result.getTitleMarginEnd()); + result.mHeadingExtraMarginSet.applyToView(contentView, R.id.notification_header); + result.mTitleMarginSet.applyToView(contentView, R.id.line1); } } private void calculateLargeIconMarginEnd(boolean largeIconShown, @NonNull TemplateBindResult result) { - int contentMargin = mContext.getResources().getDimensionPixelSize( + final Resources resources = mContext.getResources(); + final int contentMargin = resources.getDimensionPixelOffset( R.dimen.notification_content_margin_end); - int expanderSize = mContext.getResources().getDimensionPixelSize( + final int expanderSize = resources.getDimensionPixelSize( R.dimen.notification_header_expand_icon_size) - contentMargin; - int extraMarginEnd = 0; - if (largeIconShown) { - int iconSize = mContext.getResources().getDimensionPixelSize( - R.dimen.notification_right_icon_size); - extraMarginEnd = iconSize + contentMargin; - } - result.setRightIconState(largeIconShown, extraMarginEnd, expanderSize); + final int extraMarginEndIfVisible = resources.getDimensionPixelSize( + R.dimen.notification_right_icon_size) + contentMargin; + result.setRightIconState(largeIconShown, extraMarginEndIfVisible, expanderSize); } /** @@ -5153,9 +5151,14 @@ public class Notification implements Parcelable private void bindNotificationHeader(RemoteViews contentView, StandardTemplateParams p) { bindSmallIcon(contentView, p); - boolean hasTextToLeft = bindHeaderAppName(contentView, p); + // Populate text left-to-right so that separators are only shown between strings + boolean hasTextToLeft = bindHeaderAppName(contentView, p, false /* force */); hasTextToLeft |= bindHeaderTextSecondary(contentView, p, hasTextToLeft); hasTextToLeft |= bindHeaderText(contentView, p, hasTextToLeft); + if (!hasTextToLeft) { + // If there's still no text, force add the app name so there is some text. + hasTextToLeft |= bindHeaderAppName(contentView, p, true /* force */); + } bindHeaderChronometerAndTime(contentView, p, hasTextToLeft); bindProfileBadge(contentView, p); bindAlertedIcon(contentView, p); @@ -5219,7 +5222,7 @@ public class Notification implements Parcelable && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) { summaryText = mN.extras.getCharSequence(EXTRA_INFO_TEXT); } - if (summaryText != null) { + if (!TextUtils.isEmpty(summaryText)) { // TODO: Remove the span entirely to only have the string with propper formating. contentView.setTextViewText(R.id.header_text, processTextSpans( processLegacyText(summaryText))); @@ -5291,13 +5294,13 @@ public class Notification implements Parcelable /** * @return true if the app name will be visible */ - private boolean bindHeaderAppName(RemoteViews contentView, StandardTemplateParams p) { - if (p.mViewType == StandardTemplateParams.VIEW_TYPE_MINIMIZED) { - contentView.setViewVisibility(R.id.app_name_text, View.GONE); + private boolean bindHeaderAppName(RemoteViews contentView, StandardTemplateParams p, + boolean force) { + if (p.mViewType == StandardTemplateParams.VIEW_TYPE_MINIMIZED && !force) { + // unless the force flag is set, don't show the app name in the minimized state. return false; } if (p.mHeaderless && p.hasTitle()) { - contentView.setViewVisibility(R.id.app_name_text, View.GONE); // the headerless template will have the TITLE in this position; return true to // keep the divider visible between that title and the next text element. return true; @@ -7759,8 +7762,10 @@ public class Notification implements Parcelable addExtras(mBuilder.mN.extras); if (!isConversationLayout) { // also update the end margin if there is an image + // NOTE: This template doesn't support moving this icon to the left, so we don't + // need to fully apply the MarginSet contentView.setViewLayoutMarginEnd(R.id.notification_messaging, - bindResult.getHeadingExtraMarginEnd()); + bindResult.mHeadingExtraMarginSet.getValue()); } contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor", mBuilder.isColorized(p) @@ -8757,9 +8762,8 @@ public class Notification implements Parcelable if (!headerless) { // also update the end margin to account for the large icon or expander Resources resources = mBuilder.mContext.getResources(); - int endMargin = resources.getDimensionPixelSize( - R.dimen.notification_content_margin_end) + result.getTitleMarginEnd(); - remoteViews.setViewLayoutMarginEnd(R.id.notification_main_column, endMargin); + result.mTitleMarginSet.applyToView(remoteViews, R.id.notification_main_column, + resources.getDimensionPixelOffset(R.dimen.notification_content_margin_end)); } } @@ -10997,42 +11001,74 @@ public class Notification implements Parcelable */ private static class TemplateBindResult { boolean mRightIconVisible; - int mRightIconMarginEnd; - int mExpanderSize; /** - * @return the margin end that needs to be added to the heading so that it won't overlap + * The margin end that needs to be added to the heading so that it won't overlap * with the large icon. This value includes the space required to accommodate the large * icon, but should be added to the space needed to accommodate the expander. This does * not include the 16dp content margin that all notification views must have. */ - public int getHeadingExtraMarginEnd() { - return mRightIconMarginEnd; - } + public final MarginSet mHeadingExtraMarginSet = new MarginSet(); /** - * @return the margin end that needs to be added to the heading so that it won't overlap + * The margin end that needs to be added to the heading so that it won't overlap * with the large icon. This value includes the space required to accommodate the large * icon as well as the expander. This does not include the 16dp content margin that all * notification views must have. */ - public int getHeadingFullMarginEnd() { - return mRightIconMarginEnd + mExpanderSize; - } + public final MarginSet mHeadingFullMarginSet = new MarginSet(); /** - * @return the margin end that needs to be added to the title text of the big state + * The margin end that needs to be added to the title text of the big state * so that it won't overlap with the large icon, but assuming the text can run under * the expander when that icon is not visible. */ - public int getTitleMarginEnd() { - return mRightIconVisible ? getHeadingFullMarginEnd() : 0; - } + public final MarginSet mTitleMarginSet = new MarginSet(); - public void setRightIconState(boolean visible, int marginEnd, int expanderSize) { + public void setRightIconState(boolean visible, int marginEndIfVisible, int expanderSize) { mRightIconVisible = visible; - mRightIconMarginEnd = marginEnd; - mExpanderSize = expanderSize; + mHeadingExtraMarginSet.setValues(0, marginEndIfVisible); + mHeadingFullMarginSet.setValues(expanderSize, marginEndIfVisible + expanderSize); + mTitleMarginSet.setValues(0, marginEndIfVisible + expanderSize); + } + + /** + * This contains the end margins for a view when the right icon is visible or not. These + * values are both needed so that NotificationGroupingUtil can 'move' the right_icon to the + * left_icon and adjust the margins, and to undo that change as well. + */ + private class MarginSet { + private int mValueIfGone; + private int mValueIfVisible; + + public void setValues(int valueIfGone, int valueIfVisible) { + mValueIfGone = valueIfGone; + mValueIfVisible = valueIfVisible; + } + + public void applyToView(@NonNull RemoteViews views, @IdRes int viewId) { + applyToView(views, viewId, 0); + } + + public void applyToView(@NonNull RemoteViews views, @IdRes int viewId, + @Px int extraMargin) { + final int marginEnd = getValue() + extraMargin; + if (viewId == R.id.notification_header) { + views.setInt(R.id.notification_header, "setTopLineExtraMarginEnd", marginEnd); + } else { + views.setViewLayoutMarginEnd(viewId, marginEnd); + } + if (mRightIconVisible) { + views.setIntTag(viewId, R.id.tag_margin_end_when_icon_visible, + mValueIfVisible + extraMargin); + views.setIntTag(viewId, R.id.tag_margin_end_when_icon_gone, + mValueIfGone + extraMargin); + } + } + + public int getValue() { + return mRightIconVisible ? mValueIfVisible : mValueIfGone; + } } } @@ -11074,7 +11110,9 @@ public class Notification implements Parcelable } final boolean hasTitle() { - return title != null && title.length() != 0 && !mHasCustomContent; + // We hide the title when the notification is a decorated custom view so that decorated + // custom views always have to include their own title. + return !TextUtils.isEmpty(title) && !mHasCustomContent; } final StandardTemplateParams viewType(int viewType) { diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 73777909d417..7287acdd0241 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -284,8 +284,7 @@ public final class SystemServiceRegistry { new CachedServiceFetcher<ActivityTaskManager>() { @Override public ActivityTaskManager createService(ContextImpl ctx) { - return new ActivityTaskManager( - ctx.getOuterContext(), ctx.mMainThread.getHandler()); + return ActivityTaskManager.getInstance(); }}); registerService(Context.URI_GRANTS_SERVICE, UriGrantsManager.class, diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING index d2be8a4a6597..36241a8ebe08 100644 --- a/core/java/android/app/TEST_MAPPING +++ b/core/java/android/app/TEST_MAPPING @@ -92,6 +92,54 @@ } ], "file_patterns": ["(/|^)Activity.java"] + }, + { + "name": "CtsContentTestCases", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "include-filter": "android.content.wm.cts" + } + ], + "file_patterns": ["(/|^)ContextImpl.java"] + }, + { + "name": "CtsOsTestCases", + "options": [ + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "include-filter": "android.os.cts.StrictModeTest" + } + ], + "file_patterns": ["(/|^)ContextImpl.java"] + }, + { + "name": "FrameworksCoreTests", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "include-filter": "android.content.ContextTest" + } + ], + "file_patterns": ["(/|^)ContextImpl.java"] } ], "postsubmit": [ diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index ca67dba45dd0..1a8a4b7f16da 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -214,7 +214,6 @@ public class TaskInfo { */ public int parentTaskId; - /** * Parent bounds. * @hide @@ -227,6 +226,12 @@ public class TaskInfo { */ public boolean isFocused; + /** + * Whether this task is visible. + * @hide + */ + public boolean isVisible; + TaskInfo() { // Do nothing } @@ -311,7 +316,8 @@ public class TaskInfo { && pictureInPictureParams == that.pictureInPictureParams && getWindowingMode() == that.getWindowingMode() && Objects.equals(taskDescription, that.taskDescription) - && isFocused == that.isFocused; + && isFocused == that.isFocused + && isVisible == that.isVisible; } private boolean equalsLetterboxParams(TaskInfo that) { @@ -358,6 +364,7 @@ public class TaskInfo { parentTaskId = source.readInt(); parentBounds = source.readTypedObject(Rect.CREATOR); isFocused = source.readBoolean(); + isVisible = source.readBoolean(); } /** @@ -394,6 +401,7 @@ public class TaskInfo { dest.writeInt(parentTaskId); dest.writeTypedObject(parentBounds, flags); dest.writeBoolean(isFocused); + dest.writeBoolean(isVisible); } @Override @@ -413,12 +421,13 @@ public class TaskInfo { + " topActivityType=" + topActivityType + " pictureInPictureParams=" + pictureInPictureParams + " topActivityInfo=" + topActivityInfo - + " launchCookies" + launchCookies + + " launchCookies=" + launchCookies + " letterboxActivityBounds=" + letterboxActivityBounds + " positionInParent=" + positionInParent + " parentTaskId=" + parentTaskId + " parentBounds=" + parentBounds + " isFocused=" + isFocused + + " isVisible=" + isVisible + "}"; } } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 42427fa825eb..26784f2c247e 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -3721,6 +3721,27 @@ public class DevicePolicyManager { } /** + * Returns the password complexity that applies to this user, aggregated from other users if + * necessary (for example, if the DPC has set password complexity requirements on the parent + * profile DPM instance of a managed profile user, they would apply to the primary user on the + * device). + * @hide + */ + @PasswordComplexity + public int getAggregatedPasswordComplexityForUser(int userId) { + if (mService == null) { + return PASSWORD_COMPLEXITY_NONE; + } + + try { + return mService.getAggregatedPasswordComplexityForUser(userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + + /** * When called by a profile owner of a managed profile returns true if the profile uses unified * challenge with its parent user. * @@ -5349,6 +5370,27 @@ public class DevicePolicyManager { } } + // STOPSHIP(b/174298501): clarify the expected return value following generateKeyPair call. + /** + * Called by a device or profile owner, or delegated certificate installer, to query whether a + * certificate and private key are installed under a given alias. + * + * @param alias The alias under which the key pair is installed. + * @return {@code true} if a key pair with this alias exists, {@code false} otherwise. + * @throws SecurityException if the caller is not a device or profile owner or a delegated + * certificate installer. + * @see #setDelegatedScopes + * @see #DELEGATION_CERT_INSTALL + */ + public boolean hasKeyPair(@NonNull String alias) { + throwIfParentInstance("hasKeyPair"); + try { + return mService.hasKeyPair(mContext.getPackageName(), alias); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Called by a device or profile owner, or delegated certificate installer, to generate a * new private/public key pair. If the device supports key generation via secure hardware, diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 58368bc3779a..8be3cdc1296a 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -89,6 +89,7 @@ interface IDevicePolicyManager { int getPasswordComplexity(boolean parent); void setRequiredPasswordComplexity(int passwordComplexity, boolean parent); int getRequiredPasswordComplexity(boolean parent); + int getAggregatedPasswordComplexityForUser(int userId); boolean isUsingUnifiedPassword(in ComponentName admin); int getCurrentFailedPasswordAttempts(int userHandle, boolean parent); int getProfileWithMinimumFailedPasswordsForWipe(int userHandle, boolean parent); @@ -184,6 +185,7 @@ interface IDevicePolicyManager { in byte[] certBuffer, in byte[] certChainBuffer, String alias, boolean requestAccess, boolean isUserSelectable); boolean removeKeyPair(in ComponentName who, in String callerPackage, String alias); + boolean hasKeyPair(in String callerPackage, in String alias); boolean generateKeyPair(in ComponentName who, in String callerPackage, in String algorithm, in ParcelableKeyGenParameterSpec keySpec, in int idAttestationFlags, out KeymasterCertificateChain attestationChain); diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java index 86f91d79ad2b..1cf45670ed93 100644 --- a/core/java/android/app/admin/SecurityLog.java +++ b/core/java/android/app/admin/SecurityLog.java @@ -85,7 +85,8 @@ public class SecurityLog { TAG_CRYPTO_SELF_TEST_COMPLETED, TAG_KEY_INTEGRITY_VIOLATION, TAG_CERT_VALIDATION_FAILURE, - TAG_CAMERA_POLICY_SET + TAG_CAMERA_POLICY_SET, + TAG_PASSWORD_COMPLEXITY_REQUIRED }) public @interface SecurityLogTag {} @@ -478,6 +479,21 @@ public class SecurityLog { SecurityLogTags.SECURITY_CAMERA_POLICY_SET; /** + * Indicates that an admin has set a password complexity requirement, using the platform's + * pre-defined complexity levels. The log entry contains the following information about the + * event, encapsulated in an {@link Object} array and accessible via + * {@link SecurityEvent#getData()}: + * <li> [0] admin package name ({@code String}) + * <li> [1] admin user ID ({@code Integer}) + * <li> [2] target user ID ({@code Integer}) + * <li> [3] Password complexity ({@code Integer}) + * + * @see DevicePolicyManager#setRequiredPasswordComplexity(int) + */ + public static final int TAG_PASSWORD_COMPLEXITY_REQUIRED = + SecurityLogTags.SECURITY_PASSWORD_COMPLEXITY_REQUIRED; + + /** * Event severity level indicating that the event corresponds to normal workflow. */ public static final int LEVEL_INFO = 1; @@ -617,6 +633,7 @@ public class SecurityLog { case TAG_USER_RESTRICTION_ADDED: case TAG_USER_RESTRICTION_REMOVED: case TAG_CAMERA_POLICY_SET: + case TAG_PASSWORD_COMPLEXITY_REQUIRED: return LEVEL_INFO; case TAG_CERT_AUTHORITY_REMOVED: case TAG_CRYPTO_SELF_TEST_COMPLETED: diff --git a/core/java/android/app/admin/SecurityLogTags.logtags b/core/java/android/app/admin/SecurityLogTags.logtags index 100fd4cbd40f..db5245c919ab 100644 --- a/core/java/android/app/admin/SecurityLogTags.logtags +++ b/core/java/android/app/admin/SecurityLogTags.logtags @@ -1,4 +1,4 @@ -# See system/core/logcat/event.logtags for a description of the format of this file. +# See system/logging/logcat/event.logtags for a description of the format of this file. option java_package android.app.admin @@ -39,3 +39,4 @@ option java_package android.app.admin 210032 security_key_integrity_violation (key_id|3),(uid|1) 210033 security_cert_validation_failure (reason|3) 210034 security_camera_policy_set (package|3),(admin_user|1),(target_user|1),(disabled|1) +210035 security_password_complexity_required (package|3),(admin_user|1),(target_user|1),(complexity|1) diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index e35fb037582a..d7dc86a5c59f 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -63,7 +63,6 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; -import android.os.storage.StorageManager; import android.system.Int32Ref; import android.text.TextUtils; import android.util.EventLog; @@ -110,7 +109,7 @@ public abstract class ContentResolver implements ContentInterface { * * @hide */ - public static final boolean DEPRECATE_DATA_COLUMNS = StorageManager.hasIsolatedStorage(); + public static final boolean DEPRECATE_DATA_COLUMNS = true; /** * Special filesystem path prefix which indicates that a path should be diff --git a/core/java/android/content/TEST_MAPPING b/core/java/android/content/TEST_MAPPING new file mode 100644 index 000000000000..a2880dfdfd17 --- /dev/null +++ b/core/java/android/content/TEST_MAPPING @@ -0,0 +1,52 @@ +{ + "presubmit": [ + { + "name": "CtsContentTestCases", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "include-filter": "android.content.wm.cts" + } + ], + "file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java"] + }, + { + "name": "CtsOsTestCases", + "options": [ + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "include-filter": "android.os.cts.StrictModeTest" + } + ], + "file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java"] + }, + { + "name": "FrameworksCoreTests", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "include-filter": "android.content.ContextTest" + } + ], + "file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java"] + } + ] +}
\ No newline at end of file diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 32900225d887..ca5eeb1863c7 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -911,38 +911,77 @@ public final class DisplayManager { public interface DeviceConfig { /** - * Key for refresh rate in the zone defined by thresholds. + * Key for refresh rate in the low zone defined by thresholds. * + * Note that the name and value don't match because they were added before we had a high + * zone to consider. * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER * @see android.R.integer#config_defaultZoneBehavior */ - String KEY_REFRESH_RATE_IN_ZONE = "refresh_rate_in_zone"; + String KEY_REFRESH_RATE_IN_LOW_ZONE = "refresh_rate_in_zone"; /** - * Key for accessing the display brightness thresholds for the configured refresh rate zone. + * Key for accessing the low display brightness thresholds for the configured refresh + * rate zone. * The value will be a pair of comma separated integers representing the minimum and maximum * thresholds of the zone, respectively, in display backlight units (i.e. [0, 255]). * + * Note that the name and value don't match because they were added before we had a high + * zone to consider. + * * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER * @see android.R.array#config_brightnessThresholdsOfPeakRefreshRate * @hide */ - String KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS = + String KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS = "peak_refresh_rate_brightness_thresholds"; /** - * Key for accessing the ambient brightness thresholds for the configured refresh rate zone. - * The value will be a pair of comma separated integers representing the minimum and maximum - * thresholds of the zone, respectively, in lux. + * Key for accessing the low ambient brightness thresholds for the configured refresh + * rate zone. The value will be a pair of comma separated integers representing the minimum + * and maximum thresholds of the zone, respectively, in lux. + * + * Note that the name and value don't match because they were added before we had a high + * zone to consider. * * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER * @see android.R.array#config_ambientThresholdsOfPeakRefreshRate * @hide */ - String KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS = + String KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS = "peak_refresh_rate_ambient_thresholds"; + /** + * Key for refresh rate in the high zone defined by thresholds. + * + * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER + * @see android.R.integer#config_fixedRefreshRateInHighZone + */ + String KEY_REFRESH_RATE_IN_HIGH_ZONE = "refresh_rate_in_high_zone"; /** + * Key for accessing the display brightness thresholds for the configured refresh rate zone. + * The value will be a pair of comma separated integers representing the minimum and maximum + * thresholds of the zone, respectively, in display backlight units (i.e. [0, 255]). + * + * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER + * @see android.R.array#config_brightnessHighThresholdsOfFixedRefreshRate + * @hide + */ + String KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS = + "fixed_refresh_rate_high_display_brightness_thresholds"; + + /** + * Key for accessing the ambient brightness thresholds for the configured refresh rate zone. + * The value will be a pair of comma separated integers representing the minimum and maximum + * thresholds of the zone, respectively, in lux. + * + * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER + * @see android.R.array#config_ambientHighThresholdsOfFixedRefreshRate + * @hide + */ + String KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS = + "fixed_refresh_rate_high_ambient_brightness_thresholds"; + /** * Key for default peak refresh rate * * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER diff --git a/core/java/android/net/NetworkProvider.java b/core/java/android/net/NetworkProvider.java index d31218d9b67b..a17a49897d39 100644 --- a/core/java/android/net/NetworkProvider.java +++ b/core/java/android/net/NetworkProvider.java @@ -51,13 +51,6 @@ public class NetworkProvider { public static final int ID_NONE = -1; /** - * A hardcoded ID for NetworkAgents representing VPNs. These agents are not created by any - * provider, so they use this constant for clarity instead of NONE. - * @hide only used by ConnectivityService. - */ - public static final int ID_VPN = -2; - - /** * The first providerId value that will be allocated. * @hide only used by ConnectivityService. */ diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java index 0f2a9f21a0bc..f76eb86f0fca 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -1138,8 +1138,7 @@ public abstract class VibrationEffect implements Parcelable { * * @param primitiveId The primitive to add * @param scale The scale to apply to the intensity of the primitive. - * @param delay The amount of time, in milliseconds, to wait between playing the prior - * primitive and this one + * @param delay The amount of time in milliseconds to wait before playing this primitive * @return The {@link Composition} object to enable adding multiple primitives in one chain. */ @NonNull diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index cfc3e01c23c5..870d224f2ff7 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -156,10 +156,6 @@ public class StorageManager { /** {@hide} */ public static final String PROP_VIRTUAL_DISK = "persist.sys.virtual_disk"; /** {@hide} */ - public static final String PROP_ISOLATED_STORAGE = "persist.sys.isolated_storage"; - /** {@hide} */ - public static final String PROP_ISOLATED_STORAGE_SNAPSHOT = "sys.isolated_storage_snapshot"; - /** {@hide} */ public static final String PROP_FORCED_SCOPED_STORAGE_WHITELIST = "forced_scoped_storage_whitelist"; @@ -263,10 +259,6 @@ public class StorageManager { public static final int DEBUG_SDCARDFS_FORCE_OFF = 1 << 4; /** {@hide} */ public static final int DEBUG_VIRTUAL_DISK = 1 << 5; - /** {@hide} */ - public static final int DEBUG_ISOLATED_STORAGE_FORCE_ON = 1 << 6; - /** {@hide} */ - public static final int DEBUG_ISOLATED_STORAGE_FORCE_OFF = 1 << 7; /** {@hide} */ public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE; @@ -1695,16 +1687,13 @@ public class StorageManager { /** * Return if the currently booted device has the "isolated storage" feature - * flag enabled. This will eventually be fully enabled in the final - * {@link android.os.Build.VERSION_CODES#Q} release. + * flag enabled. * * @hide */ @SystemApi public static boolean hasIsolatedStorage() { - // Prefer to use snapshot for current boot when available - return SystemProperties.getBoolean(PROP_ISOLATED_STORAGE_SNAPSHOT, - SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, true)); + return false; } /** diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java index 4379ce1055ef..55ba15a5e57b 100644 --- a/core/java/android/os/storage/StorageManagerInternal.java +++ b/core/java/android/os/storage/StorageManagerInternal.java @@ -28,50 +28,8 @@ import java.util.Set; * @hide Only for use within the system server. */ public abstract class StorageManagerInternal { - - /** - * Policy that influences how external storage is mounted and reported. - */ - public interface ExternalStorageMountPolicy { - /** - * Gets the external storage mount mode for the given uid. - * - * @param uid The UID for which to determine mount mode. - * @param packageName The package in the UID for making the call. - * @return The mount mode. - * - * @see com.android.internal.os.Zygote#MOUNT_EXTERNAL_NONE - * @see com.android.internal.os.Zygote#MOUNT_EXTERNAL_DEFAULT - * @see com.android.internal.os.Zygote#MOUNT_EXTERNAL_READ - * @see com.android.internal.os.Zygote#MOUNT_EXTERNAL_WRITE - */ - public int getMountMode(int uid, String packageName); - - /** - * Gets whether external storage should be reported to the given UID. - * - * @param uid The UID for which to determine whether it has external storage. - * @param packageName The package in the UID for making the call. - * @return Weather to report external storage. - * @return True to report the state of external storage, false to - * report it as unmounted. - */ - public boolean hasExternalStorage(int uid, String packageName); - } - - /** - * Adds a policy for determining how external storage is mounted and reported. - * The mount mode is the most conservative result from querying all registered - * policies. Similarly, the reported state is the most conservative result from - * querying all registered policies. - * - * @param policy The policy to add. - */ - public abstract void addExternalStoragePolicy(ExternalStorageMountPolicy policy); - /** - * Gets the mount mode to use for a given UID as determined by consultin all - * policies. + * Gets the mount mode to use for a given UID * * @param uid The UID for which to get mount mode. * @param packageName The package in the UID for making the call. diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java index 901494b845b0..237a9f2867d6 100644 --- a/core/java/android/os/storage/VolumeInfo.java +++ b/core/java/android/os/storage/VolumeInfo.java @@ -200,6 +200,21 @@ public class VolumeInfo implements Parcelable { internalPath = parcel.readString8(); } + public VolumeInfo(VolumeInfo volumeInfo) { + this.id = volumeInfo.id; + this.type = volumeInfo.type; + this.disk = volumeInfo.disk; + this.partGuid = volumeInfo.partGuid; + this.mountFlags = volumeInfo.mountFlags; + this.mountUserId = volumeInfo.mountUserId; + this.state = volumeInfo.state; + this.fsType = volumeInfo.fsType; + this.fsUuid = volumeInfo.fsUuid; + this.fsLabel = volumeInfo.fsLabel; + this.path = volumeInfo.path; + this.internalPath = volumeInfo.internalPath; + } + @UnsupportedAppUsage public static @NonNull String getEnvironmentForState(int state) { final String envState = sStateToEnvironment.get(state); diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 714bcea9c01f..44cc0f5c330d 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -451,6 +451,22 @@ public final class DeviceConfig { "connectivity_thermal_power_manager"; /** + * Namespace for all statsd native features that can be applied immediately. + * + * @hide + */ + @SystemApi + public static final String NAMESPACE_STATSD_NATIVE = "statsd_native"; + + /** + * Namespace for all statsd native features that are applied on boot. + * + * @hide + */ + @SystemApi + public static final String NAMESPACE_STATSD_NATIVE_BOOT = "statsd_native_boot"; + + /** * Namespace for configuration related features. * * @hide diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 884f8ccd5e54..249a781509ac 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -13286,16 +13286,6 @@ public final class Settings { "storage_settings_clobber_threshold"; /** - * If set to 1, {@link Secure#LOCATION_MODE} will be set to {@link Secure#LOCATION_MODE_OFF} - * temporarily for all users. - * - * @hide - */ - @TestApi - public static final String LOCATION_GLOBAL_KILL_SWITCH = - "location_global_kill_switch"; - - /** * If set to 1, SettingsProvider's restoreAnyVersion="true" attribute will be ignored * and restoring to lower version of platform API will be skipped. * @@ -13415,11 +13405,6 @@ public final class Settings { public static final String MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY = "max_sound_trigger_detection_service_ops_per_day"; - /** {@hide} */ - public static final String ISOLATED_STORAGE_LOCAL = "isolated_storage_local"; - /** {@hide} */ - public static final String ISOLATED_STORAGE_REMOTE = "isolated_storage_remote"; - /** * Indicates whether aware is available in the current location. * @hide diff --git a/core/java/android/view/ContentInfo.java b/core/java/android/view/ContentInfo.java new file mode 100644 index 000000000000..b58937beed55 --- /dev/null +++ b/core/java/android/view/ContentInfo.java @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ClipData; +import android.net.Uri; +import android.os.Bundle; +import android.util.ArrayMap; + +import com.android.internal.util.Preconditions; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Map; +import java.util.Objects; +import java.util.function.Predicate; + +/** + * Holds all the relevant data for a request to {@link View#performReceiveContent}. + */ +public final class ContentInfo { + + /** + * Specifies the UI through which content is being inserted. Future versions of Android may + * support additional values. + * + * @hide + */ + @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_APP, SOURCE_CLIPBOARD, SOURCE_INPUT_METHOD, + SOURCE_DRAG_AND_DROP, SOURCE_AUTOFILL, SOURCE_PROCESS_TEXT}) + @Retention(RetentionPolicy.SOURCE) + public @interface Source {} + + /** + * Specifies that the operation was triggered by the app that contains the target view. + */ + public static final int SOURCE_APP = 0; + + /** + * Specifies that the operation was triggered by a paste from the clipboard (e.g. "Paste" or + * "Paste as plain text" action in the insertion/selection menu). + */ + public static final int SOURCE_CLIPBOARD = 1; + + /** + * Specifies that the operation was triggered from the soft keyboard (also known as input + * method editor or IME). See https://developer.android.com/guide/topics/text/image-keyboard + * for more info. + */ + public static final int SOURCE_INPUT_METHOD = 2; + + /** + * Specifies that the operation was triggered by the drag/drop framework. See + * https://developer.android.com/guide/topics/ui/drag-drop for more info. + */ + public static final int SOURCE_DRAG_AND_DROP = 3; + + /** + * Specifies that the operation was triggered by the autofill framework. See + * https://developer.android.com/guide/topics/text/autofill for more info. + */ + public static final int SOURCE_AUTOFILL = 4; + + /** + * Specifies that the operation was triggered by a result from a + * {@link android.content.Intent#ACTION_PROCESS_TEXT PROCESS_TEXT} action in the selection + * menu. + */ + public static final int SOURCE_PROCESS_TEXT = 5; + + /** + * Returns the symbolic name of the given source. + * + * @hide + */ + static String sourceToString(@Source int source) { + switch (source) { + case SOURCE_APP: return "SOURCE_APP"; + case SOURCE_CLIPBOARD: return "SOURCE_CLIPBOARD"; + case SOURCE_INPUT_METHOD: return "SOURCE_INPUT_METHOD"; + case SOURCE_DRAG_AND_DROP: return "SOURCE_DRAG_AND_DROP"; + case SOURCE_AUTOFILL: return "SOURCE_AUTOFILL"; + case SOURCE_PROCESS_TEXT: return "SOURCE_PROCESS_TEXT"; + } + return String.valueOf(source); + } + + /** + * Flags to configure the insertion behavior. + * + * @hide + */ + @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_CONVERT_TO_PLAIN_TEXT}) + @Retention(RetentionPolicy.SOURCE) + public @interface Flags {} + + /** + * Flag requesting that the content should be converted to plain text prior to inserting. + */ + public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1 << 0; + + /** + * Returns the symbolic names of the set flags or {@code "0"} if no flags are set. + * + * @hide + */ + static String flagsToString(@Flags int flags) { + if ((flags & FLAG_CONVERT_TO_PLAIN_TEXT) != 0) { + return "FLAG_CONVERT_TO_PLAIN_TEXT"; + } + return String.valueOf(flags); + } + + @NonNull + private final ClipData mClip; + @Source + private final int mSource; + @Flags + private final int mFlags; + @Nullable + private final Uri mLinkUri; + @Nullable + private final Bundle mExtras; + + private ContentInfo(Builder b) { + this.mClip = Objects.requireNonNull(b.mClip); + this.mSource = Preconditions.checkArgumentInRange(b.mSource, 0, SOURCE_PROCESS_TEXT, + "source"); + this.mFlags = Preconditions.checkFlagsArgument(b.mFlags, FLAG_CONVERT_TO_PLAIN_TEXT); + this.mLinkUri = b.mLinkUri; + this.mExtras = b.mExtras; + } + + @NonNull + @Override + public String toString() { + return "ContentInfo{" + + "clip=" + mClip + + ", source=" + sourceToString(mSource) + + ", flags=" + flagsToString(mFlags) + + ", linkUri=" + mLinkUri + + ", extras=" + mExtras + + "}"; + } + + /** + * The data to be inserted. + */ + @NonNull + public ClipData getClip() { + return mClip; + } + + /** + * The source of the operation. See {@code SOURCE_} constants. Future versions of Android + * may pass additional values. + */ + @Source + public int getSource() { + return mSource; + } + + /** + * Optional flags that control the insertion behavior. See {@code FLAG_} constants. + */ + @Flags + public int getFlags() { + return mFlags; + } + + /** + * Optional http/https URI for the content that may be provided by the IME. This is only + * populated if the source is {@link #SOURCE_INPUT_METHOD} and if a non-empty + * {@link android.view.inputmethod.InputContentInfo#getLinkUri linkUri} was passed by the + * IME. + */ + @Nullable + public Uri getLinkUri() { + return mLinkUri; + } + + /** + * Optional additional metadata. If the source is {@link #SOURCE_INPUT_METHOD}, this will + * include the {@link android.view.inputmethod.InputConnection#commitContent opts} passed by + * the IME. + */ + @Nullable + public Bundle getExtras() { + return mExtras; + } + + /** + * Partitions the content based on the given predicate. + * + * <p>Similar to a + * {@link java.util.stream.Collectors#partitioningBy(Predicate) partitioning collector}, + * this function classifies the content and organizes it into a map, grouping the items that + * matched vs didn't match the predicate. + * + * <p>Except for the {@link ClipData} items, the returned objects will contain all the same + * metadata as the original. + * + * @param itemPredicate The predicate to test each {@link ClipData.Item} to determine which + * partition to place it into. + * @return A map containing the partitioned content. The map will contain a single entry if + * all items were classified into the same partition (all matched or all didn't match the + * predicate) or two entries (if there's at least one item that matched the predicate and at + * least one item that didn't match the predicate). + */ + @NonNull + public Map<Boolean, ContentInfo> partition(@NonNull Predicate<ClipData.Item> itemPredicate) { + if (mClip.getItemCount() == 1) { + Map<Boolean, ContentInfo> result = new ArrayMap<>(1); + result.put(itemPredicate.test(mClip.getItemAt(0)), this); + return result; + } + ArrayList<ClipData.Item> accepted = new ArrayList<>(); + ArrayList<ClipData.Item> remaining = new ArrayList<>(); + for (int i = 0; i < mClip.getItemCount(); i++) { + ClipData.Item item = mClip.getItemAt(i); + if (itemPredicate.test(item)) { + accepted.add(item); + } else { + remaining.add(item); + } + } + Map<Boolean, ContentInfo> result = new ArrayMap<>(2); + if (!accepted.isEmpty()) { + ClipData acceptedClip = new ClipData(mClip.getDescription(), accepted); + result.put(true, new Builder(this).setClip(acceptedClip).build()); + } + if (!remaining.isEmpty()) { + ClipData remainingClip = new ClipData(mClip.getDescription(), remaining); + result.put(false, new Builder(this).setClip(remainingClip).build()); + } + return result; + } + + /** + * Builder for {@link ContentInfo}. + */ + public static final class Builder { + @NonNull + private ClipData mClip; + @Source + private int mSource; + @Flags + private int mFlags; + @Nullable + private Uri mLinkUri; + @Nullable + private Bundle mExtras; + + /** + * Creates a new builder initialized with the data from the given builder. + */ + public Builder(@NonNull ContentInfo other) { + mClip = other.mClip; + mSource = other.mSource; + mFlags = other.mFlags; + mLinkUri = other.mLinkUri; + mExtras = other.mExtras; + } + + /** + * Creates a new builder. + * @param clip The data to insert. + * @param source The source of the operation. See {@code SOURCE_} constants. + */ + public Builder(@NonNull ClipData clip, @Source int source) { + mClip = clip; + mSource = source; + } + + /** + * Sets the data to be inserted. + * @param clip The data to insert. + * @return this builder + */ + @NonNull + public Builder setClip(@NonNull ClipData clip) { + mClip = clip; + return this; + } + + /** + * Sets the source of the operation. + * @param source The source of the operation. See {@code SOURCE_} constants. + * @return this builder + */ + @NonNull + public Builder setSource(@Source int source) { + mSource = source; + return this; + } + + /** + * Sets flags that control content insertion behavior. + * @param flags Optional flags to configure the insertion behavior. Use 0 for default + * behavior. See {@code FLAG_} constants. + * @return this builder + */ + @NonNull + public Builder setFlags(@Flags int flags) { + mFlags = flags; + return this; + } + + /** + * Sets the http/https URI for the content. See + * {@link android.view.inputmethod.InputContentInfo#getLinkUri} for more info. + * @param linkUri Optional http/https URI for the content. + * @return this builder + */ + @NonNull + public Builder setLinkUri(@Nullable Uri linkUri) { + mLinkUri = linkUri; + return this; + } + + /** + * Sets additional metadata. + * @param extras Optional bundle with additional metadata. + * @return this builder + */ + @NonNull + public Builder setExtras(@Nullable Bundle extras) { + mExtras = extras; + return this; + } + + /** + * @return A new {@link ContentInfo} instance with the data from this builder. + */ + @NonNull + public ContentInfo build() { + return new ContentInfo(this); + } + } +} diff --git a/core/java/android/view/Gravity.java b/core/java/android/view/Gravity.java index defa58e10e6e..c8bfd36b2b48 100644 --- a/core/java/android/view/Gravity.java +++ b/core/java/android/view/Gravity.java @@ -15,8 +15,13 @@ */ package android.view; + +import android.annotation.IntDef; import android.graphics.Rect; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Standard constants and tools for placing an object within a potentially * larger container. @@ -122,6 +127,32 @@ public class Gravity */ public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = START | END; + + /** + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, value = { + Gravity.FILL, + Gravity.FILL_HORIZONTAL, + Gravity.FILL_VERTICAL, + Gravity.START, + Gravity.END, + Gravity.LEFT, + Gravity.RIGHT, + Gravity.TOP, + Gravity.BOTTOM, + Gravity.CENTER, + Gravity.CENTER_HORIZONTAL, + Gravity.CENTER_VERTICAL, + Gravity.DISPLAY_CLIP_HORIZONTAL, + Gravity.DISPLAY_CLIP_VERTICAL, + Gravity.CLIP_HORIZONTAL, + Gravity.CLIP_VERTICAL, + Gravity.NO_GRAVITY + }) + public @interface GravityFlags {} + /** * Apply a gravity constant to an object. This supposes that the layout direction is LTR. * diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 1a6eea554cae..a23b7e193024 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -32,6 +32,7 @@ import android.graphics.Region; import android.os.Bundle; import android.os.IRemoteCallback; import android.os.ParcelFileDescriptor; +import android.service.attestation.ImpressionToken; import android.view.DisplayCutout; import android.view.IApplicationToken; import android.view.IAppTransitionAnimationSpecsFuture; @@ -760,4 +761,23 @@ interface IWindowManager * {@link android.content.pm.PackageManager#getHoldLockToken()}. */ void holdLock(in IBinder token, in int durationMs); + + /** + * Gets an array of support hashing algorithms that can be used to generate the hash of the + * screenshot. The String value of one algorithm should be used when requesting to generate + * the impression attestation token. + * + * @return a String array of supported hashing algorithms. + */ + String[] getSupportedImpressionAlgorithms(); + + /** + * Validate the impression token was generated by the system. The impression token passed in + * should be the token generated when calling {@link IWindowSession#generateImpressionToken} + * + * @param impressionToken The token to verify that it was generated by the system. + * @return true if the token was generated by the system or false if the token cannot be + * verified. + */ + boolean verifyImpressionToken(in ImpressionToken impressionToken); } diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 0089a852a893..cfdaf8ccc5fb 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -22,6 +22,7 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; import android.os.Bundle; +import android.service.attestation.ImpressionToken; import android.util.MergedConfiguration; import android.view.DisplayCutout; import android.view.InputChannel; @@ -344,4 +345,16 @@ interface IWindowSession { * window, the system will try to find a new focus target. */ void grantEmbeddedWindowFocus(IWindow window, in IBinder inputToken, boolean grantFocus); + + /** + * Generates an impression token that can be used to validate whether specific content was on + * screen. + * + * @param window The token for the window where the view to attest is shown. + * @param boundsInWindow The size and position of the ads view in the window + * @param hashAlgorithm The String for the hashing algorithm to use based on values returned + * from {@link IWindowManager#getSupportedImpressionAlgorithms()} + */ + ImpressionToken generateImpressionToken(IWindow window, in Rect boundsInWindow, + in String hashAlgorithm); } diff --git a/core/java/android/view/OnReceiveContentListener.java b/core/java/android/view/OnReceiveContentListener.java index db9c53863c7f..b551fa80b5cd 100644 --- a/core/java/android/view/OnReceiveContentListener.java +++ b/core/java/android/view/OnReceiveContentListener.java @@ -16,22 +16,8 @@ package android.view; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.ClipData; -import android.net.Uri; -import android.os.Bundle; -import android.util.ArrayMap; - -import com.android.internal.util.Preconditions; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.Map; -import java.util.Objects; -import java.util.function.Predicate; /** * Listener for apps to implement handling for insertion of content. Content may be both text and @@ -48,10 +34,13 @@ import java.util.function.Predicate; * public static final String[] MIME_TYPES = new String[] {"image/*", "video/*"}; * * @Override - * public Payload onReceiveContent(View view, Payload payload) { - * Map<Boolean, Payload> split = payload.partition(item -> item.getUri() != null); - * if (split.get(true) != null) { - * ClipData clip = payload.getClip(); + * public ContentInfo onReceiveContent(View view, ContentInfo payload) { + * Map<Boolean, ContentInfo> split = + * payload.partition(item -> item.getUri() != null); + * ContentInfo uriItems = split.get(true); + * ContentInfo remainingItems = split.get(false); + * if (uriItems != null) { + * ClipData clip = uriItems.getClip(); * for (int i = 0; i < clip.getItemCount(); i++) { * Uri uri = clip.getItemAt(i).getUri(); * // ... app-specific logic to handle the URI ... @@ -59,7 +48,7 @@ import java.util.function.Predicate; * } * // Return anything that we didn't handle ourselves. This preserves the default platform * // behavior for text and anything else for which we are not implementing custom handling. - * return split.get(false); + * return remainingItems; * } * } * @@ -83,8 +72,8 @@ public interface OnReceiveContentListener { * handling. For example, an implementation may provide handling for content URIs (to provide * support for inserting images, etc) and delegate the processing of text to the platform to * preserve the common behavior for inserting text. See the class javadoc for a sample - * implementation and see {@link Payload#partition} for a convenient way to split the passed-in - * content. + * implementation and see {@link ContentInfo#partition} for a convenient way to split the + * passed-in content. * * <p>If implementing handling for text: if the view has a selection, the selection should * be overwritten by the passed-in content; if there's no selection, the passed-in content @@ -103,314 +92,6 @@ public interface OnReceiveContentListener { * succeed even if this method returns null. For example, an app may end up not inserting * an item if it exceeds the app's size limit for that type of content. */ - @Nullable Payload onReceiveContent(@NonNull View view, @NonNull Payload payload); - - /** - * Holds all the relevant data for a request to {@link OnReceiveContentListener}. - */ - final class Payload { - - /** - * Specifies the UI through which content is being inserted. Future versions of Android may - * support additional values. - * - * @hide - */ - @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_APP, SOURCE_CLIPBOARD, SOURCE_INPUT_METHOD, - SOURCE_DRAG_AND_DROP, SOURCE_AUTOFILL, SOURCE_PROCESS_TEXT}) - @Retention(RetentionPolicy.SOURCE) - public @interface Source {} - - /** - * Specifies that the operation was triggered by the app that contains the target view. - */ - public static final int SOURCE_APP = 0; - - /** - * Specifies that the operation was triggered by a paste from the clipboard (e.g. "Paste" or - * "Paste as plain text" action in the insertion/selection menu). - */ - public static final int SOURCE_CLIPBOARD = 1; - - /** - * Specifies that the operation was triggered from the soft keyboard (also known as input - * method editor or IME). See https://developer.android.com/guide/topics/text/image-keyboard - * for more info. - */ - public static final int SOURCE_INPUT_METHOD = 2; - - /** - * Specifies that the operation was triggered by the drag/drop framework. See - * https://developer.android.com/guide/topics/ui/drag-drop for more info. - */ - public static final int SOURCE_DRAG_AND_DROP = 3; - - /** - * Specifies that the operation was triggered by the autofill framework. See - * https://developer.android.com/guide/topics/text/autofill for more info. - */ - public static final int SOURCE_AUTOFILL = 4; - - /** - * Specifies that the operation was triggered by a result from a - * {@link android.content.Intent#ACTION_PROCESS_TEXT PROCESS_TEXT} action in the selection - * menu. - */ - public static final int SOURCE_PROCESS_TEXT = 5; - - /** - * Returns the symbolic name of the given source. - * - * @hide - */ - static String sourceToString(@Source int source) { - switch (source) { - case SOURCE_APP: return "SOURCE_APP"; - case SOURCE_CLIPBOARD: return "SOURCE_CLIPBOARD"; - case SOURCE_INPUT_METHOD: return "SOURCE_INPUT_METHOD"; - case SOURCE_DRAG_AND_DROP: return "SOURCE_DRAG_AND_DROP"; - case SOURCE_AUTOFILL: return "SOURCE_AUTOFILL"; - case SOURCE_PROCESS_TEXT: return "SOURCE_PROCESS_TEXT"; - } - return String.valueOf(source); - } - - /** - * Flags to configure the insertion behavior. - * - * @hide - */ - @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_CONVERT_TO_PLAIN_TEXT}) - @Retention(RetentionPolicy.SOURCE) - public @interface Flags {} - - /** - * Flag requesting that the content should be converted to plain text prior to inserting. - */ - public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1 << 0; - - /** - * Returns the symbolic names of the set flags or {@code "0"} if no flags are set. - * - * @hide - */ - static String flagsToString(@Flags int flags) { - if ((flags & FLAG_CONVERT_TO_PLAIN_TEXT) != 0) { - return "FLAG_CONVERT_TO_PLAIN_TEXT"; - } - return String.valueOf(flags); - } - - @NonNull private final ClipData mClip; - private final @Source int mSource; - private final @Flags int mFlags; - @Nullable private final Uri mLinkUri; - @Nullable private final Bundle mExtras; - - private Payload(Builder b) { - this.mClip = Objects.requireNonNull(b.mClip); - this.mSource = Preconditions.checkArgumentInRange(b.mSource, 0, SOURCE_PROCESS_TEXT, - "source"); - this.mFlags = Preconditions.checkFlagsArgument(b.mFlags, FLAG_CONVERT_TO_PLAIN_TEXT); - this.mLinkUri = b.mLinkUri; - this.mExtras = b.mExtras; - } - - @NonNull - @Override - public String toString() { - return "Payload{" - + "clip=" + mClip - + ", source=" + sourceToString(mSource) - + ", flags=" + flagsToString(mFlags) - + ", linkUri=" + mLinkUri - + ", extras=" + mExtras - + "}"; - } - - /** - * The data to be inserted. - */ - public @NonNull ClipData getClip() { - return mClip; - } - - /** - * The source of the operation. See {@code SOURCE_} constants. Future versions of Android - * may pass additional values. - */ - public @Source int getSource() { - return mSource; - } - - /** - * Optional flags that control the insertion behavior. See {@code FLAG_} constants. - */ - public @Flags int getFlags() { - return mFlags; - } - - /** - * Optional http/https URI for the content that may be provided by the IME. This is only - * populated if the source is {@link #SOURCE_INPUT_METHOD} and if a non-empty - * {@link android.view.inputmethod.InputContentInfo#getLinkUri linkUri} was passed by the - * IME. - */ - public @Nullable Uri getLinkUri() { - return mLinkUri; - } - - /** - * Optional additional metadata. If the source is {@link #SOURCE_INPUT_METHOD}, this will - * include the {@link android.view.inputmethod.InputConnection#commitContent opts} passed by - * the IME. - */ - public @Nullable Bundle getExtras() { - return mExtras; - } - - /** - * Partitions this payload based on the given predicate. - * - * <p>Similar to a - * {@link java.util.stream.Collectors#partitioningBy(Predicate) partitioning collector}, - * this function classifies the content in this payload and organizes it into a map, - * grouping the content that matched vs didn't match the predicate. - * - * <p>Except for the {@link ClipData} items, the returned payloads will contain all the same - * metadata as the original payload. - * - * @param itemPredicate The predicate to test each {@link ClipData.Item} to determine which - * partition to place it into. - * @return A map containing the partitioned content. The map will contain a single entry if - * all items were classified into the same partition (all matched or all didn't match the - * predicate) or two entries (if there's at least one item that matched the predicate and at - * least one item that didn't match the predicate). - */ - public @NonNull Map<Boolean, Payload> partition( - @NonNull Predicate<ClipData.Item> itemPredicate) { - if (mClip.getItemCount() == 1) { - Map<Boolean, Payload> result = new ArrayMap<>(1); - result.put(itemPredicate.test(mClip.getItemAt(0)), this); - return result; - } - ArrayList<ClipData.Item> accepted = new ArrayList<>(); - ArrayList<ClipData.Item> remaining = new ArrayList<>(); - for (int i = 0; i < mClip.getItemCount(); i++) { - ClipData.Item item = mClip.getItemAt(i); - if (itemPredicate.test(item)) { - accepted.add(item); - } else { - remaining.add(item); - } - } - Map<Boolean, Payload> result = new ArrayMap<>(2); - if (!accepted.isEmpty()) { - ClipData acceptedClip = new ClipData(mClip.getDescription(), accepted); - result.put(true, new Builder(this).setClip(acceptedClip).build()); - } - if (!remaining.isEmpty()) { - ClipData remainingClip = new ClipData(mClip.getDescription(), remaining); - result.put(false, new Builder(this).setClip(remainingClip).build()); - } - return result; - } - - /** - * Builder for {@link Payload}. - */ - public static final class Builder { - @NonNull private ClipData mClip; - private @Source int mSource; - private @Flags int mFlags; - @Nullable private Uri mLinkUri; - @Nullable private Bundle mExtras; - - /** - * Creates a new builder initialized with the data from the given builder. - */ - public Builder(@NonNull Payload payload) { - mClip = payload.mClip; - mSource = payload.mSource; - mFlags = payload.mFlags; - mLinkUri = payload.mLinkUri; - mExtras = payload.mExtras; - } - - /** - * Creates a new builder. - * @param clip The data to insert. - * @param source The source of the operation. See {@code SOURCE_} constants. - */ - public Builder(@NonNull ClipData clip, @Source int source) { - mClip = clip; - mSource = source; - } - - /** - * Sets the data to be inserted. - * @param clip The data to insert. - * @return this builder - */ - @NonNull - public Builder setClip(@NonNull ClipData clip) { - mClip = clip; - return this; - } - - /** - * Sets the source of the operation. - * @param source The source of the operation. See {@code SOURCE_} constants. - * @return this builder - */ - @NonNull - public Builder setSource(@Source int source) { - mSource = source; - return this; - } - - /** - * Sets flags that control content insertion behavior. - * @param flags Optional flags to configure the insertion behavior. Use 0 for default - * behavior. See {@code FLAG_} constants. - * @return this builder - */ - @NonNull - public Builder setFlags(@Flags int flags) { - mFlags = flags; - return this; - } - - /** - * Sets the http/https URI for the content. See - * {@link android.view.inputmethod.InputContentInfo#getLinkUri} for more info. - * @param linkUri Optional http/https URI for the content. - * @return this builder - */ - @NonNull - public Builder setLinkUri(@Nullable Uri linkUri) { - mLinkUri = linkUri; - return this; - } - - /** - * Sets additional metadata. - * @param extras Optional bundle with additional metadata. - * @return this builder - */ - @NonNull - public Builder setExtras(@Nullable Bundle extras) { - mExtras = extras; - return this; - } - - /** - * @return A new {@link Payload} instance with the data from this builder. - */ - @NonNull - public Payload build() { - return new Payload(this); - } - } - } + @Nullable + ContentInfo onReceiveContent(@NonNull View view, @NonNull ContentInfo payload); } diff --git a/core/java/android/view/TEST_MAPPING b/core/java/android/view/TEST_MAPPING index 4ea4310b25a4..c8b746f2248f 100644 --- a/core/java/android/view/TEST_MAPPING +++ b/core/java/android/view/TEST_MAPPING @@ -2,6 +2,24 @@ "presubmit": [ { "name": "CtsAccelerationTestCases" + }, + { + "name": "CtsOsTestCases", + "options": [ + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "include-filter": "android.os.cts.StrictModeTest" + } + ], + "file_patterns": ["(/|^)ViewConfiguration.java", "(/|^)GestureDetector.java"] } ], "imports": [ diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 30ec2b050dbe..a5c66537b11f 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -112,7 +112,6 @@ import android.view.AccessibilityIterators.TextSegmentIterator; import android.view.AccessibilityIterators.WordTextSegmentIterator; import android.view.ContextMenu.ContextMenuInfo; import android.view.InputDevice.InputSourceClass; -import android.view.OnReceiveContentListener.Payload; import android.view.Window.OnContentApplyWindowInsetsListener; import android.view.WindowInsets.Type; import android.view.WindowInsetsAnimation.Bounds; @@ -9063,11 +9062,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The portion of the passed-in content that was not accepted (may be all, some, or none * of the passed-in content). */ - public @Nullable Payload performReceiveContent(@NonNull Payload payload) { + @Nullable + public ContentInfo performReceiveContent(@NonNull ContentInfo payload) { final OnReceiveContentListener listener = (mListenerInfo == null) ? null : getListenerInfo().mOnReceiveContentListener; if (listener != null) { - final Payload remaining = listener.onReceiveContent(this, payload); + final ContentInfo remaining = listener.onReceiveContent(this, payload); return (remaining == null) ? null : onReceiveContent(remaining); } return onReceiveContent(payload); @@ -9088,7 +9088,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The portion of the passed-in content that was not handled (may be all, some, or none * of the passed-in content). */ - public @Nullable Payload onReceiveContent(@NonNull Payload payload) { + @Nullable + public ContentInfo onReceiveContent(@NonNull ContentInfo payload) { return payload; } @@ -9113,7 +9114,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The MIME types accepted by {@link #performReceiveContent} for this view (may * include patterns such as "image/*"). */ - public @Nullable String[] getOnReceiveContentMimeTypes() { + @Nullable + public String[] getOnReceiveContentMimeTypes() { return mOnReceiveContentMimeTypes; } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 8c8ea00ef41f..b9afbc95de0d 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -104,6 +104,7 @@ import android.os.Parcelable; import android.text.TextUtils; import android.util.Log; import android.util.proto.ProtoOutputStream; +import android.view.Gravity.GravityFlags; import android.view.View.OnApplyWindowInsetsListener; import android.view.WindowInsets.Side; import android.view.WindowInsets.Side.InsetsSide; @@ -2157,15 +2158,6 @@ public interface WindowManager extends ViewManager { public static final int PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY = 0x00100000; /** - * Flag to indicate that this window should be considered a screen decoration similar to the - * nav bar and status bar. This will cause this window to affect the window insets reported - * to other windows when it is visible. - * @hide - */ - @RequiresPermission(permission.STATUS_BAR_SERVICE) - public static final int PRIVATE_FLAG_IS_SCREEN_DECOR = 0x00400000; - - /** * Flag to indicate that the status bar window is in a state such that it forces showing * the navigation bar unless the navigation bar window is explicitly set to * {@link View#GONE}. @@ -2270,7 +2262,6 @@ public interface WindowManager extends ViewManager { PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE, SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY, - PRIVATE_FLAG_IS_SCREEN_DECOR, PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION, PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC, PRIVATE_FLAG_USE_BLAST, @@ -2358,10 +2349,6 @@ public interface WindowManager extends ViewManager { equals = PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY, name = "IS_ROUNDED_CORNERS_OVERLAY"), @ViewDebug.FlagToString( - mask = PRIVATE_FLAG_IS_SCREEN_DECOR, - equals = PRIVATE_FLAG_IS_SCREEN_DECOR, - name = "IS_SCREEN_DECOR"), - @ViewDebug.FlagToString( mask = PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION, equals = PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION, name = "STATUS_FORCE_SHOW_NAVIGATION"), @@ -2586,6 +2573,7 @@ public interface WindowManager extends ViewManager { * * @see Gravity */ + @GravityFlags public int gravity; /** diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index 0ed7ca79113e..673073ea197d 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -24,6 +24,7 @@ import android.graphics.Rect; import android.graphics.Region; import android.os.IBinder; import android.os.RemoteException; +import android.service.attestation.ImpressionToken; import android.util.Log; import android.util.MergedConfiguration; import android.window.ClientWindowFrames; @@ -460,4 +461,10 @@ public class WindowlessWindowManager implements IWindowSession { public void grantEmbeddedWindowFocus(IWindow callingWindow, IBinder targetInputToken, boolean grantFocus) { } + + @Override + public ImpressionToken generateImpressionToken(IWindow window, Rect boundsInWindow, + String hashAlgorithm) { + return null; + } } diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 8fdcac7b4f9d..364ae8186e54 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -19,7 +19,7 @@ package android.view.autofill; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE; import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED; -import static android.view.OnReceiveContentListener.Payload.SOURCE_AUTOFILL; +import static android.view.ContentInfo.SOURCE_AUTOFILL; import static android.view.autofill.Helper.sDebug; import static android.view.autofill.Helper.sVerbose; import static android.view.autofill.Helper.toList; @@ -61,8 +61,8 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.view.Choreographer; +import android.view.ContentInfo; import android.view.KeyEvent; -import android.view.OnReceiveContentListener.Payload; import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; @@ -2371,8 +2371,8 @@ public final class AutofillManager { reportAutofillContentFailure(id); return; } - Payload payload = new Payload.Builder(clip, SOURCE_AUTOFILL).build(); - Payload result = view.performReceiveContent(payload); + ContentInfo payload = new ContentInfo.Builder(clip, SOURCE_AUTOFILL).build(); + ContentInfo result = view.performReceiveContent(payload); if (result != null) { Log.w(TAG, "autofillContent(): receiver could not insert content: id=" + id + ", view=" + view + ", clip=" + clip); diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index 1ab9edf0caa2..f057c1239e52 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -16,7 +16,7 @@ package android.view.inputmethod; -import static android.view.OnReceiveContentListener.Payload.SOURCE_INPUT_METHOD; +import static android.view.ContentInfo.SOURCE_INPUT_METHOD; import android.annotation.CallSuper; import android.annotation.IntRange; @@ -38,9 +38,9 @@ import android.text.TextUtils; import android.text.method.MetaKeyKeyListener; import android.util.Log; import android.util.LogPrinter; +import android.view.ContentInfo; import android.view.KeyCharacterMap; import android.view.KeyEvent; -import android.view.OnReceiveContentListener; import android.view.View; import com.android.internal.util.Preconditions; @@ -952,8 +952,8 @@ public class BaseInputConnection implements InputConnection { } final ClipData clip = new ClipData(inputContentInfo.getDescription(), new ClipData.Item(inputContentInfo.getContentUri())); - final OnReceiveContentListener.Payload payload = - new OnReceiveContentListener.Payload.Builder(clip, SOURCE_INPUT_METHOD) + final ContentInfo payload = + new ContentInfo.Builder(clip, SOURCE_INPUT_METHOD) .setLinkUri(inputContentInfo.getLinkUri()) .setExtras(opts) .build(); diff --git a/core/java/android/view/inputmethod/DumpableInputConnection.java b/core/java/android/view/inputmethod/DumpableInputConnection.java new file mode 100644 index 000000000000..9819a5734491 --- /dev/null +++ b/core/java/android/view/inputmethod/DumpableInputConnection.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.inputmethod; + +import android.annotation.NonNull; +import android.util.proto.ProtoOutputStream; + +/** @hide */ +public interface DumpableInputConnection { + + /** + * Method used to dump state of InputConnection implementations of interest. + * + * @param proto Stream to write the state to + * @param fieldId FieldId of DumpableInputConnection as defined in the parent message + */ + void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId); +} diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 315d4461692c..8d2c2d96637f 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -22,6 +22,7 @@ import static android.util.imetracing.ImeTracing.PROTO_ARG; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.DISPLAY_ID; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.EDITOR_INFO; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_INSETS_SOURCE_CONSUMER; +import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INPUT_CONNECTION; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INPUT_METHOD_MANAGER; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.VIEW_ROOT_IMPL; import static android.view.inputmethod.InputMethodManagerProto.ACTIVE; @@ -87,8 +88,10 @@ import android.view.WindowManager.LayoutParams.SoftInputModeFlags; import android.view.autofill.AutofillManager; import com.android.internal.annotations.GuardedBy; +import com.android.internal.inputmethod.Completable; import com.android.internal.inputmethod.InputMethodDebug; import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry; +import com.android.internal.inputmethod.ResultCallbacks; import com.android.internal.inputmethod.StartInputFlags; import com.android.internal.inputmethod.StartInputReason; import com.android.internal.inputmethod.UnbindReason; @@ -665,6 +668,7 @@ public final class InputMethodManager { final int startInputReason = nextFocusHasConnection ? WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION : WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION; + final Completable.InputBindResult value = Completable.createInputBindResult(); mService.startInputOrWindowGainedFocus( startInputReason, mClient, focusedView.getWindowToken(), startInputFlags, softInputMode, @@ -672,7 +676,9 @@ public final class InputMethodManager { null, null, 0 /* missingMethodFlags */, - mCurRootView.mContext.getApplicationInfo().targetSdkVersion); + mCurRootView.mContext.getApplicationInfo().targetSdkVersion, + ResultCallbacks.of(value)); + Completable.getResult(value); // ignore the result } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -998,9 +1004,9 @@ public final class InputMethodManager { private final InputMethodManager mParentInputMethodManager; private final WeakReference<View> mServedView; - ControlledInputConnectionWrapper(Looper mainLooper, InputConnection conn, + ControlledInputConnectionWrapper(Looper icLooper, InputConnection conn, InputMethodManager inputMethodManager, View servedView) { - super(mainLooper, conn); + super(icLooper, conn); mParentInputMethodManager = inputMethodManager; mServedView = new WeakReference<>(servedView); } @@ -1046,6 +1052,18 @@ public final class InputMethodManager { + " mServedView=" + mServedView.get() + "}"; } + + void dumpDebug(ProtoOutputStream proto, long fieldId) { + // Check that the call is initiated in the main thread of the current InputConnection + // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are + // executed on this thread. Otherwise the messages are dispatched to the correct thread + // in IInputConnectionWrapper, but this is not wanted while dumpng, for performance + // reasons. + if (getInputConnection() instanceof DumpableInputConnection && Looper.myLooper() + == getLooper()) { + ((DumpableInputConnection) getInputConnection()).dumpDebug(proto, fieldId); + } + } } private static class ImeThreadFactory implements ThreadFactory { @@ -2026,10 +2044,13 @@ public final class InputMethodManager { if (DEBUG) Log.v(TAG, "START INPUT: view=" + dumpViewInfo(view) + " ic=" + ic + " tba=" + tba + " startInputFlags=" + InputMethodDebug.startInputFlagsToString(startInputFlags)); - res = mService.startInputOrWindowGainedFocus( + final Completable.InputBindResult value = Completable.createInputBindResult(); + mService.startInputOrWindowGainedFocus( startInputReason, mClient, windowGainingFocus, startInputFlags, softInputMode, windowFlags, tba, servedContext, missingMethodFlags, - view.getContext().getApplicationInfo().targetSdkVersion); + view.getContext().getApplicationInfo().targetSdkVersion, + ResultCallbacks.of(value)); + res = Completable.getResult(value); if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res); if (res == null) { Log.wtf(TAG, "startInputOrWindowGainedFocus must not return" @@ -2207,6 +2228,7 @@ public final class InputMethodManager { * @hide */ public void notifyImeHidden(IBinder windowToken) { + ImeTracing.getInstance().triggerClientDump("InputMethodManager#notifyImeHidden", this); synchronized (mH) { try { if (mCurMethod != null && mCurRootView != null @@ -3312,6 +3334,9 @@ public final class InputMethodManager { if (mImeInsetsConsumer != null) { mImeInsetsConsumer.dumpDebug(proto, IME_INSETS_SOURCE_CONSUMER); } + if (mServedInputConnectionWrapper != null) { + mServedInputConnectionWrapper.dumpDebug(proto, INPUT_CONNECTION); + } } } } diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java index 14abbdb6d33f..9c63d56d74da 100644 --- a/core/java/android/view/inputmethod/InputMethodSubtype.java +++ b/core/java/android/view/inputmethod/InputMethodSubtype.java @@ -71,7 +71,8 @@ public final class InputMethodSubtype implements Parcelable { // TODO: remove this private static final String EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME = "UntranslatableReplacementStringInSubtypeName"; - private static final int SUBTYPE_ID_NONE = 0; + /** {@hide} */ + public static final int SUBTYPE_ID_NONE = 0; private final boolean mIsAuxiliary; private final boolean mOverridesImplicitlyEnabledSubtype; diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 00ba326d2ba9..0025d1e2a853 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -16,7 +16,7 @@ package android.widget; -import static android.view.OnReceiveContentListener.Payload.SOURCE_DRAG_AND_DROP; +import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP; import android.R; import android.animation.ValueAnimator; @@ -87,6 +87,7 @@ import android.util.SparseArray; import android.util.TypedValue; import android.view.ActionMode; import android.view.ActionMode.Callback; +import android.view.ContentInfo; import android.view.ContextMenu; import android.view.ContextThemeWrapper; import android.view.DragAndDropPermissions; @@ -98,7 +99,6 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; -import android.view.OnReceiveContentListener; import android.view.SubMenu; import android.view.View; import android.view.View.DragShadowBuilder; @@ -2869,8 +2869,8 @@ public class Editor { final int originalLength = mTextView.getText().length(); Selection.setSelection((Spannable) mTextView.getText(), offset); final ClipData clip = event.getClipData(); - final OnReceiveContentListener.Payload payload = - new OnReceiveContentListener.Payload.Builder(clip, SOURCE_DRAG_AND_DROP) + final ContentInfo payload = + new ContentInfo.Builder(clip, SOURCE_DRAG_AND_DROP) .build(); mTextView.performReceiveContent(payload); if (dragDropIntoItself) { diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index 7c20472df357..4f1c40a8d1c2 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -2773,6 +2773,7 @@ public class NumberPicker extends LinearLayout { int left, int top, int right, int bottom) { AccessibilityNodeInfo info = mInputText.createAccessibilityNodeInfo(); info.setSource(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT); + info.setAccessibilityFocused(mAccessibilityFocusedView == VIRTUAL_VIEW_ID_INPUT); if (mAccessibilityFocusedView != VIRTUAL_VIEW_ID_INPUT) { info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS); } @@ -2802,6 +2803,7 @@ public class NumberPicker extends LinearLayout { info.setClickable(true); info.setLongClickable(true); info.setEnabled(NumberPicker.this.isEnabled()); + info.setAccessibilityFocused(mAccessibilityFocusedView == virtualViewId); Rect boundsInParent = mTempRect; boundsInParent.set(left, top, right, bottom); info.setVisibleToUser(isVisibleToUser(boundsInParent)); @@ -2843,6 +2845,7 @@ public class NumberPicker extends LinearLayout { info.setParent((View) getParentForAccessibility()); info.setEnabled(NumberPicker.this.isEnabled()); info.setScrollable(true); + info.setAccessibilityFocused(mAccessibilityFocusedView == View.NO_ID); final float applicationScale = getContext().getResources().getCompatibilityInfo().applicationScale; diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java index a034a7c2dc7e..e08ccfddc4c5 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -156,8 +156,7 @@ public final class SelectionActionModeHelper { mSmartSelectSprite != null ? this::startSelectionActionModeWithSmartSelectAnimation : this::startSelectionActionMode, - mTextClassificationHelper::getOriginalSelection, - mTextClassificationHelper::isTextClassifierDestroyed) + mTextClassificationHelper::getOriginalSelection) .execute(); } } @@ -179,8 +178,7 @@ public final class SelectionActionModeHelper { mTextClassificationHelper.getTimeoutDuration(), mTextClassificationHelper::classifyText, this::startLinkActionMode, - mTextClassificationHelper::getOriginalSelection, - mTextClassificationHelper::isTextClassifierDestroyed) + mTextClassificationHelper::getOriginalSelection) .execute(); } } @@ -196,8 +194,7 @@ public final class SelectionActionModeHelper { mTextClassificationHelper.getTimeoutDuration(), mTextClassificationHelper::classifyText, this::invalidateActionMode, - mTextClassificationHelper::getOriginalSelection, - mTextClassificationHelper::isTextClassifierDestroyed) + mTextClassificationHelper::getOriginalSelection) .execute(); } } @@ -995,7 +992,6 @@ public final class SelectionActionModeHelper { private final Supplier<SelectionResult> mSelectionResultSupplier; private final Consumer<SelectionResult> mSelectionResultCallback; private final Supplier<SelectionResult> mTimeOutResultSupplier; - private final Supplier<Boolean> mIsTextClassifierDestroyedSupplier; private final TextView mTextView; private final String mOriginalText; @@ -1010,16 +1006,13 @@ public final class SelectionActionModeHelper { @NonNull TextView textView, int timeOut, @NonNull Supplier<SelectionResult> selectionResultSupplier, @NonNull Consumer<SelectionResult> selectionResultCallback, - @NonNull Supplier<SelectionResult> timeOutResultSupplier, - @NonNull Supplier<Boolean> isTextClassifierDestroyedSupplier) { + @NonNull Supplier<SelectionResult> timeOutResultSupplier) { super(textView != null ? textView.getHandler() : null); mTextView = Objects.requireNonNull(textView); mTimeOutDuration = timeOut; mSelectionResultSupplier = Objects.requireNonNull(selectionResultSupplier); mSelectionResultCallback = Objects.requireNonNull(selectionResultCallback); mTimeOutResultSupplier = Objects.requireNonNull(timeOutResultSupplier); - mIsTextClassifierDestroyedSupplier = - Objects.requireNonNull(isTextClassifierDestroyedSupplier); // Make a copy of the original text. mOriginalText = getText(mTextView).toString(); } @@ -1033,14 +1026,8 @@ public final class SelectionActionModeHelper { try { result = mSelectionResultSupplier.get(); } catch (IllegalStateException e) { - // Swallows the exception if the text classifier session is destroyed - if (mIsTextClassifierDestroyedSupplier.get()) { - Log.w(LOG_TAG, - "TextClassificationAsyncTask failed because TextClassifier destroyed", - e); - } else { - throw e; - } + // TODO(b/174300371): Only swallows the exception if the TCSession is destroyed + Log.w(LOG_TAG, "TextClassificationAsyncTask failed.", e); } mTextView.removeCallbacks(onTimeOut); return result; @@ -1173,10 +1160,6 @@ public final class SelectionActionModeHelper { } } - public boolean isTextClassifierDestroyed() { - return mTextClassifier.get().isDestroyed(); - } - private boolean isDarkLaunchEnabled() { return TextClassificationManager.getSettings(mContext).isModelDarkLaunchEnabled(); } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 98f808784803..2357f368c428 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -17,10 +17,10 @@ package android.widget; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; -import static android.view.OnReceiveContentListener.Payload.FLAG_CONVERT_TO_PLAIN_TEXT; -import static android.view.OnReceiveContentListener.Payload.SOURCE_AUTOFILL; -import static android.view.OnReceiveContentListener.Payload.SOURCE_CLIPBOARD; -import static android.view.OnReceiveContentListener.Payload.SOURCE_PROCESS_TEXT; +import static android.view.ContentInfo.FLAG_CONVERT_TO_PLAIN_TEXT; +import static android.view.ContentInfo.SOURCE_AUTOFILL; +import static android.view.ContentInfo.SOURCE_CLIPBOARD; +import static android.view.ContentInfo.SOURCE_PROCESS_TEXT; import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY; import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH; import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX; @@ -146,6 +146,7 @@ import android.util.TypedValue; import android.view.AccessibilityIterators.TextSegmentIterator; import android.view.ActionMode; import android.view.Choreographer; +import android.view.ContentInfo; import android.view.ContextMenu; import android.view.DragEvent; import android.view.Gravity; @@ -154,7 +155,6 @@ import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MotionEvent; -import android.view.OnReceiveContentListener.Payload; import android.view.PointerIcon; import android.view.View; import android.view.ViewConfiguration; @@ -2151,7 +2151,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (result != null) { if (isTextEditable()) { ClipData clip = ClipData.newPlainText("", result); - Payload payload = new Payload.Builder(clip, SOURCE_PROCESS_TEXT).build(); + ContentInfo payload = + new ContentInfo.Builder(clip, SOURCE_PROCESS_TEXT).build(); performReceiveContent(payload); if (mEditor != null) { mEditor.refreshTextActionMode(); @@ -11857,7 +11858,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener + " cannot be autofilled into " + this); return; } - final Payload payload = new Payload.Builder(clip, SOURCE_AUTOFILL).build(); + final ContentInfo payload = new ContentInfo.Builder(clip, SOURCE_AUTOFILL).build(); performReceiveContent(payload); } @@ -12924,7 +12925,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (clip == null) { return; } - final Payload payload = new Payload.Builder(clip, SOURCE_CLIPBOARD) + final ContentInfo payload = new ContentInfo.Builder(clip, SOURCE_CLIPBOARD) .setFlags(withFormatting ? 0 : FLAG_CONVERT_TO_PLAIN_TEXT) .build(); performReceiveContent(payload); @@ -13740,8 +13741,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @return The portion of the passed-in content that was not handled (may be all, some, or none * of the passed-in content). */ + @Nullable @Override - public @Nullable Payload onReceiveContent(@NonNull Payload payload) { + public ContentInfo onReceiveContent(@NonNull ContentInfo payload) { if (mEditor != null) { return mEditor.getDefaultOnReceiveContentListener().onReceiveContent(this, payload); } diff --git a/core/java/android/widget/TextViewOnReceiveContentListener.java b/core/java/android/widget/TextViewOnReceiveContentListener.java index 7ef68ec7a4ee..8cef1061c423 100644 --- a/core/java/android/widget/TextViewOnReceiveContentListener.java +++ b/core/java/android/widget/TextViewOnReceiveContentListener.java @@ -17,10 +17,10 @@ package android.widget; import static android.content.ContentResolver.SCHEME_CONTENT; -import static android.view.OnReceiveContentListener.Payload.FLAG_CONVERT_TO_PLAIN_TEXT; -import static android.view.OnReceiveContentListener.Payload.SOURCE_AUTOFILL; -import static android.view.OnReceiveContentListener.Payload.SOURCE_DRAG_AND_DROP; -import static android.view.OnReceiveContentListener.Payload.SOURCE_INPUT_METHOD; +import static android.view.ContentInfo.FLAG_CONVERT_TO_PLAIN_TEXT; +import static android.view.ContentInfo.SOURCE_AUTOFILL; +import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP; +import static android.view.ContentInfo.SOURCE_INPUT_METHOD; import android.annotation.NonNull; import android.annotation.Nullable; @@ -38,9 +38,10 @@ import android.text.Selection; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.util.Log; +import android.view.ContentInfo; +import android.view.ContentInfo.Flags; +import android.view.ContentInfo.Source; import android.view.OnReceiveContentListener; -import android.view.OnReceiveContentListener.Payload.Flags; -import android.view.OnReceiveContentListener.Payload.Source; import android.view.View; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; @@ -53,20 +54,21 @@ import java.util.ArrayList; import java.util.Arrays; /** - * Default implementation of {@link OnReceiveContentListener} for editable - * {@link TextView} components. This class handles insertion of text (plain text, styled text, HTML, - * etc) but not images or other content. + * Default implementation for {@link View#onReceiveContent} for editable {@link TextView} + * components. This class handles insertion of text (plain text, styled text, HTML, etc) but not + * images or other content. * * @hide */ @VisibleForTesting public final class TextViewOnReceiveContentListener implements OnReceiveContentListener { - private static final String LOG_TAG = "OnReceiveContent"; + private static final String LOG_TAG = "ReceiveContent"; @Nullable private InputConnectionInfo mInputConnectionInfo; + @Nullable @Override - public @Nullable Payload onReceiveContent(@NonNull View view, @NonNull Payload payload) { + public ContentInfo onReceiveContent(@NonNull View view, @NonNull ContentInfo payload) { if (Log.isLoggable(LOG_TAG, Log.DEBUG)) { Log.d(LOG_TAG, "onReceive: " + payload); } @@ -126,7 +128,7 @@ public final class TextViewOnReceiveContentListener implements OnReceiveContentL editable.replace(start, end, replacement); } - private void onReceiveForAutofill(@NonNull TextView view, @NonNull Payload payload) { + private void onReceiveForAutofill(@NonNull TextView view, @NonNull ContentInfo payload) { ClipData clip = payload.getClip(); if (isUsageOfImeCommitContentEnabled(view)) { clip = handleNonTextViaImeCommitContent(clip); @@ -145,7 +147,8 @@ public final class TextViewOnReceiveContentListener implements OnReceiveContentL Selection.setSelection(editable, editable.length()); } - private static void onReceiveForDragAndDrop(@NonNull TextView view, @NonNull Payload payload) { + private static void onReceiveForDragAndDrop(@NonNull TextView view, + @NonNull ContentInfo payload) { final CharSequence text = coerceToText(payload.getClip(), view.getContext(), payload.getFlags()); replaceSelection((Editable) view.getText(), text); diff --git a/core/java/com/android/internal/inputmethod/CallbackUtils.java b/core/java/com/android/internal/inputmethod/CallbackUtils.java new file mode 100644 index 000000000000..ec6779216ae5 --- /dev/null +++ b/core/java/com/android/internal/inputmethod/CallbackUtils.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.inputmethod; + +import android.annotation.AnyThread; +import android.annotation.NonNull; +import android.os.RemoteException; + +import com.android.internal.view.InputBindResult; + +import java.util.function.Supplier; + +/** + * Defines a set of helper methods to callback corresponding results in {@link ResultCallbacks}. + */ +public final class CallbackUtils { + + /** + * Not intended to be instantiated. + */ + private CallbackUtils() { + } + + /** + * A utility method using given {@link IInputBindResultResultCallback} to callback the + * {@link InputBindResult}. + * + * @param callback {@link IInputBindResultResultCallback} to be called back. + * @param resultSupplier the supplier from which {@link InputBindResult} is provided. + */ + @AnyThread + public static void onResult(@NonNull IInputBindResultResultCallback callback, + @NonNull Supplier<InputBindResult> resultSupplier) { + try { + callback.onResult(resultSupplier.get()); + } catch (RemoteException ignored) { } + } +} diff --git a/core/java/com/android/internal/inputmethod/Completable.java b/core/java/com/android/internal/inputmethod/Completable.java index d8d1a7df6aa8..b9e1cf09dc07 100644 --- a/core/java/com/android/internal/inputmethod/Completable.java +++ b/core/java/com/android/internal/inputmethod/Completable.java @@ -124,6 +124,16 @@ public final class Completable { return true; } } + + /** + * Blocks the calling thread until this object becomes ready to return the value. + */ + @AnyThread + public void await() { + try { + mLatch.await(); + } catch (InterruptedException ignored) { } + } } /** @@ -250,6 +260,13 @@ public final class Completable { } /** + * @return an instance of {@link Completable.InputBindResult}. + */ + public static Completable.InputBindResult createInputBindResult() { + return new Completable.InputBindResult(); + } + + /** * Completable object of {@link java.lang.Boolean}. */ public static final class Boolean extends Values<java.lang.Boolean> { } @@ -278,6 +295,18 @@ public final class Completable { extends Values<com.android.internal.view.InputBindResult> { } /** + * Await the result by the {@link Completable.Values}. + * + * @return the result once {@link ValueBase#onComplete()} + */ + @AnyThread + @Nullable + public static <T> T getResult(@NonNull Completable.Values<T> value) { + value.await(); + return value.getValue(); + } + + /** * Await the result by the {@link Completable.Int}, and log it if there is no result after * given timeout. * diff --git a/core/java/com/android/internal/inputmethod/IInputBindResultResultCallback.aidl b/core/java/com/android/internal/inputmethod/IInputBindResultResultCallback.aidl new file mode 100644 index 000000000000..b52b3b100ed0 --- /dev/null +++ b/core/java/com/android/internal/inputmethod/IInputBindResultResultCallback.aidl @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.inputmethod; + +import com.android.internal.view.InputBindResult; + +oneway interface IInputBindResultResultCallback { + void onResult(in InputBindResult result); +}
\ No newline at end of file diff --git a/core/java/com/android/internal/inputmethod/ResultCallbacks.java b/core/java/com/android/internal/inputmethod/ResultCallbacks.java index 7131284e42df..c59dcf4ce420 100644 --- a/core/java/com/android/internal/inputmethod/ResultCallbacks.java +++ b/core/java/com/android/internal/inputmethod/ResultCallbacks.java @@ -21,6 +21,8 @@ import android.annotation.BinderThread; import android.annotation.NonNull; import android.annotation.Nullable; +import com.android.internal.view.InputBindResult; + import java.lang.ref.WeakReference; import java.util.concurrent.atomic.AtomicReference; @@ -154,4 +156,31 @@ public final class ResultCallbacks { } }; } + + /** + * Creates {@link IInputBindResultResultCallback.Stub} that is to set + * {@link Completable.InputBindResult} when receiving the result. + * + * @param value {@link Completable.InputBindResult} to be set when receiving the result. + * @return {@link IInputBindResultResultCallback.Stub} that can be passed as a binder IPC + * parameter. + */ + @AnyThread + public static IInputBindResultResultCallback.Stub of( + @NonNull Completable.InputBindResult value) { + final AtomicReference<WeakReference<Completable.InputBindResult>> + atomicRef = new AtomicReference<>(new WeakReference<>(value)); + + return new IInputBindResultResultCallback.Stub() { + @BinderThread + @Override + public void onResult(InputBindResult result) { + final Completable.InputBindResult value = unwrap(atomicRef); + if (value == null) { + return; + } + value.onComplete(result); + } + }; + } } diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java index ffc7f05eb703..1553e2eb0793 100644 --- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java +++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java @@ -48,7 +48,8 @@ import java.lang.annotation.Retention; SoftInputShowHideReason.HIDE_DOCKED_STACK_ATTACHED, SoftInputShowHideReason.HIDE_RECENTS_ANIMATION, SoftInputShowHideReason.HIDE_BUBBLES, - SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR}) + SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR, + SoftInputShowHideReason.HIDE_REMOVE_CLIENT}) public @interface SoftInputShowHideReason { /** Show soft input by {@link android.view.inputmethod.InputMethodManager#showSoftInput}. */ int SHOW_SOFT_INPUT = 0; @@ -161,4 +162,9 @@ public @interface SoftInputShowHideReason { * soft-input when the same window focused again to align with the same behavior prior to R. */ int HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR = 20; + + /** + * Hide soft input when a {@link com.android.internal.view.IInputMethodClient} is removed. + */ + int HIDE_REMOVE_CLIENT = 21; } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 7571f5db8cad..dcd74fd1ad3e 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -38,6 +38,7 @@ import android.net.NetworkStats; import android.net.Uri; import android.net.wifi.WifiManager; import android.os.BatteryManager; +import android.os.BatteryProperty; import android.os.BatteryStats; import android.os.Binder; import android.os.Build; @@ -54,6 +55,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.WorkSource; import android.os.WorkSource.WorkChain; @@ -144,6 +146,14 @@ import java.util.concurrent.locks.ReentrantLock; public class BatteryStatsImpl extends BatteryStats { private static final String TAG = "BatteryStatsImpl"; private static final boolean DEBUG = false; + + // TODO(b/169376495): STOPSHIP if true + private static final boolean DEBUG_FOREGROUND_STATS = true; + + private static final boolean ENABLE_FOREGROUND_STATS_COLLECTION = + DEBUG_FOREGROUND_STATS && SystemProperties.getBoolean( + "debug.battery_foreground_stats_collection", false); + public static final boolean DEBUG_ENERGY = false; private static final boolean DEBUG_ENERGY_CPU = DEBUG_ENERGY; private static final boolean DEBUG_BINDER_STATS = false; @@ -739,6 +749,37 @@ public class BatteryStatsImpl extends BatteryStats { long mTrackRunningHistoryElapsedRealtimeMs = 0; long mTrackRunningHistoryUptimeMs = 0; + private static final int FOREGROUND_UID_INITIAL_CAPACITY = 10; + private static final int INVALID_UID = -1; + + private final IntArray mForegroundUids = ENABLE_FOREGROUND_STATS_COLLECTION + ? new IntArray(FOREGROUND_UID_INITIAL_CAPACITY) : null; + + // Last recorded battery energy capacity. + // This is used for computing foregrund power per application. + // See: PowerForUid below + private long mLastBatteryEnergyCapacityNWh = 0; + + private static final class PowerForUid { + public long energyNwh = 0; + // Same as energyNwh, but not tracked for the first 2 minutes; + public long filteredEnergyNwh = 0; + public double totalHours = 0; + public long baseTimeMs = 0; + + double computePower() { + // units in nW + return totalHours != 0 ? energyNwh / totalHours : -1.0; + } + + double computeFilteredPower() { + // units in nW + return totalHours != 0 ? filteredEnergyNwh / totalHours : -1.0; + } + } + private final HashMap<Integer, PowerForUid> mUidToPower = ENABLE_FOREGROUND_STATS_COLLECTION + ? new HashMap<>() : null; + final BatteryStatsHistory mBatteryStatsHistory; final HistoryItem mHistoryCur = new HistoryItem(); @@ -1026,6 +1067,8 @@ public class BatteryStatsImpl extends BatteryStats { private int mNumConnectivityChange; + private int mBatteryVolt = -1; + private int mBatteryCharge = -1; private int mEstimatedBatteryCapacity = -1; private int mMinLearnedBatteryCapacity = -1; @@ -4154,6 +4197,49 @@ public class BatteryStatsImpl extends BatteryStats { // TODO(b/155216561): It is possible for isolated uids to be in a higher // state than its parent uid. We should track the highest state within the union of host // and isolated uids rather than only the parent uid. + + + int uidState = mapToInternalProcessState(state); + + boolean isForeground = (uidState == Uid.PROCESS_STATE_TOP) + || (uidState == Uid.PROCESS_STATE_FOREGROUND); + + + if (ENABLE_FOREGROUND_STATS_COLLECTION) { + boolean previouslyInForegrond = false; + for (int i = 0; i < mForegroundUids.size(); i++) { + if (mForegroundUids.get(i) == uid) { + previouslyInForegrond = true; + if (!isForeground) { + // If we were previously in the foreground, remove the uid + // from the foreground set and dirty the slot. + mForegroundUids.set(i, INVALID_UID); + final PowerForUid pfu = + mUidToPower.computeIfAbsent(uid, unused -> new PowerForUid()); + pfu.baseTimeMs = 0; + break; + } + } + } + + if (!previouslyInForegrond && isForeground) { + boolean addedToForeground = false; + // Check if we have a free slot to clobber... + for (int i = 0; i < mForegroundUids.size(); i++) { + if (mForegroundUids.get(i) == INVALID_UID) { + addedToForeground = true; + mForegroundUids.set(i, uid); + break; + } + } + + // ...if not, append to the end of the array. + if (!addedToForeground) { + mForegroundUids.add(uid); + } + } + } + FrameworkStatsLog.write(FrameworkStatsLog.UID_PROCESS_STATE_CHANGED, uid, ActivityManager.processStateAmToProto(state)); getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs) @@ -12968,6 +13054,7 @@ public class BatteryStatsImpl extends BatteryStats { doWrite = true; resetAllStatsLocked(mSecUptime, mSecRealtime); if (chargeUAh > 0 && level > 0) { + mBatteryCharge = chargeUAh; // Only use the reported coulomb charge value if it is supported and reported. mEstimatedBatteryCapacity = (int) ((chargeUAh / 1000) / (level / 100.0)); } @@ -13140,6 +13227,47 @@ public class BatteryStatsImpl extends BatteryStats { startRecordingHistory(elapsedRealtimeMs, uptimeMs, true); } } + + if (ENABLE_FOREGROUND_STATS_COLLECTION) { + mBatteryVolt = volt; + if (onBattery) { + final long energyNwh = (volt * (long) chargeUAh); + final long energyDelta = mLastBatteryEnergyCapacityNWh - energyNwh; + for (int i = 0; i < mForegroundUids.size(); i++) { + final int uid = mForegroundUids.get(i); + if (uid == INVALID_UID) { + continue; + } + final PowerForUid pfu = mUidToPower + .computeIfAbsent(uid, unused -> new PowerForUid()); + if (pfu.baseTimeMs <= 0) { + pfu.baseTimeMs = currentTimeMs; + } else { + // Check if mLastBatteryEnergyCapacityNWh > energyNwh, + // to make sure we only count discharges + if (energyDelta > 0) { + pfu.energyNwh += energyDelta; + // Convert from milliseconds to hours + // 1000 ms per second * 3600 seconds per hour + pfu.totalHours += ((double) (currentTimeMs - pfu.baseTimeMs) + / (1.0 * 1000 * 60 * 60)); + // Now convert from 2 minutes to hours + // 2 minutes = 1/30 of an hour + if (pfu.totalHours > (2.0 / 60)) { + pfu.filteredEnergyNwh += energyDelta; + } + + } + pfu.baseTimeMs = currentTimeMs; + } + } + mLastBatteryEnergyCapacityNWh = energyNwh; + } else if (onBattery != mOnBattery) { + // Transition to onBattery = false + mUidToPower.values().forEach(v -> v.baseTimeMs = 0); + } + } + mCurrentBatteryLevel = level; if (mDischargePlugLevel < 0) { mDischargePlugLevel = level; @@ -16056,6 +16184,48 @@ public class BatteryStatsImpl extends BatteryStats { } public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) { + if (ENABLE_FOREGROUND_STATS_COLLECTION) { + long actualCharge = -1; + long actualEnergy = -1; + try { + IBatteryPropertiesRegistrar registrar = + IBatteryPropertiesRegistrar.Stub.asInterface( + ServiceManager.getService("batteryproperties")); + if (registrar != null) { + BatteryProperty prop = new BatteryProperty(); + if (registrar.getProperty( + BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER, prop) == 0) { + actualCharge = prop.getLong(); + } + prop = new BatteryProperty(); + if (registrar.getProperty( + BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER, prop) == 0) { + actualEnergy = prop.getLong(); + } + } + } catch (RemoteException e) { + // Ignore. + } + pw.printf("ActualCharge (uAh): %d\n", (int) actualCharge); + pw.printf("ActualEnergy (nWh): %d\n", actualEnergy); + pw.printf("mBatteryCharge (uAh): %d\n", mBatteryCharge); + pw.printf("mBatteryVolts (mV): %d\n", mBatteryVolt); + pw.printf("est energy (nWh): %d\n", mBatteryVolt * (long) mBatteryCharge); + pw.printf("mEstimatedBatteryCapacity (mAh): %d\n", mEstimatedBatteryCapacity); + pw.printf("mMinLearnedBatteryCapacity (uAh): %d\n", mMinLearnedBatteryCapacity); + pw.printf("mMaxLearnedBatteryCapacity (uAh): %d\n", mMaxLearnedBatteryCapacity); + pw.printf("est. capacity: %f\n", + (float) actualCharge / (mEstimatedBatteryCapacity * 1000)); + pw.printf("mCurrentBatteryLevel: %d\n", mCurrentBatteryLevel); + pw.println("Total Power per app:"); + mUidToPower.entrySet().forEach(e -> + pw.printf("Uid: %d, Total watts (nW): %f\n", + e.getKey(), e.getValue().computePower())); + pw.println("Total Power per app after first 2 minutes initial launch:"); + mUidToPower.entrySet().forEach(e -> + pw.printf("Uid: %d, Total watts (nW): %f\n", + e.getKey(), e.getValue().computeFilteredPower())); + } if (DEBUG) { pw.println("mOnBatteryTimeBase:"); mOnBatteryTimeBase.dump(pw, " "); diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index 33ebe43cb23a..4deb40a0d772 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -25,6 +25,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.os.Trace; import android.util.Log; import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; @@ -111,6 +112,12 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } } + protected Looper getLooper() { + synchronized (mMainLooper) { + return mMainLooper; + } + } + protected boolean isFinished() { synchronized (mLock) { return mFinished; @@ -259,61 +266,80 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { void executeMessage(Message msg) { switch (msg.what) { case DO_GET_TEXT_AFTER_CURSOR: { - final ICharSequenceResultCallback callback = (ICharSequenceResultCallback) msg.obj; - final InputConnection ic = getInputConnection(); - final CharSequence result; - if (ic == null || !isActive()) { - Log.w(TAG, "getTextAfterCursor on inactive InputConnection"); - result = null; - } else { - result = ic.getTextAfterCursor(msg.arg1, msg.arg2); - } + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextAfterCursor"); try { - callback.onResult(result); - } catch (RemoteException e) { - Log.w(TAG, "Failed to return the result to getTextAfterCursor()." + final ICharSequenceResultCallback callback = + (ICharSequenceResultCallback) msg.obj; + final InputConnection ic = getInputConnection(); + final CharSequence result; + if (ic == null || !isActive()) { + Log.w(TAG, "getTextAfterCursor on inactive InputConnection"); + result = null; + } else { + result = ic.getTextAfterCursor(msg.arg1, msg.arg2); + } + try { + callback.onResult(result); + } catch (RemoteException e) { + Log.w(TAG, "Failed to return the result to getTextAfterCursor()." + " result=" + result, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } return; } case DO_GET_TEXT_BEFORE_CURSOR: { - final ICharSequenceResultCallback callback = (ICharSequenceResultCallback) msg.obj; - final InputConnection ic = getInputConnection(); - final CharSequence result; - if (ic == null || !isActive()) { - Log.w(TAG, "getTextBeforeCursor on inactive InputConnection"); - result = null; - } else { - result = ic.getTextBeforeCursor(msg.arg1, msg.arg2); - } + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextBeforeCursor"); try { - callback.onResult(result); - } catch (RemoteException e) { - Log.w(TAG, "Failed to return the result to getTextBeforeCursor()." + final ICharSequenceResultCallback callback = + (ICharSequenceResultCallback) msg.obj; + final InputConnection ic = getInputConnection(); + final CharSequence result; + if (ic == null || !isActive()) { + Log.w(TAG, "getTextBeforeCursor on inactive InputConnection"); + result = null; + } else { + result = ic.getTextBeforeCursor(msg.arg1, msg.arg2); + } + try { + callback.onResult(result); + } catch (RemoteException e) { + Log.w(TAG, "Failed to return the result to getTextBeforeCursor()." + " result=" + result, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } return; } case DO_GET_SELECTED_TEXT: { - final ICharSequenceResultCallback callback = (ICharSequenceResultCallback) msg.obj; - final InputConnection ic = getInputConnection(); - final CharSequence result; - if (ic == null || !isActive()) { - Log.w(TAG, "getSelectedText on inactive InputConnection"); - result = null; - } else { - result = ic.getSelectedText(msg.arg1); - } + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSelectedText"); try { - callback.onResult(result); - } catch (RemoteException e) { - Log.w(TAG, "Failed to return the result to getSelectedText()." - + " result=" + result, e); + final ICharSequenceResultCallback callback = + (ICharSequenceResultCallback) msg.obj; + final InputConnection ic = getInputConnection(); + final CharSequence result; + if (ic == null || !isActive()) { + Log.w(TAG, "getSelectedText on inactive InputConnection"); + result = null; + } else { + result = ic.getSelectedText(msg.arg1); + } + try { + callback.onResult(result); + } catch (RemoteException e) { + Log.w(TAG, "Failed to return the result to getSelectedText()." + + " result=" + result, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } return; } case DO_GET_SURROUNDING_TEXT: { final SomeArgs args = (SomeArgs) msg.obj; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSurroundingText"); try { int beforeLength = (int) args.arg1; int afterLength = (int) args.arg2; @@ -335,30 +361,37 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { + " result=" + result, e); } } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); args.recycle(); } return; } case DO_GET_CURSOR_CAPS_MODE: { - final IIntResultCallback callback = (IIntResultCallback) msg.obj; - final InputConnection ic = getInputConnection(); - final int result; - if (ic == null || !isActive()) { - Log.w(TAG, "getCursorCapsMode on inactive InputConnection"); - result = 0; - } else { - result = ic.getCursorCapsMode(msg.arg1); - } + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getCursorCapsMode"); try { - callback.onResult(result); - } catch (RemoteException e) { - Log.w(TAG, "Failed to return the result to getCursorCapsMode()." + final IIntResultCallback callback = (IIntResultCallback) msg.obj; + final InputConnection ic = getInputConnection(); + final int result; + if (ic == null || !isActive()) { + Log.w(TAG, "getCursorCapsMode on inactive InputConnection"); + result = 0; + } else { + result = ic.getCursorCapsMode(msg.arg1); + } + try { + callback.onResult(result); + } catch (RemoteException e) { + Log.w(TAG, "Failed to return the result to getCursorCapsMode()." + " result=" + result, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } return; } case DO_GET_EXTRACTED_TEXT: { final SomeArgs args = (SomeArgs) msg.obj; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getExtractedText"); try { final ExtractedTextRequest request = (ExtractedTextRequest) args.arg1; final IExtractedTextResultCallback callback = @@ -378,159 +411,237 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { + " result=" + result, e); } } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); args.recycle(); } return; } case DO_COMMIT_TEXT: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "commitText on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitText"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "commitText on inactive InputConnection"); + return; + } + ic.commitText((CharSequence) msg.obj, msg.arg1); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.commitText((CharSequence)msg.obj, msg.arg1); return; } case DO_SET_SELECTION: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "setSelection on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setSelection"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "setSelection on inactive InputConnection"); + return; + } + ic.setSelection(msg.arg1, msg.arg2); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.setSelection(msg.arg1, msg.arg2); return; } case DO_PERFORM_EDITOR_ACTION: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "performEditorAction on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performEditorAction"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "performEditorAction on inactive InputConnection"); + return; + } + ic.performEditorAction(msg.arg1); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.performEditorAction(msg.arg1); return; } case DO_PERFORM_CONTEXT_MENU_ACTION: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "performContextMenuAction on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performContextMenuAction"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "performContextMenuAction on inactive InputConnection"); + return; + } + ic.performContextMenuAction(msg.arg1); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.performContextMenuAction(msg.arg1); return; } case DO_COMMIT_COMPLETION: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "commitCompletion on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitCompletion"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "commitCompletion on inactive InputConnection"); + return; + } + ic.commitCompletion((CompletionInfo) msg.obj); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.commitCompletion((CompletionInfo)msg.obj); return; } case DO_COMMIT_CORRECTION: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "commitCorrection on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitCorrection"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "commitCorrection on inactive InputConnection"); + return; + } + ic.commitCorrection((CorrectionInfo) msg.obj); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.commitCorrection((CorrectionInfo)msg.obj); return; } case DO_SET_COMPOSING_TEXT: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "setComposingText on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setComposingText"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "setComposingText on inactive InputConnection"); + return; + } + ic.setComposingText((CharSequence) msg.obj, msg.arg1); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.setComposingText((CharSequence)msg.obj, msg.arg1); return; } case DO_SET_COMPOSING_REGION: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "setComposingRegion on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setComposingRegion"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "setComposingRegion on inactive InputConnection"); + return; + } + ic.setComposingRegion(msg.arg1, msg.arg2); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.setComposingRegion(msg.arg1, msg.arg2); return; } case DO_FINISH_COMPOSING_TEXT: { - if (isFinished()) { - // In this case, #finishComposingText() is guaranteed to be called already. - // There should be no negative impact if we ignore this call silently. - if (DEBUG) { - Log.w(TAG, "Bug 35301295: Redundant finishComposingText."); + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#finishComposingText"); + try { + if (isFinished()) { + // In this case, #finishComposingText() is guaranteed to be called already. + // There should be no negative impact if we ignore this call silently. + if (DEBUG) { + Log.w(TAG, "Bug 35301295: Redundant finishComposingText."); + } + return; } - return; - } - InputConnection ic = getInputConnection(); - // Note we do NOT check isActive() here, because this is safe - // for an IME to call at any time, and we need to allow it - // through to clean up our state after the IME has switched to - // another client. - if (ic == null) { - Log.w(TAG, "finishComposingText on inactive InputConnection"); - return; + InputConnection ic = getInputConnection(); + // Note we do NOT check isActive() here, because this is safe + // for an IME to call at any time, and we need to allow it + // through to clean up our state after the IME has switched to + // another client. + if (ic == null) { + Log.w(TAG, "finishComposingText on inactive InputConnection"); + return; + } + ic.finishComposingText(); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.finishComposingText(); return; } case DO_SEND_KEY_EVENT: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "sendKeyEvent on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#sendKeyEvent"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "sendKeyEvent on inactive InputConnection"); + return; + } + ic.sendKeyEvent((KeyEvent) msg.obj); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.sendKeyEvent((KeyEvent)msg.obj); return; } case DO_CLEAR_META_KEY_STATES: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "clearMetaKeyStates on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#clearMetaKeyStates"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "clearMetaKeyStates on inactive InputConnection"); + return; + } + ic.clearMetaKeyStates(msg.arg1); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.clearMetaKeyStates(msg.arg1); return; } case DO_DELETE_SURROUNDING_TEXT: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "deleteSurroundingText on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#deleteSurroundingText"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "deleteSurroundingText on inactive InputConnection"); + return; + } + ic.deleteSurroundingText(msg.arg1, msg.arg2); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.deleteSurroundingText(msg.arg1, msg.arg2); return; } case DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, + "InputConnection#deleteSurroundingTextInCodePoints"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection"); + return; + } + ic.deleteSurroundingTextInCodePoints(msg.arg1, msg.arg2); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.deleteSurroundingTextInCodePoints(msg.arg1, msg.arg2); return; } case DO_BEGIN_BATCH_EDIT: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "beginBatchEdit on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#beginBatchEdit"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "beginBatchEdit on inactive InputConnection"); + return; + } + ic.beginBatchEdit(); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.beginBatchEdit(); return; } case DO_END_BATCH_EDIT: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "endBatchEdit on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#endBatchEdit"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "endBatchEdit on inactive InputConnection"); + return; + } + ic.endBatchEdit(); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.endBatchEdit(); return; } case DO_PERFORM_PRIVATE_COMMAND: { final SomeArgs args = (SomeArgs) msg.obj; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performPrivateCommand"); try { final String action = (String) args.arg1; final Bundle data = (Bundle) args.arg2; @@ -541,25 +652,31 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } ic.performPrivateCommand(action, data); } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); args.recycle(); } return; } case DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO: { - final IIntResultCallback callback = (IIntResultCallback) msg.obj; - final InputConnection ic = getInputConnection(); - final boolean result; - if (ic == null || !isActive()) { - Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection"); - result = false; - } else { - result = ic.requestCursorUpdates(msg.arg1); - } + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#requestCursorUpdates"); try { - callback.onResult(result ? 1 : 0); - } catch (RemoteException e) { - Log.w(TAG, "Failed to return the result to requestCursorUpdates()." - + " result=" + result, e); + final IIntResultCallback callback = (IIntResultCallback) msg.obj; + final InputConnection ic = getInputConnection(); + final boolean result; + if (ic == null || !isActive()) { + Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection"); + result = false; + } else { + result = ic.requestCursorUpdates(msg.arg1); + } + try { + callback.onResult(result ? 1 : 0); + } catch (RemoteException e) { + Log.w(TAG, "Failed to return the result to requestCursorUpdates()." + + " result=" + result, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } return; } @@ -571,6 +688,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { if (isFinished()) { return; } + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#closeConnection"); try { InputConnection ic = getInputConnection(); // Note we do NOT check isActive() here, because this is safe @@ -590,12 +708,14 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { mInputConnection = null; mFinished = true; } + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } return; } case DO_COMMIT_CONTENT: { final int flags = msg.arg1; SomeArgs args = (SomeArgs) msg.obj; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitContent"); try { final IIntResultCallback callback = (IIntResultCallback) args.arg3; final InputConnection ic = getInputConnection(); @@ -620,6 +740,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { + " result=" + result, e); } } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); args.recycle(); } return; diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 33abbe82c109..e78ed4e211a7 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -24,6 +24,7 @@ import android.view.inputmethod.EditorInfo; import com.android.internal.view.InputBindResult; import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethodClient; +import com.android.internal.inputmethod.IInputBindResultResultCallback; /** * Public interface to the global input method manager, used by all client @@ -48,14 +49,15 @@ interface IInputMethodManager { // If windowToken is null, this just does startInput(). Otherwise this reports that a window // has gained focus, and if 'attribute' is non-null then also does startInput. // @NonNull - InputBindResult startInputOrWindowGainedFocus( + void startInputOrWindowGainedFocus( /* @StartInputReason */ int startInputReason, in IInputMethodClient client, in IBinder windowToken, /* @StartInputFlags */ int startInputFlags, /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode, int windowFlags, in EditorInfo attribute, IInputContext inputContext, /* @InputConnectionInspector.MissingMethodFlags */ int missingMethodFlags, - int unverifiedTargetSdkVersion); + int unverifiedTargetSdkVersion, + in IInputBindResultResultCallback inputBindResult); void showInputMethodPickerFromClient(in IInputMethodClient client, int auxiliarySubtypeMode); diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index ff3543c837eb..d30d662806d4 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -16,21 +16,36 @@ package com.android.internal.widget; +import static android.view.inputmethod.InputConnectionProto.CURSOR_CAPS_MODE; +import static android.view.inputmethod.InputConnectionProto.EDITABLE_TEXT; +import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT; +import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT_END; +import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT_START; + import android.compat.annotation.UnsupportedAppUsage; import android.os.Bundle; import android.text.Editable; +import android.text.Selection; import android.text.method.KeyListener; import android.util.Log; +import android.util.proto.ProtoOutputStream; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; +import android.view.inputmethod.DumpableInputConnection; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; import android.widget.TextView; -public class EditableInputConnection extends BaseInputConnection { +/** + * Base class for an editable InputConnection instance. This is created by {@link TextView} or + * {@link EditText}. + */ +public class EditableInputConnection extends BaseInputConnection + implements DumpableInputConnection { private static final boolean DEBUG = false; + private static final boolean DUMP_TEXT = false; private static final String TAG = "EditableInputConnection"; private final TextView mTextView; @@ -222,4 +237,28 @@ public class EditableInputConnection extends BaseInputConnection { } return true; } + + @Override + public void dumpDebug(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + CharSequence editableText = mTextView.getText(); + CharSequence selectedText = getSelectedText(0 /* flags */); + if (DUMP_TEXT) { + if (editableText != null) { + proto.write(EDITABLE_TEXT, editableText.toString()); + } + if (selectedText != null) { + proto.write(SELECTED_TEXT, selectedText.toString()); + } + } + final Editable content = getEditable(); + if (content != null) { + int start = Selection.getSelectionStart(content); + int end = Selection.getSelectionEnd(content); + proto.write(SELECTED_TEXT_START, start); + proto.write(SELECTED_TEXT_END, end); + } + proto.write(CURSOR_CAPS_MODE, getCursorCapsMode(0)); + proto.end(token); + } } diff --git a/core/java/com/android/internal/widget/LocalImageResolver.java b/core/java/com/android/internal/widget/LocalImageResolver.java index b4e108faee2d..3f205c785258 100644 --- a/core/java/com/android/internal/widget/LocalImageResolver.java +++ b/core/java/com/android/internal/widget/LocalImageResolver.java @@ -16,69 +16,61 @@ package com.android.internal.widget; -import android.annotation.Nullable; import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.drawable.BitmapDrawable; +import android.graphics.ImageDecoder; +import android.graphics.drawable.AnimatedImageDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; -import android.util.Log; +import android.util.Size; import java.io.IOException; -import java.io.InputStream; -/** - * A class to extract Bitmaps from a MessagingStyle message. - */ +/** A class to extract Drawables from a MessagingStyle/ConversationStyle message. */ public class LocalImageResolver { private static final String TAG = LocalImageResolver.class.getSimpleName(); private static final int MAX_SAFE_ICON_SIZE_PX = 480; - @Nullable public static Drawable resolveImage(Uri uri, Context context) throws IOException { - BitmapFactory.Options onlyBoundsOptions = getBoundsOptionsForImage(uri, context); - if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1)) { - return null; - } - - int originalSize = - (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth) - ? onlyBoundsOptions.outHeight - : onlyBoundsOptions.outWidth; - - double ratio = (originalSize > MAX_SAFE_ICON_SIZE_PX) - ? (originalSize / MAX_SAFE_ICON_SIZE_PX) - : 1.0; - - BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); - bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio); - InputStream input = context.getContentResolver().openInputStream(uri); - Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions); - input.close(); - return new BitmapDrawable(context.getResources(), bitmap); + final ImageDecoder.Source source = + ImageDecoder.createSource(context.getContentResolver(), uri); + final Drawable drawable = + ImageDecoder.decodeDrawable(source, LocalImageResolver::onHeaderDecoded); + return drawable; } - private static BitmapFactory.Options getBoundsOptionsForImage(Uri uri, Context context) + public static Drawable resolveImage(Uri uri, Context context, int maxWidth, int maxHeight) throws IOException { - BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options(); - try (InputStream input = context.getContentResolver().openInputStream(uri)) { - if (input == null) { - throw new IllegalArgumentException(); + final ImageDecoder.Source source = + ImageDecoder.createSource(context.getContentResolver(), uri); + return ImageDecoder.decodeDrawable(source, (decoder, info, unused) -> { + final Size size = info.getSize(); + if (size.getWidth() > size.getHeight()) { + if (size.getWidth() > maxWidth) { + final int targetHeight = size.getHeight() * maxWidth / size.getWidth(); + decoder.setTargetSize(maxWidth, targetHeight); + } + } else { + if (size.getHeight() > maxHeight) { + final int targetWidth = size.getWidth() * maxHeight / size.getHeight(); + decoder.setTargetSize(targetWidth, maxHeight); + } } - onlyBoundsOptions.inJustDecodeBounds = true; - BitmapFactory.decodeStream(input, null, onlyBoundsOptions); - } catch (IllegalArgumentException iae) { - onlyBoundsOptions.outWidth = -1; - onlyBoundsOptions.outHeight = -1; - Log.e(TAG, "error loading image", iae); - } - return onlyBoundsOptions; + }); } private static int getPowerOfTwoForSampleRatio(double ratio) { - int k = Integer.highestOneBit((int) Math.floor(ratio)); + final int k = Integer.highestOneBit((int) Math.floor(ratio)); return Math.max(1, k); } + + private static void onHeaderDecoded(ImageDecoder decoder, ImageDecoder.ImageInfo info, + ImageDecoder.Source source) { + final Size size = info.getSize(); + final int originalSize = Math.max(size.getHeight(), size.getWidth()); + final double ratio = (originalSize > MAX_SAFE_ICON_SIZE_PX) + ? originalSize * 1f / MAX_SAFE_ICON_SIZE_PX + : 1.0; + decoder.setTargetSampleSize(getPowerOfTwoForSampleRatio(ratio)); + } } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index b562ef838633..9712b4e794c5 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -311,6 +311,15 @@ public class LockPatternUtils { return getDevicePolicyManager().getPasswordMinimumMetrics(userId); } + /** + * Returns the effective complexity for the user. + * @param userId The user to return the complexity for. + * @return complexity level for the user. + */ + public @DevicePolicyManager.PasswordComplexity int getRequestedPasswordComplexity(int userId) { + return getDevicePolicyManager().getAggregatedPasswordComplexityForUser(userId); + } + public int getRequestedPasswordQuality(int userId) { return getDevicePolicyManager().getPasswordQuality(null, userId); } diff --git a/core/jni/android_media_MicrophoneInfo.cpp b/core/jni/android_media_MicrophoneInfo.cpp index 5bd808b67c57..b70190fc5ec3 100644 --- a/core/jni/android_media_MicrophoneInfo.cpp +++ b/core/jni/android_media_MicrophoneInfo.cpp @@ -56,8 +56,8 @@ jint convertMicrophoneInfoFromNative(JNIEnv *env, jobject *jMicrophoneInfo, jobject jFrequencyResponses = NULL; jobject jChannelMappings = NULL; - jDeviceId = env->NewStringUTF(String8(microphoneInfo->getDeviceId()).string()); - jAddress = env->NewStringUTF(String8(microphoneInfo->getAddress()).string()); + jDeviceId = env->NewStringUTF(microphoneInfo->getDeviceId().c_str()); + jAddress = env->NewStringUTF(microphoneInfo->getAddress().c_str()); if (microphoneInfo->getGeometricLocation().size() != 3 || microphoneInfo->getOrientation().size() != 3) { jStatus = nativeToJavaStatus(BAD_VALUE); diff --git a/core/proto/OWNERS b/core/proto/OWNERS index 5eeeb048e6d6..748b4b4f5743 100644 --- a/core/proto/OWNERS +++ b/core/proto/OWNERS @@ -26,17 +26,7 @@ hyunyoungs@google.com # Graphics stats jreck@google.com -# Temporary Block to assist in migration -# Bug: 143080132 -per-file *enums.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com -per-file *media_output_enum.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com -per-file *networkcapabilities.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com -per-file *data_stall_event.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com -per-file *procstats_enum.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com -per-file *usb.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com -per-file *network_stack.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com -per-file *tethering.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com -per-file *dns_resolver.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com -per-file *device_policy.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com -per-file *launcher.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com -per-file *mediametrics.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +# Accessibility +pweaver@google.com +hongmingjin@google.com +cbrower@google.com diff --git a/core/proto/android/view/inputmethod/inputconnection.proto b/core/proto/android/view/inputmethod/inputconnection.proto new file mode 100644 index 000000000000..ad9a95aa95e6 --- /dev/null +++ b/core/proto/android/view/inputmethod/inputconnection.proto @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +import "frameworks/base/core/proto/android/privacy.proto"; + +package android.view.inputmethod; + +option java_multiple_files = true; + +/** + * Represents a {@link android.view.inputmethod.InputConnection} object. + */ +message InputConnectionProto { + optional string editable_text = 1 [(.android.privacy).dest = DEST_LOCAL]; + optional string selected_text = 2 [(.android.privacy).dest = DEST_LOCAL]; + optional int32 selected_text_start = 3; + optional int32 selected_text_end = 4; + optional int32 cursor_caps_mode = 5; +}
\ No newline at end of file diff --git a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto index 5c0f341cb9e4..c1dce6f2d093 100644 --- a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto +++ b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto @@ -24,6 +24,7 @@ import "frameworks/base/core/proto/android/view/viewrootimpl.proto"; import "frameworks/base/core/proto/android/view/insetscontroller.proto"; import "frameworks/base/core/proto/android/view/imeinsetssourceconsumer.proto"; import "frameworks/base/core/proto/android/view/inputmethod/editorinfo.proto"; +import "frameworks/base/core/proto/android/view/inputmethod/inputconnection.proto"; import "frameworks/base/core/proto/android/view/imefocuscontroller.proto"; import "frameworks/base/core/proto/android/server/inputmethod/inputmethodmanagerservice.proto"; @@ -70,6 +71,7 @@ message InputMethodClientsTraceProto { optional ImeInsetsSourceConsumerProto ime_insets_source_consumer = 5; optional EditorInfoProto editor_info = 6; optional ImeFocusControllerProto ime_focus_controller = 7; + optional InputConnectionProto input_connection = 8; } } diff --git a/core/proto/android/view/windowlayoutparams.proto b/core/proto/android/view/windowlayoutparams.proto index 4bb56f8acfa4..062485d304c3 100644 --- a/core/proto/android/view/windowlayoutparams.proto +++ b/core/proto/android/view/windowlayoutparams.proto @@ -35,7 +35,7 @@ message WindowLayoutParamsProto { optional int32 height = 5; optional float horizontal_margin = 6; optional float vertical_margin = 7; - optional int32 gravity = 8; + optional int32 gravity = 8 [(.android.typedef) = "android.view.Gravity.GravityFlags"]; optional int32 soft_input_mode = 9 [(.android.typedef) = "android.view.WindowManager.LayoutParams.SoftInputModeFlags"]; optional .android.graphics.PixelFormatProto.Format format = 10; optional int32 window_animations = 11; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 11a821468aa1..5a6905699535 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -120,6 +120,12 @@ <protected-broadcast android:name="android.app.action.EXIT_DESK_MODE" /> <protected-broadcast android:name="android.app.action.NEXT_ALARM_CLOCK_CHANGED" /> + <protected-broadcast android:name="android.app.action.USER_ADDED" /> + <protected-broadcast android:name="android.app.action.USER_REMOVED" /> + <protected-broadcast android:name="android.app.action.USER_STARTED" /> + <protected-broadcast android:name="android.app.action.USER_STOPPED" /> + <protected-broadcast android:name="android.app.action.USER_SWITCHED" /> + <protected-broadcast android:name="android.app.action.BUGREPORT_SHARING_DECLINED" /> <protected-broadcast android:name="android.app.action.BUGREPORT_FAILED" /> <protected-broadcast android:name="android.app.action.BUGREPORT_SHARE" /> @@ -3104,6 +3110,12 @@ <permission android:name="android.permission.DUMP" android:protectionLevel="signature|privileged|development" /> + <!-- Allows an application to start tracing for InputMethod and WindowManager. + <p>Not for use by third-party applications. + @hide --> + <permission android:name="android.permission.CONTROL_UI_TRACING" + android:protectionLevel="signature|privileged|development" /> + <!-- Allows an application to read the low-level system log files. <p>Not for use by third-party applications, because Log entries can contain the user's private information. --> @@ -4190,6 +4202,14 @@ <permission android:name="android.permission.FACTORY_TEST" android:protectionLevel="signature" /> + <!-- @hide @TestApi Allows an application to broadcast the intent {@link + android.content.Intent#ACTION_CLOSE_SYSTEM_DIALOGS}. + <p>Not for use by third-party applications. + --> + <permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" + android:protectionLevel="signature|recents" /> + <uses-permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" /> + <!-- Allows an application to broadcast a notification that an application package has been removed. <p>Not for use by third-party applications. diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml index b0ee12a520d9..88998f2167a8 100644 --- a/core/res/res/layout/notification_template_header.xml +++ b/core/res/res/layout/notification_template_header.xml @@ -26,6 +26,18 @@ android:theme="@style/Theme.DeviceDefault.Notification" > + <ImageView + android:id="@+id/left_icon" + android:layout_width="@dimen/notification_left_icon_size" + android:layout_height="@dimen/notification_left_icon_size" + android:layout_gravity="center_vertical|start" + android:layout_marginStart="@dimen/notification_left_icon_start" + android:background="@drawable/notification_large_icon_outline" + android:importantForAccessibility="no" + android:scaleType="centerCrop" + android:visibility="gone" + /> + <com.android.internal.widget.CachingIconView android:id="@+id/icon" android:layout_width="@dimen/notification_icon_circle_size" diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml index 69d4a12f4d69..000638475a10 100644 --- a/core/res/res/layout/notification_template_material_base.xml +++ b/core/res/res/layout/notification_template_material_base.xml @@ -23,6 +23,18 @@ android:tag="base" > + <ImageView + android:id="@+id/left_icon" + android:layout_width="@dimen/notification_left_icon_size" + android:layout_height="@dimen/notification_left_icon_size" + android:layout_gravity="center_vertical|start" + android:layout_marginStart="@dimen/notification_left_icon_start" + android:background="@drawable/notification_large_icon_outline" + android:importantForAccessibility="no" + android:scaleType="centerCrop" + android:visibility="gone" + /> + <com.android.internal.widget.CachingIconView android:id="@+id/icon" android:layout_width="@dimen/notification_icon_circle_size" @@ -34,7 +46,7 @@ /> <LinearLayout - android:id="@+id/notification_standard_view_column" + android:id="@+id/notification_headerless_view_column" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index c9f140df68cb..f8cbfebabdcf 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4239,6 +4239,35 @@ If non-positive, then the refresh rate is unchanged even if thresholds are configured. --> <integer name="config_defaultRefreshRateInZone">0</integer> + <!-- The display uses different gamma curves for different refresh rates. It's hard for panel + vendor to tune the curves to have exact same brightness for different refresh rate. So + flicker could be observed at switch time. The issue can be observed on the screen with + even full white content at the high brightness. To prevent flickering, we support fixed + refresh rates if the display and ambient brightness are equal to or above the provided + thresholds. You can define multiple threshold levels as higher brightness environments + may have lower display brightness requirements for the flickering is visible. And the + high brightness environment could have higher threshold. + For example, fixed refresh rate if + display brightness >= disp0 && ambient brightness >= amb0 + || display brightness >= disp1 && ambient brightness >= amb1 --> + <integer-array translatable="false" name="config_highDisplayBrightnessThresholdsOfFixedRefreshRate"> + <!-- + <item>disp0</item> + <item>disp1</item> + --> + </integer-array> + + <integer-array translatable="false" name="config_highAmbientBrightnessThresholdsOfFixedRefreshRate"> + <!-- + <item>amb0</item> + <item>amb1</item> + --> + </integer-array> + + <!-- Default refresh rate in the high zone defined by brightness and ambient thresholds. + If non-positive, then the refresh rate is unchanged even if thresholds are configured. --> + <integer name="config_fixedRefreshRateInHighZone">0</integer> + <!-- The type of the light sensor to be used by the display framework for things like auto-brightness. If unset, then it just gets the default sensor of type TYPE_LIGHT. --> <string name="config_displayLightSensorType" translatable="false" /> @@ -4522,4 +4551,7 @@ <!-- If true, hide the display cutout with display area --> <bool name="config_hideDisplayCutoutWithDisplayArea">false</bool> + + <!-- Indicates that default fitness tracker app needs to request sensor and location permissions. --> + <bool name="config_trackerAppNeedsPermissions">false</bool> </resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 19591f6a666f..4bcabff109ea 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -714,6 +714,10 @@ <dimen name="notification_right_icon_headerless_margin">12dp</dimen> <!-- The top margin of the right icon in the "big" notification states --> <dimen name="notification_right_icon_big_margin_top">16dp</dimen> + <!-- The size of the left icon --> + <dimen name="notification_left_icon_size">@dimen/notification_icon_circle_size</dimen> + <!-- The left padding of the left icon --> + <dimen name="notification_left_icon_start">@dimen/notification_icon_circle_start</dimen> <!-- The alpha of a disabled notification button --> <item type="dimen" format="float" name="notification_action_disabled_alpha">0.5</item> diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index f77c6f99c063..a12d2a951460 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -194,6 +194,12 @@ <!-- A tag used to save the index where the custom view is stored --> <item type="id" name="notification_custom_view_index_tag" /> + <!-- A tag used to store the margin end for this view when the right icon is visible --> + <item type="id" name="tag_margin_end_when_icon_gone" /> + + <!-- A tag used to store the margin end for this view when the right icon is gone --> + <item type="id" name="tag_margin_end_when_icon_visible" /> + <!-- Marks the "copy to clipboard" button in the ChooserActivity --> <item type="id" name="chooser_copy_button" /> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 40aae9e4e085..84556d41a07e 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -218,7 +218,7 @@ <java-symbol type="id" name="inbox_text6" /> <java-symbol type="id" name="status_bar_latest_event_content" /> <java-symbol type="id" name="notification_main_column" /> - <java-symbol type="id" name="notification_standard_view_column" /> + <java-symbol type="id" name="notification_headerless_view_column" /> <java-symbol type="id" name="sms_short_code_confirm_message" /> <java-symbol type="id" name="sms_short_code_detail_layout" /> <java-symbol type="id" name="sms_short_code_detail_message" /> @@ -3070,6 +3070,8 @@ <java-symbol type="dimen" name="notification_media_image_margin_end" /> <java-symbol type="id" name="notification_action_list_margin_target" /> <java-symbol type="dimen" name="notification_action_disabled_alpha" /> + <java-symbol type="id" name="tag_margin_end_when_icon_visible" /> + <java-symbol type="id" name="tag_margin_end_when_icon_gone" /> <!-- Override Wake Key Behavior When Screen is Off --> <java-symbol type="bool" name="config_wakeOnDpadKeyPress" /> @@ -3811,6 +3813,11 @@ <java-symbol type="array" name="config_brightnessThresholdsOfPeakRefreshRate" /> <java-symbol type="array" name="config_ambientThresholdsOfPeakRefreshRate" /> + <!-- For fixed refresh rate displays in high brightness--> + <java-symbol type="integer" name="config_fixedRefreshRateInHighZone" /> + <java-symbol type="array" name="config_highDisplayBrightnessThresholdsOfFixedRefreshRate" /> + <java-symbol type="array" name="config_highAmbientBrightnessThresholdsOfFixedRefreshRate" /> + <!-- For Auto-Brightness --> <java-symbol type="string" name="config_displayLightSensorType" /> @@ -4103,4 +4110,7 @@ <java-symbol type="string" name="window_magnification_prompt_content" /> <java-symbol type="string" name="turn_on_magnification_settings_action" /> <java-symbol type="string" name="dismiss_action" /> + + <java-symbol type="bool" name="config_trackerAppNeedsPermissions"/> + </resources> diff --git a/core/res/res/xml/config_user_types.xml b/core/res/res/xml/config_user_types.xml index 5fd8a95af64d..71dfc551ccc1 100644 --- a/core/res/res/xml/config_user_types.xml +++ b/core/res/res/xml/config_user_types.xml @@ -40,7 +40,7 @@ The following example modifies two AOSP user types (the FULL user android.os.use and the PROFILE user android.os.usertype.profile.MANAGED) and creates a new PROFILE user type (com.example.profilename): -<user-types> +<user-types version="0"> <full-type name="android.os.usertype.full.SECONDARY" > <default-restrictions no_sms="true" /> </full-type> @@ -65,6 +65,11 @@ and the PROFILE user android.os.usertype.profile.MANAGED) and creates a new PROF <profile-type name="com.example.profilename" max-allowed-per-parent="2" /> + + <change-user-type + from="android.os.usertype.profile.MANAGED" + to="com.example.profilename" + whenVersionLeq="1" /> </user-types> Mandatory attributes: @@ -93,6 +98,10 @@ If this file is updated, the properties of any pre-existing user types will be u Note, however, that default-restrictions refers to the restrictions applied at the time of user creation; therefore, the active restrictions of any pre-existing users will not be updated. +The 'change-user-type' tag should be used in conjunction with the 'version' property of +'user-types'. It defines a type change for all pre-existing users of 'from' type to the new 'to' +type, if the former 'user-type's version of device is less than or equal to 'whenVersionLeq'. + --> <user-types> </user-types> diff --git a/core/tests/coretests/src/android/widget/NumberPickerTest.java b/core/tests/coretests/src/android/widget/NumberPickerTest.java new file mode 100644 index 000000000000..cab7c89f4ca1 --- /dev/null +++ b/core/tests/coretests/src/android/widget/NumberPickerTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.widget; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.platform.test.annotations.Presubmit; +import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeProvider; + +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +@Presubmit +public class NumberPickerTest { + @Test + public void testAccessibilityFocusedProperty() { + final int virtualViewIdIncrement = 1; + final int VirtualViewIdInput = 2; + final int VirtualViewIdDecrement = 3; + final NumberPicker np = + new NumberPicker(InstrumentationRegistry.getInstrumentation().getContext()); + final AccessibilityNodeProvider provider = np.getAccessibilityNodeProvider(); + + AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo(View.NO_ID); + assertFalse(info.isAccessibilityFocused()); + info.recycle(); + provider.performAction(View.NO_ID, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); + info = provider.createAccessibilityNodeInfo(View.NO_ID); + assertTrue(info.isAccessibilityFocused()); + info.recycle(); + + info = provider.createAccessibilityNodeInfo(virtualViewIdIncrement); + assertFalse(info.isAccessibilityFocused()); + info.recycle(); + provider.performAction( + virtualViewIdIncrement, + AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, + null + ); + info = provider.createAccessibilityNodeInfo(virtualViewIdIncrement); + assertTrue(info.isAccessibilityFocused()); + info.recycle(); + + info = provider.createAccessibilityNodeInfo(VirtualViewIdInput); + assertFalse(info.isAccessibilityFocused()); + info.recycle(); + provider.performAction( + VirtualViewIdInput, + AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, + null + ); + info = provider.createAccessibilityNodeInfo(VirtualViewIdInput); + assertTrue(info.isAccessibilityFocused()); + info.recycle(); + + info = provider.createAccessibilityNodeInfo(VirtualViewIdDecrement); + assertFalse(info.isAccessibilityFocused()); + info.recycle(); + provider.performAction( + VirtualViewIdDecrement, + AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, + null + ); + info = provider.createAccessibilityNodeInfo(VirtualViewIdDecrement); + assertTrue(info.isAccessibilityFocused()); + info.recycle(); + } +} diff --git a/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java index 7b9283b41ff0..9978648ee32e 100644 --- a/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java +++ b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java @@ -16,11 +16,11 @@ package android.widget; -import static android.view.OnReceiveContentListener.Payload.SOURCE_AUTOFILL; -import static android.view.OnReceiveContentListener.Payload.SOURCE_CLIPBOARD; -import static android.view.OnReceiveContentListener.Payload.SOURCE_DRAG_AND_DROP; -import static android.view.OnReceiveContentListener.Payload.SOURCE_INPUT_METHOD; -import static android.view.OnReceiveContentListener.Payload.SOURCE_PROCESS_TEXT; +import static android.view.ContentInfo.SOURCE_AUTOFILL; +import static android.view.ContentInfo.SOURCE_CLIPBOARD; +import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP; +import static android.view.ContentInfo.SOURCE_INPUT_METHOD; +import static android.view.ContentInfo.SOURCE_PROCESS_TEXT; import static android.widget.espresso.TextViewActions.clickOnTextAtIndex; import static androidx.test.espresso.Espresso.onView; @@ -41,7 +41,7 @@ import android.content.ClipData; import android.content.ClipDescription; import android.net.Uri; import android.os.Bundle; -import android.view.OnReceiveContentListener; +import android.view.ContentInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnectionWrapper; import android.view.inputmethod.InputContentInfo; @@ -133,8 +133,8 @@ public class TextViewOnReceiveContentTest { // InputConnection.commitContent. ClipDescription description = new ClipDescription("", new String[] {"image/gif"}); ClipData clip = new ClipData(description, new ClipData.Item(SAMPLE_CONTENT_URI)); - OnReceiveContentListener.Payload payload = - new OnReceiveContentListener.Payload.Builder(clip, SOURCE_AUTOFILL).build(); + ContentInfo payload = + new ContentInfo.Builder(clip, SOURCE_AUTOFILL).build(); mDefaultReceiver.onReceiveContent(mEditText, payload); verify(ic.mMock, times(1)) .commitContent(any(InputContentInfo.class), eq(0), eq(null)); @@ -155,8 +155,8 @@ public class TextViewOnReceiveContentTest { // Invoke the listener and assert that the InputConnection is not invoked. ClipDescription description = new ClipDescription("", new String[] {"image/gif"}); ClipData clip = new ClipData(description, new ClipData.Item(SAMPLE_CONTENT_URI)); - OnReceiveContentListener.Payload payload = - new OnReceiveContentListener.Payload.Builder(clip, SOURCE_AUTOFILL).build(); + ContentInfo payload = + new ContentInfo.Builder(clip, SOURCE_AUTOFILL).build(); mDefaultReceiver.onReceiveContent(mEditText, payload); verifyZeroInteractions(ic.mMock); } @@ -176,20 +176,20 @@ public class TextViewOnReceiveContentTest { // trigger calls to InputConnection.commitContent. ClipDescription description = new ClipDescription("", new String[] {"image/gif"}); ClipData clip = new ClipData(description, new ClipData.Item(SAMPLE_CONTENT_URI)); - OnReceiveContentListener.Payload payload = - new OnReceiveContentListener.Payload.Builder(clip, SOURCE_CLIPBOARD).build(); + ContentInfo payload = + new ContentInfo.Builder(clip, SOURCE_CLIPBOARD).build(); mDefaultReceiver.onReceiveContent(mEditText, payload); verifyZeroInteractions(ic.mMock); - payload = new OnReceiveContentListener.Payload.Builder(clip, SOURCE_INPUT_METHOD).build(); + payload = new ContentInfo.Builder(clip, SOURCE_INPUT_METHOD).build(); mDefaultReceiver.onReceiveContent(mEditText, payload); verifyZeroInteractions(ic.mMock); - payload = new OnReceiveContentListener.Payload.Builder(clip, SOURCE_DRAG_AND_DROP).build(); + payload = new ContentInfo.Builder(clip, SOURCE_DRAG_AND_DROP).build(); mDefaultReceiver.onReceiveContent(mEditText, payload); verifyZeroInteractions(ic.mMock); - payload = new OnReceiveContentListener.Payload.Builder(clip, SOURCE_PROCESS_TEXT).build(); + payload = new ContentInfo.Builder(clip, SOURCE_PROCESS_TEXT).build(); mDefaultReceiver.onReceiveContent(mEditText, payload); verifyZeroInteractions(ic.mMock); } diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 8406fdf99360..1fb63f04ccf5 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -322,6 +322,7 @@ applications that come with the platform <permission name="android.permission.DELETE_CACHE_FILES"/> <permission name="android.permission.DELETE_PACKAGES"/> <permission name="android.permission.DUMP"/> + <permission name="android.permission.CONTROL_UI_TRACING"/> <permission name="android.permission.ACTIVITY_EMBEDDING"/> <permission name="android.permission.FORCE_STOP_PACKAGES"/> <permission name="android.permission.GET_APP_OPS_STATS"/> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index a52eca7e0d73..6bcab8a34e2c 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -2749,6 +2749,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "1105210816": { + "message": "Skipping config check in destroyed state %s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "1112047265": { "message": "finishDrawingWindow: %s mDrawState=%s", "level": "DEBUG", diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl index 97da3cc6f80f..1ae6a631dbcb 100644 --- a/keystore/java/android/security/IKeyChainService.aidl +++ b/keystore/java/android/security/IKeyChainService.aidl @@ -46,6 +46,7 @@ interface IKeyChainService { boolean installKeyPair( in byte[] privateKey, in byte[] userCert, in byte[] certChain, String alias, int uid); boolean removeKeyPair(String alias); + boolean containsKeyPair(String alias); // APIs used by Settings boolean deleteCaCertificate(String alias); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java index 8a547b4477fd..7b3b5dbfa51c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java @@ -18,12 +18,10 @@ package com.android.wm.shell.draganddrop; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.ClipDescription.EXTRA_ACTIVITY_OPTIONS; import static android.content.ClipDescription.EXTRA_PENDING_INTENT; -import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY; import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT; import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK; import static android.content.Intent.EXTRA_PACKAGE_NAME; @@ -78,7 +76,7 @@ public class DragAndDropPolicy { private static final String TAG = DragAndDropPolicy.class.getSimpleName(); private final Context mContext; - private final IActivityTaskManager mIActivityTaskManager; + private final ActivityTaskManager mActivityTaskManager; private final Starter mStarter; private final SplitScreen mSplitScreen; private final ArrayList<DragAndDropPolicy.Target> mTargets = new ArrayList<>(); @@ -86,15 +84,15 @@ public class DragAndDropPolicy { private DragSession mSession; public DragAndDropPolicy(Context context, SplitScreen splitScreen) { - this(context, ActivityTaskManager.getService(), splitScreen, + this(context, ActivityTaskManager.getInstance(), splitScreen, new DefaultStarter(context, splitScreen)); } @VisibleForTesting - DragAndDropPolicy(Context context, IActivityTaskManager activityTaskManager, + DragAndDropPolicy(Context context, ActivityTaskManager activityTaskManager, SplitScreen splitScreen, Starter starter) { mContext = context; - mIActivityTaskManager = activityTaskManager; + mActivityTaskManager = activityTaskManager; mSplitScreen = splitScreen; mStarter = starter; } @@ -103,7 +101,7 @@ public class DragAndDropPolicy { * Starts a new drag session with the given initial drag data. */ void start(DisplayLayout displayLayout, ClipData data) { - mSession = new DragSession(mContext, mIActivityTaskManager, displayLayout, data); + mSession = new DragSession(mContext, mActivityTaskManager, displayLayout, data); // TODO(b/169894807): Also update the session data with task stack changes mSession.update(); } @@ -271,7 +269,7 @@ public class DragAndDropPolicy { */ private static class DragSession { private final Context mContext; - private final IActivityTaskManager mIActivityTaskManager; + private final ActivityTaskManager mActivityTaskManager; private final ClipData mInitialDragData; final DisplayLayout displayLayout; @@ -285,10 +283,10 @@ public class DragAndDropPolicy { boolean dragItemSupportsSplitscreen; boolean isPhone; - DragSession(Context context, IActivityTaskManager activityTaskManager, + DragSession(Context context, ActivityTaskManager activityTaskManager, DisplayLayout dispLayout, ClipData data) { mContext = context; - mIActivityTaskManager = activityTaskManager; + mActivityTaskManager = activityTaskManager; mInitialDragData = data; displayLayout = dispLayout; } @@ -298,19 +296,14 @@ public class DragAndDropPolicy { */ void update() { - try { - List<ActivityManager.RunningTaskInfo> tasks = - mIActivityTaskManager.getFilteredTasks(1, - false /* filterOnlyVisibleRecents */); - if (!tasks.isEmpty()) { - final ActivityManager.RunningTaskInfo task = tasks.get(0); - runningTaskWinMode = task.getWindowingMode(); - runningTaskActType = task.getActivityType(); - runningTaskId = task.taskId; - runningTaskIsResizeable = task.isResizeable; - } - } catch (RemoteException e) { - // Fall through + List<ActivityManager.RunningTaskInfo> tasks = + mActivityTaskManager.getTasks(1, false /* filterOnlyVisibleRecents */); + if (!tasks.isEmpty()) { + final ActivityManager.RunningTaskInfo task = tasks.get(0); + runningTaskWinMode = task.getWindowingMode(); + runningTaskActType = task.getActivityType(); + runningTaskId = task.taskId; + runningTaskIsResizeable = task.isResizeable; } final ActivityInfo info = mInitialDragData.getItemAt(0).getActivityInfo(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java index 090d2270817b..4e62ea6e7233 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java @@ -272,8 +272,9 @@ class HideDisplayCutoutOrganizer extends DisplayAreaOrganizer { @VisibleForTesting void applyBoundsAndOffsets(WindowContainerToken token, SurfaceControl leash, WindowContainerTransaction wct, SurfaceControl.Transaction t) { - wct.setBounds(token, mCurrentDisplayBounds.isEmpty() ? null : mCurrentDisplayBounds); + wct.setBounds(token, mCurrentDisplayBounds); t.setPosition(leash, mOffsetX, mOffsetY); + t.setWindowCrop(leash, mCurrentDisplayBounds.width(), mCurrentDisplayBounds.height()); } @VisibleForTesting diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxTaskListener.java index 061d3f86b669..6e87f131ed95 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxTaskListener.java @@ -107,6 +107,7 @@ public class LetterboxTaskListener implements ShellTaskOrganizer.TaskListener { transaction.setWindowCrop(leash, crop); } + // TODO(b/173440321): Correct presentation of letterboxed activities in One-handed mode. private void resolveTaskPositionAndCrop( ActivityManager.RunningTaskInfo taskInfo, Point positionInParent, @@ -125,15 +126,18 @@ public class LetterboxTaskListener implements ShellTaskOrganizer.TaskListener { final Rect activityBounds = taskInfo.letterboxActivityBounds; Insets insets = getInsets(); + Rect displayBoundsWithInsets = + new Rect(mWindowManager.getMaximumWindowMetrics().getBounds()); + displayBoundsWithInsets.inset(insets); Rect taskBoundsWithInsets = new Rect(taskBounds); - applyInsets(taskBoundsWithInsets, insets, taskInfo.parentBounds); + taskBoundsWithInsets.intersect(displayBoundsWithInsets); Rect activityBoundsWithInsets = new Rect(activityBounds); - applyInsets(activityBoundsWithInsets, insets, taskInfo.parentBounds); + activityBoundsWithInsets.intersect(displayBoundsWithInsets); Rect parentBoundsWithInsets = new Rect(parentBounds); - applyInsets(parentBoundsWithInsets, insets, parentBounds); + parentBoundsWithInsets.intersect(displayBoundsWithInsets); // Crop need to be in the task coordinates. crop.set(activityBoundsWithInsets); @@ -217,10 +221,4 @@ public class LetterboxTaskListener implements ShellTaskOrganizer.TaskListener { | WindowInsets.Type.displayCutout()); } - private void applyInsets(Rect innerBounds, Insets insets, Rect outerBounds) { - Rect outerBoundsWithInsets = new Rect(outerBounds); - outerBoundsWithInsets.inset(insets); - innerBounds.intersect(outerBoundsWithInsets); - } - } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 07af289c4f35..6d6c76139c87 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -506,7 +506,7 @@ public class SplitScreenController implements SplitScreen, // Try fetching the top running task. final List<RunningTaskInfo> runningTasks = - ActivityTaskManager.getService().getTasks(1 /* maxNum */); + ActivityTaskManager.getInstance().getTasks(1 /* maxNum */); if (runningTasks == null || runningTasks.isEmpty()) { return false; } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt index 2fc6944a3a5f..ced99de21a46 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.flicker.apppairs +import android.platform.test.annotations.Presubmit import android.os.SystemClock import android.util.Log import android.view.Surface @@ -43,6 +44,7 @@ import java.io.IOException * Test AppPairs launch. * To run this test: `atest WMShellFlickerTests:AppPairsTest` */ +@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt index 6c4e65818e49..6b44ce6ace0c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.flicker.pip +import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.dsl.FlickerBuilder @@ -36,6 +37,7 @@ import org.junit.runners.Parameterized * Test Pip launch. * To run this test: `atest WMShellFlickerTests:PipKeyboardTest` */ +@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt index a0056dfc0948..c61a0f171714 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.flicker.splitscreen +import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.dsl.FlickerBuilder @@ -38,6 +39,7 @@ import org.junit.runners.Parameterized * Test SplitScreen launch. * To run this test: `atest WMShellFlickerTests:EnterSplitScreenTest` */ +@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt index 32e112dbd598..bf9286980b9a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.flicker.splitscreen +import android.platform.test.annotations.Presubmit import android.util.Rational import android.view.Surface import androidx.test.filters.FlakyTest @@ -40,6 +41,7 @@ import org.junit.runners.Parameterized * Test exit SplitScreen mode. * To run this test: `atest WMShellFlickerTests:ExitSplitScreenTest` */ +@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) 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 fad1f057267a..92d4bee7cfc2 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 @@ -42,7 +42,7 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import android.app.ActivityManager; -import android.app.IActivityTaskManager; +import android.app.ActivityTaskManager; import android.app.PendingIntent; import android.content.ClipData; import android.content.ClipDescription; @@ -69,7 +69,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.util.ArrayList; @@ -88,7 +87,7 @@ public class DragAndDropPolicyTest { private Context mContext; @Mock - private IActivityTaskManager mIActivityTaskManager; + private ActivityTaskManager mActivityTaskManager; @Mock private SplitScreen mSplitScreen; @@ -134,7 +133,7 @@ public class DragAndDropPolicyTest { return null; }).when(mSplitScreen).registerInSplitScreenListener(any()); - mPolicy = new DragAndDropPolicy(mContext, mIActivityTaskManager, mSplitScreen, mStarter); + mPolicy = new DragAndDropPolicy(mContext, mActivityTaskManager, mSplitScreen, mStarter); mActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY); mNonResizeableActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY); setClipDataResizeable(mNonResizeableActivityClipData, false); @@ -188,9 +187,9 @@ public class DragAndDropPolicyTest { return info; } - private void setRunningTask(ActivityManager.RunningTaskInfo task) throws RemoteException { - doReturn(Collections.singletonList(task)).when(mIActivityTaskManager) - .getFilteredTasks(anyInt(), anyBoolean()); + private void setRunningTask(ActivityManager.RunningTaskInfo task) { + doReturn(Collections.singletonList(task)).when(mActivityTaskManager) + .getTasks(anyInt(), anyBoolean()); } private void setClipDataResizeable(ClipData data, boolean resizeable) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxTaskListenerTest.java index 0f719afd08b3..fc0e20b553d3 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxTaskListenerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxTaskListenerTest.java @@ -96,6 +96,7 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { mLetterboxTaskListener.onTaskAppeared( createTaskInfo( /* taskId */ 1, + /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds /* parentBounds */ new Rect(0, 0, 200, 100), /* activityBounds */ new Rect(75, 0, 125, 75), /* taskBounds */ new Rect(50, 0, 125, 100)), @@ -109,6 +110,7 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { mLetterboxTaskListener.onTaskInfoChanged( createTaskInfo( /* taskId */ 1, + /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds /* parentBounds */ new Rect(0, 0, 200, 100), // Activity is offset by 25 to the left /* activityBounds */ new Rect(50, 0, 100, 75), @@ -130,6 +132,7 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { mLetterboxTaskListener.onTaskAppeared( createTaskInfo( /* taskId */ 1, + /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds /* parentBounds */ new Rect(0, 0, 200, 100), /* activityBounds */ new Rect(150, 0, 200, 75), /* taskBounds */ new Rect(125, 0, 200, 100)), @@ -150,6 +153,7 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { mLetterboxTaskListener.onTaskAppeared( createTaskInfo( /* taskId */ 1, + /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds /* parentBounds */ new Rect(0, 0, 200, 100), /* activityBounds */ new Rect(150, 0, 200, 75), /* taskBounds */ new Rect(125, 0, 200, 100)), @@ -170,6 +174,7 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { mLetterboxTaskListener.onTaskAppeared( createTaskInfo( /* taskId */ 1, + /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds /* parentBounds */ new Rect(0, 0, 200, 100), /* activityBounds */ new Rect(50, 0, 100, 75), /* taskBounds */ new Rect(25, 0, 100, 100)), @@ -190,6 +195,7 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { mLetterboxTaskListener.onTaskAppeared( createTaskInfo( /* taskId */ 1, + /* maxBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds /* parentBounds */ new Rect(0, 0, 100, 150), /* activityBounds */ new Rect(0, 75, 50, 125), /* taskBounds */ new Rect(0, 50, 100, 125)), @@ -210,6 +216,7 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { mLetterboxTaskListener.onTaskAppeared( createTaskInfo( /* taskId */ 1, + /* maxBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds /* parentBounds */ new Rect(0, 0, 100, 150), /* activityBounds */ new Rect(0, 75, 50, 125), /* taskBounds */ new Rect(0, 50, 100, 125)), @@ -230,6 +237,7 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { mLetterboxTaskListener.onTaskAppeared( createTaskInfo( /* taskId */ 1, + /* maxBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds /* parentBounds */ new Rect(0, 0, 100, 150), /* activityBounds */ new Rect(0, 75, 50, 125), /* taskBounds */ new Rect(0, 50, 100, 125)), @@ -250,6 +258,7 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { mLetterboxTaskListener.onTaskAppeared( createTaskInfo( /* taskId */ 1, + /* maxBounds= */ new Rect(0, 0, 200, 125), // equal to parent bounds /* parentBounds */ new Rect(0, 0, 200, 125), /* activityBounds */ new Rect(15, 0, 175, 120), /* taskBounds */ new Rect(0, 0, 100, 125)), // equal to parent bounds @@ -272,6 +281,7 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { mLetterboxTaskListener.onTaskAppeared( createTaskInfo( /* taskId */ 1, + /* maxBounds= */ new Rect(0, 0, 100, 150), /* parentBounds */ new Rect(0, 75, 100, 225), /* activityBounds */ new Rect(25, 75, 75, 125), /* taskBounds */ new Rect(0, 75, 100, 125)), @@ -285,7 +295,7 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { public void testOnTaskAppeared_calledSecondTimeWithSameTaskId_throwsException() { setWindowBoundsAndInsets(new Rect(), Insets.NONE); RunningTaskInfo taskInfo = - createTaskInfo(/* taskId */ 1, new Rect(), new Rect(), new Rect()); + createTaskInfo(/* taskId */ 1, new Rect(), new Rect(), new Rect(), new Rect()); mLetterboxTaskListener.onTaskAppeared(taskInfo, mLeash); mLetterboxTaskListener.onTaskAppeared(taskInfo, mLeash); } @@ -306,11 +316,13 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { private static RunningTaskInfo createTaskInfo( int taskId, + final Rect maxBounds, final Rect parentBounds, final Rect activityBounds, final Rect taskBounds) { RunningTaskInfo taskInfo = new RunningTaskInfo(); taskInfo.taskId = taskId; + taskInfo.configuration.windowConfiguration.setMaxBounds(maxBounds); taskInfo.parentBounds = parentBounds; taskInfo.configuration.windowConfiguration.setBounds(taskBounds); taskInfo.letterboxActivityBounds = Rect.copyOrNull(activityBounds); diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 4ed5457a2a7f..cd53217d2924 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -429,6 +429,7 @@ cc_defaults { whole_static_libs: ["libskia"], srcs: [ + "canvas/CanvasFrontend.cpp", "canvas/CanvasOpBuffer.cpp", "canvas/CanvasOpRasterizer.cpp", "pipeline/skia/SkiaDisplayList.cpp", @@ -607,6 +608,7 @@ cc_test { "tests/unit/CacheManagerTests.cpp", "tests/unit/CanvasContextTests.cpp", "tests/unit/CanvasOpTests.cpp", + "tests/unit/CanvasFrontendTests.cpp", "tests/unit/CommonPoolTests.cpp", "tests/unit/DamageAccumulatorTests.cpp", "tests/unit/DeferredLayerUpdaterTests.cpp", diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in index 49817925d9b4..c6c4ba8a6493 100644 --- a/libs/hwui/DisplayListOps.in +++ b/libs/hwui/DisplayListOps.in @@ -19,7 +19,6 @@ X(Save) X(Restore) X(SaveLayer) X(SaveBehind) -X(Concat44) X(Concat) X(SetMatrix) X(Scale) diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 473dc53dc4bf..a495ec4ac411 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -125,24 +125,18 @@ struct SaveBehind final : Op { } }; -struct Concat44 final : Op { - static const auto kType = Type::Concat44; - Concat44(const SkM44& m) : matrix(m) {} - SkM44 matrix; - void draw(SkCanvas* c, const SkMatrix&) const { c->concat(matrix); } -}; struct Concat final : Op { static const auto kType = Type::Concat; - Concat(const SkMatrix& matrix) : matrix(matrix) {} - SkMatrix matrix; + Concat(const SkM44& matrix) : matrix(matrix) {} + SkM44 matrix; void draw(SkCanvas* c, const SkMatrix&) const { c->concat(matrix); } }; struct SetMatrix final : Op { static const auto kType = Type::SetMatrix; - SetMatrix(const SkMatrix& matrix) : matrix(matrix) {} - SkMatrix matrix; + SetMatrix(const SkM44& matrix) : matrix(matrix) {} + SkM44 matrix; void draw(SkCanvas* c, const SkMatrix& original) const { - c->setMatrix(SkMatrix::Concat(original, matrix)); + c->setMatrix(SkM44(original) * matrix); } }; struct Scale final : Op { @@ -569,12 +563,9 @@ void DisplayListData::saveBehind(const SkRect* subset) { } void DisplayListData::concat(const SkM44& m) { - this->push<Concat44>(0, m); -} -void DisplayListData::concat(const SkMatrix& matrix) { - this->push<Concat>(0, matrix); + this->push<Concat>(0, m); } -void DisplayListData::setMatrix(const SkMatrix& matrix) { +void DisplayListData::setMatrix(const SkM44& matrix) { this->push<SetMatrix>(0, matrix); } void DisplayListData::scale(SkScalar sx, SkScalar sy) { @@ -834,10 +825,7 @@ bool RecordingCanvas::onDoSaveBehind(const SkRect* subset) { void RecordingCanvas::didConcat44(const SkM44& m) { fDL->concat(m); } -void RecordingCanvas::didConcat(const SkMatrix& matrix) { - fDL->concat(matrix); -} -void RecordingCanvas::didSetMatrix(const SkMatrix& matrix) { +void RecordingCanvas::didSetM44(const SkM44& matrix) { fDL->setMatrix(matrix); } void RecordingCanvas::didScale(SkScalar sx, SkScalar sy) { diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 63d120c4ca19..4851148cd4d8 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -82,8 +82,7 @@ private: void restore(); void concat(const SkM44&); - void concat(const SkMatrix&); - void setMatrix(const SkMatrix&); + void setMatrix(const SkM44&); void scale(SkScalar, SkScalar); void translate(SkScalar, SkScalar); void translateZ(SkScalar); @@ -154,8 +153,7 @@ public: void onFlush() override; void didConcat44(const SkM44&) override; - void didConcat(const SkMatrix&) override; - void didSetMatrix(const SkMatrix&) override; + void didSetM44(const SkM44&) override; void didScale(SkScalar, SkScalar) override; void didTranslate(SkScalar, SkScalar) override; diff --git a/libs/hwui/SaveFlags.h b/libs/hwui/SaveFlags.h new file mode 100644 index 000000000000..f3579a8e9f19 --- /dev/null +++ b/libs/hwui/SaveFlags.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <inttypes.h> + +// TODO: Move this to an enum class +namespace android::SaveFlags { + +// These must match the corresponding Canvas API constants. +enum { + Matrix = 0x01, + Clip = 0x02, + HasAlphaLayer = 0x04, + ClipToLayer = 0x10, + + // Helper constant + MatrixClip = Matrix | Clip, +}; +typedef uint32_t Flags; + +} // namespace android::SaveFlags diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index cd908354aea5..6030c36add7a 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -505,13 +505,11 @@ void Tree::draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& inPaint) SkPaint paint = inPaint; paint.setAlpha(mProperties.getRootAlpha() * 255); - Bitmap& bitmap = getBitmapUpdateIfDirty(); - SkBitmap skiaBitmap; - bitmap.getSkBitmap(&skiaBitmap); + sk_sp<SkImage> cachedBitmap = getBitmapUpdateIfDirty().makeImage(); int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth()); int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight()); - canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds, + canvas->drawImageRect(cachedBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds, &paint, SkCanvas::kFast_SrcRectConstraint); } diff --git a/libs/hwui/canvas/CanvasFrontend.cpp b/libs/hwui/canvas/CanvasFrontend.cpp new file mode 100644 index 000000000000..2c839b0ffc15 --- /dev/null +++ b/libs/hwui/canvas/CanvasFrontend.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CanvasFrontend.h" +#include "CanvasOps.h" +#include "CanvasOpBuffer.h" + +namespace android::uirenderer { + +CanvasStateHelper::CanvasStateHelper(int width, int height) { + mInitialBounds = SkIRect::MakeWH(width, height); + mSaveStack.emplace_back(); + mClipStack.emplace_back().setRect(mInitialBounds); + mTransformStack.emplace_back(); + mCurrentClipIndex = 0; + mCurrentTransformIndex = 0; +} + +bool CanvasStateHelper::internalSave(SaveEntry saveEntry) { + mSaveStack.push_back(saveEntry); + if (saveEntry.matrix) { + // We need to push before accessing transform() to ensure the reference doesn't move + // across vector resizes + mTransformStack.emplace_back() = transform(); + mCurrentTransformIndex += 1; + } + if (saveEntry.clip) { + // We need to push before accessing clip() to ensure the reference doesn't move + // across vector resizes + mClipStack.emplace_back() = clip(); + mCurrentClipIndex += 1; + return true; + } + return false; +} + +// Assert that the cast from SkClipOp to SkRegion::Op is valid +static_assert(static_cast<int>(SkClipOp::kDifference) == SkRegion::Op::kDifference_Op); +static_assert(static_cast<int>(SkClipOp::kIntersect) == SkRegion::Op::kIntersect_Op); +static_assert(static_cast<int>(SkClipOp::kUnion_deprecated) == SkRegion::Op::kUnion_Op); +static_assert(static_cast<int>(SkClipOp::kXOR_deprecated) == SkRegion::Op::kXOR_Op); +static_assert(static_cast<int>(SkClipOp::kReverseDifference_deprecated) == SkRegion::Op::kReverseDifference_Op); +static_assert(static_cast<int>(SkClipOp::kReplace_deprecated) == SkRegion::Op::kReplace_Op); + +void CanvasStateHelper::internalClipRect(const SkRect& rect, SkClipOp op) { + clip().opRect(rect, transform(), mInitialBounds, (SkRegion::Op)op, false); +} + +void CanvasStateHelper::internalClipPath(const SkPath& path, SkClipOp op) { + clip().opPath(path, transform(), mInitialBounds, (SkRegion::Op)op, true); +} + +bool CanvasStateHelper::internalRestore() { + // Prevent underflows + if (saveCount() <= 1) { + return false; + } + + SaveEntry entry = mSaveStack[mSaveStack.size() - 1]; + mSaveStack.pop_back(); + bool needsRestorePropagation = entry.layer; + if (entry.matrix) { + mTransformStack.pop_back(); + mCurrentTransformIndex -= 1; + } + if (entry.clip) { + // We need to push before accessing clip() to ensure the reference doesn't move + // across vector resizes + mClipStack.pop_back(); + mCurrentClipIndex -= 1; + needsRestorePropagation = true; + } + return needsRestorePropagation; +} + +SkRect CanvasStateHelper::getClipBounds() const { + SkIRect ibounds = clip().getBounds(); + + if (ibounds.isEmpty()) { + return SkRect::MakeEmpty(); + } + + SkMatrix inverse; + // if we can't invert the CTM, we can't return local clip bounds + if (!transform().invert(&inverse)) { + return SkRect::MakeEmpty(); + } + + SkRect ret = SkRect::MakeEmpty(); + inverse.mapRect(&ret, SkRect::Make(ibounds)); + return ret; +} + +bool CanvasStateHelper::quickRejectRect(float left, float top, float right, float bottom) const { + // TODO: Implement + return false; +} + +bool CanvasStateHelper::quickRejectPath(const SkPath& path) const { + // TODO: Implement + return false; +} + +} // namespace android::uirenderer diff --git a/libs/hwui/canvas/CanvasFrontend.h b/libs/hwui/canvas/CanvasFrontend.h new file mode 100644 index 000000000000..5fccccb0bb43 --- /dev/null +++ b/libs/hwui/canvas/CanvasFrontend.h @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +// TODO: Can we get the dependencies scoped down more? +#include "CanvasOps.h" +#include "CanvasOpBuffer.h" +#include <SaveFlags.h> + +#include <SkRasterClip.h> +#include <ui/FatVector.h> + +#include <optional> + +namespace android::uirenderer { + +// Exists to avoid forcing all this common logic into the templated class +class CanvasStateHelper { +protected: + CanvasStateHelper(int width, int height); + ~CanvasStateHelper() = default; + + struct SaveEntry { + bool clip : 1 = false; + bool matrix : 1 = false; + bool layer : 1 = false; + }; + + constexpr SaveEntry saveEntryForLayer() { + return { + .clip = true, + .matrix = true, + .layer = true, + }; + } + + constexpr SaveEntry flagsToSaveEntry(SaveFlags::Flags flags) { + return SaveEntry { + .clip = static_cast<bool>(flags & SaveFlags::Clip), + .matrix = static_cast<bool>(flags & SaveFlags::Matrix), + .layer = false + }; + } + + bool internalSave(SaveEntry saveEntry); + bool internalSave(SaveFlags::Flags flags) { + return internalSave(flagsToSaveEntry(flags)); + } + void internalSaveLayer(const SkCanvas::SaveLayerRec& layerRec) { + internalSave({ + .clip = true, + .matrix = true, + .layer = true + }); + internalClipRect(*layerRec.fBounds, SkClipOp::kIntersect); + } + + bool internalRestore(); + + void internalClipRect(const SkRect& rect, SkClipOp op); + void internalClipPath(const SkPath& path, SkClipOp op); + + SkIRect mInitialBounds; + FatVector<SaveEntry, 6> mSaveStack; + FatVector<SkMatrix, 6> mTransformStack; + FatVector<SkConservativeClip, 6> mClipStack; + + size_t mCurrentTransformIndex; + size_t mCurrentClipIndex; + + const SkConservativeClip& clip() const { + return mClipStack[mCurrentClipIndex]; + } + + SkConservativeClip& clip() { + return mClipStack[mCurrentClipIndex]; + } + +public: + int saveCount() const { return mSaveStack.size(); } + + SkRect getClipBounds() const; + bool quickRejectRect(float left, float top, float right, float bottom) const; + bool quickRejectPath(const SkPath& path) const; + + const SkMatrix& transform() const { + return mTransformStack[mCurrentTransformIndex]; + } + + SkMatrix& transform() { + return mTransformStack[mCurrentTransformIndex]; + } + + // For compat with existing HWUI Canvas interface + void getMatrix(SkMatrix* outMatrix) const { + *outMatrix = transform(); + } + + void setMatrix(const SkMatrix& matrix) { + transform() = matrix; + } + + void concat(const SkMatrix& matrix) { + transform().preConcat(matrix); + } + + void rotate(float degrees) { + SkMatrix m; + m.setRotate(degrees); + concat(m); + } + + void scale(float sx, float sy) { + SkMatrix m; + m.setScale(sx, sy); + concat(m); + } + + void skew(float sx, float sy) { + SkMatrix m; + m.setSkew(sx, sy); + concat(m); + } + + void translate(float dx, float dy) { + transform().preTranslate(dx, dy); + } +}; + +// Front-end canvas that handles queries, up-front state, and produces CanvasOp<> output downstream +template <typename CanvasOpReceiver> +class CanvasFrontend final : public CanvasStateHelper { +public: + template<class... Args> + CanvasFrontend(int width, int height, Args&&... args) : CanvasStateHelper(width, height), + mReceiver(std::forward<Args>(args)...) { } + ~CanvasFrontend() = default; + + void save(SaveFlags::Flags flags = SaveFlags::MatrixClip) { + if (internalSave(flagsToSaveEntry(flags))) { + submit<CanvasOpType::Save>({}); + } + } + + void restore() { + if (internalRestore()) { + submit<CanvasOpType::Restore>({}); + } + } + + template <CanvasOpType T> + void draw(CanvasOp<T>&& op) { + // The front-end requires going through certain front-doors, which these aren't. + static_assert(T != CanvasOpType::Save, "Must use CanvasFrontend::save() call instead"); + static_assert(T != CanvasOpType::Restore, "Must use CanvasFrontend::restore() call instead"); + + if constexpr (T == CanvasOpType::SaveLayer) { + internalSaveLayer(op.saveLayerRec); + } + if constexpr (T == CanvasOpType::SaveBehind) { + // Don't use internalSaveLayer as this doesn't apply clipping, it's a "regular" save + // But we do want to flag it as a layer, such that restore is Definitely Required + internalSave(saveEntryForLayer()); + } + if constexpr (T == CanvasOpType::ClipRect) { + internalClipRect(op.rect, op.op); + } + if constexpr (T == CanvasOpType::ClipPath) { + internalClipPath(op.path, op.op); + } + + submit(std::move(op)); + } + + const CanvasOpReceiver& receiver() const { return mReceiver; } + +private: + CanvasOpReceiver mReceiver; + + template <CanvasOpType T> + void submit(CanvasOp<T>&& op) { + mReceiver.push_container(CanvasOpContainer(std::move(op), transform())); + } +}; + +} // namespace android::uirenderer diff --git a/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl b/libs/hwui/canvas/CanvasOpRecorder.cpp index 45e4c69102f0..bb968ee84670 100644 --- a/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl +++ b/libs/hwui/canvas/CanvasOpRecorder.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,9 @@ * limitations under the License. */ -package android.net.wifi; +#include "CanvasOpRecorder.h" -/** @hide */ -parcelable WifiApiServiceInfo { - String name; - IBinder binder; -} +#include "CanvasOpBuffer.h" +#include "CanvasOps.h" + +namespace android::uirenderer {} // namespace android::uirenderer diff --git a/libs/hwui/canvas/CanvasOpRecorder.h b/libs/hwui/canvas/CanvasOpRecorder.h new file mode 100644 index 000000000000..7d95bc4785ea --- /dev/null +++ b/libs/hwui/canvas/CanvasOpRecorder.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "hwui/Canvas.h" +#include "CanvasOpBuffer.h" + +#include <vector> + +namespace android::uirenderer { + +// Interop with existing HWUI Canvas +class CanvasOpRecorder final : /* todo: public Canvas */ { +public: + // Transform ops +private: + struct SaveEntry { + + }; + + std::vector<SaveEntry> mSaveStack; +}; + +} // namespace android::uirenderer diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index f94bae2746d9..4d67166dd8d2 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -18,6 +18,7 @@ #include <cutils/compiler.h> #include <utils/Functor.h> +#include <SaveFlags.h> #include <androidfw/ResourceTypes.h> #include "Properties.h" @@ -57,22 +58,6 @@ class SkiaDisplayList; using DisplayList = skiapipeline::SkiaDisplayList; } -namespace SaveFlags { - -// These must match the corresponding Canvas API constants. -enum { - Matrix = 0x01, - Clip = 0x02, - HasAlphaLayer = 0x04, - ClipToLayer = 0x10, - - // Helper constant - MatrixClip = Matrix | Clip, -}; -typedef uint32_t Flags; - -} // namespace SaveFlags - namespace uirenderer { namespace VectorDrawable { class Tree; diff --git a/libs/hwui/tests/unit/CanvasFrontendTests.cpp b/libs/hwui/tests/unit/CanvasFrontendTests.cpp new file mode 100644 index 000000000000..05b11795d90d --- /dev/null +++ b/libs/hwui/tests/unit/CanvasFrontendTests.cpp @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <canvas/CanvasFrontend.h> +#include <canvas/CanvasOpBuffer.h> +#include <canvas/CanvasOps.h> +#include <canvas/CanvasOpRasterizer.h> + +#include <tests/common/CallCountingCanvas.h> + +#include "SkPictureRecorder.h" +#include "SkColor.h" +#include "SkLatticeIter.h" +#include "pipeline/skia/AnimatedDrawables.h" +#include <SkNoDrawCanvas.h> + +using namespace android; +using namespace android::uirenderer; +using namespace android::uirenderer::test; + +class CanvasOpCountingReceiver { +public: + template <CanvasOpType T> + void push_container(CanvasOpContainer<T>&& op) { + mOpCounts[static_cast<size_t>(T)] += 1; + } + + int operator[](CanvasOpType op) const { + return mOpCounts[static_cast<size_t>(op)]; + } + +private: + std::array<int, static_cast<size_t>(CanvasOpType::COUNT)> mOpCounts; +}; + +TEST(CanvasFrontend, saveCount) { + SkNoDrawCanvas skiaCanvas(100, 100); + CanvasFrontend<CanvasOpCountingReceiver> opCanvas(100, 100); + const auto& receiver = opCanvas.receiver(); + + EXPECT_EQ(1, skiaCanvas.getSaveCount()); + EXPECT_EQ(1, opCanvas.saveCount()); + + skiaCanvas.save(); + opCanvas.save(SaveFlags::MatrixClip); + EXPECT_EQ(2, skiaCanvas.getSaveCount()); + EXPECT_EQ(2, opCanvas.saveCount()); + + skiaCanvas.restore(); + opCanvas.restore(); + EXPECT_EQ(1, skiaCanvas.getSaveCount()); + EXPECT_EQ(1, opCanvas.saveCount()); + + skiaCanvas.restore(); + opCanvas.restore(); + EXPECT_EQ(1, skiaCanvas.getSaveCount()); + EXPECT_EQ(1, opCanvas.saveCount()); + + EXPECT_EQ(1, receiver[CanvasOpType::Save]); + EXPECT_EQ(1, receiver[CanvasOpType::Restore]); +} + +TEST(CanvasFrontend, transform) { + SkNoDrawCanvas skiaCanvas(100, 100); + CanvasFrontend<CanvasOpCountingReceiver> opCanvas(100, 100); + + skiaCanvas.translate(10, 10); + opCanvas.translate(10, 10); + EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform()); + + { + skiaCanvas.save(); + opCanvas.save(SaveFlags::Matrix); + skiaCanvas.scale(2.0f, 1.125f); + opCanvas.scale(2.0f, 1.125f); + + EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform()); + skiaCanvas.restore(); + opCanvas.restore(); + } + + EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform()); + + { + skiaCanvas.save(); + opCanvas.save(SaveFlags::Matrix); + skiaCanvas.rotate(90.f); + opCanvas.rotate(90.f); + + EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform()); + + { + skiaCanvas.save(); + opCanvas.save(SaveFlags::Matrix); + skiaCanvas.skew(5.0f, 2.25f); + opCanvas.skew(5.0f, 2.25f); + + EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform()); + skiaCanvas.restore(); + opCanvas.restore(); + } + + skiaCanvas.restore(); + opCanvas.restore(); + } + + EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform()); +} + +TEST(CanvasFrontend, drawOpTransform) { + CanvasFrontend<CanvasOpBuffer> opCanvas(100, 100); + const auto& receiver = opCanvas.receiver(); + + auto makeDrawRect = [] { + return CanvasOp<CanvasOpType::DrawRect>{ + .rect = SkRect::MakeWH(50, 50), + .paint = SkPaint(SkColors::kBlack), + }; + }; + + opCanvas.draw(makeDrawRect()); + + opCanvas.translate(10, 10); + opCanvas.draw(makeDrawRect()); + + opCanvas.save(); + opCanvas.scale(2.0f, 4.0f); + opCanvas.draw(makeDrawRect()); + opCanvas.restore(); + + opCanvas.save(); + opCanvas.translate(20, 15); + opCanvas.draw(makeDrawRect()); + opCanvas.save(); + opCanvas.rotate(90.f); + opCanvas.draw(makeDrawRect()); + opCanvas.restore(); + opCanvas.restore(); + + // Validate the results + std::vector<SkMatrix> transforms; + transforms.reserve(5); + receiver.for_each([&](auto op) { + // Filter for the DrawRect calls; ignore the save & restores + // (TODO: Add a filtered for_each variant to OpBuffer?) + if (op->type() == CanvasOpType::DrawRect) { + transforms.push_back(op->transform()); + } + }); + + EXPECT_EQ(transforms.size(), 5); + + { + // First result should be identity + const auto& result = transforms[0]; + EXPECT_EQ(SkMatrix::kIdentity_Mask, result.getType()); + EXPECT_EQ(SkMatrix::I(), result); + } + + { + // Should be translate 10, 10 + const auto& result = transforms[1]; + EXPECT_EQ(SkMatrix::kTranslate_Mask, result.getType()); + SkMatrix m; + m.setTranslate(10, 10); + EXPECT_EQ(m, result); + } + + { + // Should be translate 10, 10 + scale 2, 4 + const auto& result = transforms[2]; + EXPECT_EQ(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask, result.getType()); + SkMatrix m; + m.setTranslate(10, 10); + m.preScale(2.0f, 4.0f); + EXPECT_EQ(m, result); + } + + { + // Should be translate 10, 10 + translate 20, 15 + const auto& result = transforms[3]; + EXPECT_EQ(SkMatrix::kTranslate_Mask, result.getType()); + SkMatrix m; + m.setTranslate(30, 25); + EXPECT_EQ(m, result); + } + + { + // Should be translate 10, 10 + translate 20, 15 + rotate 90 + const auto& result = transforms[4]; + EXPECT_EQ(SkMatrix::kTranslate_Mask | SkMatrix::kAffine_Mask | SkMatrix::kScale_Mask, + result.getType()); + SkMatrix m; + m.setTranslate(30, 25); + m.preRotate(90.f); + EXPECT_EQ(m, result); + } +}
\ No newline at end of file diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp index b15c3221dd60..f186e55ec2e3 100644 --- a/libs/hwui/tests/unit/CanvasOpTests.cpp +++ b/libs/hwui/tests/unit/CanvasOpTests.cpp @@ -16,6 +16,7 @@ #include <gtest/gtest.h> +#include <canvas/CanvasFrontend.h> #include <canvas/CanvasOpBuffer.h> #include <canvas/CanvasOps.h> #include <canvas/CanvasOpRasterizer.h> @@ -26,6 +27,7 @@ #include "SkColor.h" #include "SkLatticeIter.h" #include "pipeline/skia/AnimatedDrawables.h" +#include <SkNoDrawCanvas.h> using namespace android; using namespace android::uirenderer; @@ -78,6 +80,21 @@ struct MockOp<MockTypes::Lifecycle> { using MockBuffer = OpBuffer<MockTypes, MockOpContainer>; +class CanvasOpCountingReceiver { +public: + template <CanvasOpType T> + void push_container(CanvasOpContainer<T>&& op) { + mOpCounts[static_cast<size_t>(T)] += 1; + } + + int operator[](CanvasOpType op) const { + return mOpCounts[static_cast<size_t>(op)]; + } + +private: + std::array<int, static_cast<size_t>(CanvasOpType::COUNT)> mOpCounts; +}; + template<typename T> static int countItems(const T& t) { int count = 0; @@ -614,4 +631,35 @@ TEST(CanvasOp, immediateRendering) { rasterizer.draw(op); EXPECT_EQ(1, canvas->drawRectCount); EXPECT_EQ(1, canvas->sumTotalDrawCalls()); +} + +TEST(CanvasOp, frontendSaveCount) { + SkNoDrawCanvas skiaCanvas(100, 100); + CanvasFrontend<CanvasOpCountingReceiver> opCanvas(100, 100); + const auto& receiver = opCanvas.receiver(); + + EXPECT_EQ(1, skiaCanvas.getSaveCount()); + EXPECT_EQ(1, opCanvas.saveCount()); + + skiaCanvas.save(); + opCanvas.save(SaveFlags::MatrixClip); + EXPECT_EQ(2, skiaCanvas.getSaveCount()); + EXPECT_EQ(2, opCanvas.saveCount()); + + skiaCanvas.restore(); + opCanvas.restore(); + EXPECT_EQ(1, skiaCanvas.getSaveCount()); + EXPECT_EQ(1, opCanvas.saveCount()); + + skiaCanvas.restore(); + opCanvas.restore(); + EXPECT_EQ(1, skiaCanvas.getSaveCount()); + EXPECT_EQ(1, opCanvas.saveCount()); + + EXPECT_EQ(1, receiver[Op::Save]); + EXPECT_EQ(1, receiver[Op::Restore]); +} + +TEST(CanvasOp, frontendTransform) { + }
\ No newline at end of file diff --git a/libs/hwui/tests/unit/CommonPoolTests.cpp b/libs/hwui/tests/unit/CommonPoolTests.cpp index da6a2604a4b6..bffdeca4db54 100644 --- a/libs/hwui/tests/unit/CommonPoolTests.cpp +++ b/libs/hwui/tests/unit/CommonPoolTests.cpp @@ -54,7 +54,9 @@ TEST(DISABLED_CommonPool, threadCount) { EXPECT_EQ(0, threads.count(gettid())); } -TEST(CommonPool, singleThread) { +// Disabled since this is flaky. This isn't a necessarily useful functional test, so being +// disabled isn't that significant. However it may be good to resurrect this somehow. +TEST(CommonPool, DISABLED_singleThread) { std::mutex mutex; std::condition_variable fence; bool isProcessing = false; diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp index accf0f4f2aaa..26bc65915c7a 100644 --- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp @@ -1107,26 +1107,26 @@ TEST(ReorderBarrierDrawable, testShadowMatrix) { EXPECT_EQ(dy, TRANSLATE_Y); } - virtual void didSetMatrix(const SkMatrix& matrix) override { + virtual void didSetM44(const SkM44& matrix) override { mDrawCounter++; // First invocation is EndReorderBarrierDrawable::drawShadow to apply shadow matrix. // Second invocation is preparing the matrix for an elevated RenderNodeDrawable. - EXPECT_TRUE(matrix.isIdentity()); + EXPECT_TRUE(matrix == SkM44()); EXPECT_TRUE(getTotalMatrix().isIdentity()); } - virtual void didConcat(const SkMatrix& matrix) override { + virtual void didConcat44(const SkM44& matrix) override { mDrawCounter++; if (mFirstDidConcat) { // First invocation is EndReorderBarrierDrawable::drawShadow to apply shadow matrix. mFirstDidConcat = false; - EXPECT_EQ(SkMatrix::Translate(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y), + EXPECT_EQ(SkM44::Translate(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y), matrix); EXPECT_EQ(SkMatrix::Translate(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y), getTotalMatrix()); } else { // Second invocation is preparing the matrix for an elevated RenderNodeDrawable. - EXPECT_EQ(SkMatrix::Translate(TRANSLATE_X, TRANSLATE_Y), matrix); + EXPECT_EQ(SkM44::Translate(TRANSLATE_X, TRANSLATE_Y), matrix); EXPECT_EQ(SkMatrix::Translate(TRANSLATE_X, TRANSLATE_Y), getTotalMatrix()); } } diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp index c19e1ed6ce75..4659a929a9eb 100644 --- a/libs/hwui/tests/unit/RenderNodeTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeTests.cpp @@ -264,6 +264,8 @@ TEST(RenderNode, releasedCallback) { TestUtils::runOnRenderThreadUnmanaged([&] (RenderThread&) { TestUtils::syncHierarchyPropertiesAndDisplayList(node); }); + // Fence on any remaining post'd work + TestUtils::runOnRenderThreadUnmanaged([] (RenderThread&) {}); EXPECT_EQ(2, counts.sync); EXPECT_EQ(1, counts.destroyed); } diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java index a9c754d3df79..c6c7142c3bd0 100755 --- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java @@ -410,7 +410,7 @@ public class InstallInstalling extends AlertActivity { InstallInstalling.this, mInstallId, broadcastIntent, - PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); session.commit(pendingIntent.getIntentSender()); mCancelButton.setEnabled(false); diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledNotificationUtils.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledNotificationUtils.java index caf971800ad2..eea12eca1859 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledNotificationUtils.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledNotificationUtils.java @@ -243,8 +243,8 @@ class PackageInstalledNotificationUtils { } intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - return PendingIntent.getActivity(mContext, - 0 /* request code */, intent, PendingIntent.FLAG_UPDATE_CURRENT); + return PendingIntent.getActivity(mContext, 0 /* request code */, intent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); } /** @@ -260,8 +260,8 @@ class PackageInstalledNotificationUtils { } intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - return PendingIntent.getActivity(mContext, - 0 /* request code */, intent, PendingIntent.FLAG_UPDATE_CURRENT); + return PendingIntent.getActivity(mContext, 0 /* request code */, intent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); } /** diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java index c4dceb4fe079..7bf27dfe6420 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java @@ -95,7 +95,8 @@ public class UninstallUninstalling extends Activity implements broadcastIntent.setPackage(getPackageName()); PendingIntent pendingIntent = PendingIntent.getBroadcast(this, mUninstallId, - broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT); + broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT + | PendingIntent.FLAG_MUTABLE); int flags = allUsers ? PackageManager.DELETE_ALL_USERS : 0; flags |= keepData ? PackageManager.DELETE_KEEP_DATA : 0; diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java index bf4b03c56b79..7e0490a233f9 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java @@ -269,7 +269,8 @@ public class PackageInstallerImpl { // Create a matching PendingIntent and use it to generate the IntentSender Intent broadcastIntent = new Intent(action); PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, packageName.hashCode(), - broadcastIntent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT); + broadcastIntent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT + | PendingIntent.FLAG_MUTABLE); return pendingIntent.getIntentSender(); } diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 2b30e0a61863..41af185da0b7 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> oor tot battery gelaai is"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot battery gelaai is"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Battery word tydelik beperk"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Onbekend"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Laai"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laai tans vinnig"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 0bebfabf2eb9..8e4e402c2e16 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ኃይል እስከሚሞላ ድረስ ይቀራል"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ኃይል እስከሚሞላ ድረስ"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ባትሪ ለጊዜው ተገድቧል"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ያልታወቀ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ኃይል በመሙላት ላይ"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ኃይል በፍጥነት በመሙላት ላይ"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 6eaf3a98da11..691ec046da02 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> إلى أن يتم شحن الجهاز بالكامل"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> إلى أن يتم شحن الجهاز بالكامل"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - تأثير محدود على البطارية مؤقتًا"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"غير معروف"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"جارٍ الشحن"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"جارٍ الشحن سريعًا"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index e207e1ce4c12..61214e09ba26 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"চাৰ্জ হ’বলৈ <xliff:g id="TIME">%1$s</xliff:g> বাকী আছে"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> চাৰ্জ হ\'বলৈ"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - বেটাৰী সাময়িকভাৱে সীমিত কৰা হৈছে"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"অজ্ঞাত"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"চাৰ্জ কৰি থকা হৈছে"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"দ্ৰুততাৰে চাৰ্জ হৈছে"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index ff1209c754d1..db61527a54d8 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Enerjinin dolmasına <xliff:g id="TIME">%1$s</xliff:g> qalıb"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - Enerjinin dolmasına <xliff:g id="TIME">%2$s</xliff:g> qalıb"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batareya müvəqqəti məhdudlaşdırılıb"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Naməlum"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Enerji doldurma"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Sürətlə doldurulur"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index eb099c1926a7..70fb3636b492 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Napuniće se za <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – napuniće se za <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – baterija je trenutno ograničena"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Puni se"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo se puni"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index e6e49ab2063c..8a0db8261a71 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Засталося <xliff:g id="TIME">%1$s</xliff:g> да поўнай зарадкі"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> да поўнай зарадкі"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарад акумулятара часова абмежаваны"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Невядома"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Зарадка"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хуткая зарадка"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 251c4dd545c4..27dcf105fd2e 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Оставащо време до пълно зареждане: <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до пълно зареждане"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Батерията е временно ограничена"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Неизвестно"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Зарежда се"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Зарежда се бързо"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 6a99a84358d4..29862e6efd31 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -211,9 +211,9 @@ <string name="adb_wireless_error" msgid="721958772149779856">"সমস্যা"</string> <string name="adb_wireless_settings" msgid="2295017847215680229">"ওয়্যারলেস ডিবাগিং"</string> <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"কোন কোন ডিভাইস উপলভ্য আছে তা দেখে নিয়ে ব্যবহার করার জন্য, ওয়্যারলেস ডিবাগিং চালু করুন"</string> - <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"QR কোড ব্যবহার করে ডিভাইস যোগ করুন"</string> + <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"QR কোড ব্যবহার করে ডিভাইসের সাথে পেয়ার করুন"</string> <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"QR কোড স্ক্যানার ব্যবহার করে নতুন ডিভাইস যোগ করুন"</string> - <string name="adb_pair_method_code_title" msgid="1122590300445142904">"যোগ করার কোড ব্যবহার করে ডিভাইস যোগ করুন"</string> + <string name="adb_pair_method_code_title" msgid="1122590300445142904">"পেয়ারিং কোড ব্যবহার করে ডিভাইসের সাথে পেয়ার করুন"</string> <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"ছয় সংখ্যার কোড ব্যবহার করে নতুন ডিভাইস যোগ করুন"</string> <string name="adb_paired_devices_title" msgid="5268997341526217362">"যোগ করা ডিভাইস"</string> <string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"এখন কানেক্ট রয়েছে"</string> @@ -222,16 +222,16 @@ <string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"ডিভাইসে আঙ্গুলের ছাপ: <xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string> <string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"কানেক্ট করা যায়নি"</string> <string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>টি সঠিক নেটওয়ার্কে কানেক্ট আছে কিনা দেখে নিন"</string> - <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"ডিভাইসের সাথে যোগ করুন"</string> - <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"ওয়াই-ফাই যোগ করার কোড"</string> - <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"যোগ করা যায়নি"</string> + <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"ডিভাইসের সাথে পেয়ার করুন"</string> + <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"ওয়াই-ফাইয়ের সাথে পেয়ার করার কোড"</string> + <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"পেয়ার করা যায়নি"</string> <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"ডিভাইসটি একই নেটওয়ার্কে কানেক্ট আছে কিনা দেখে নিন।"</string> - <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR কোড স্ক্যান করে ওয়াই-ফাই ব্যবহার করে ডিভাইস যোগ করুন"</string> + <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR কোড স্ক্যান করে ওয়াই-ফাই ব্যবহার করে ডিভাইসের সাথে পেয়ার করুন"</string> <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"ডিভাইস যোগ করা হচ্ছে…"</string> <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"ডিভাইস যোগ করা যায়নি। এটি দুটি কারণে হয়ে থাকে - QR কোডটি সঠিক নয় বা ডিভাইসটি একই নেটওয়ার্কে কানেক্ট করা নেই।"</string> <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP অ্যাড্রেস ও পোর্ট"</string> <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR কোড স্ক্যান করুন"</string> - <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"QR কোড স্ক্যান করে ওয়াই-ফাইয়ের সাহায্যে ডিভাইস যোগ করুন"</string> + <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"QR কোড স্ক্যান করে ওয়াই-ফাই ব্যবহার করে ডিভাইসের সাথে পেয়ার করুন"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"একটি ওয়াই-ফাই নেটওয়ার্কের সাথে কানেক্ট করুন"</string> <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, debug, dev"</string> <string name="bugreport_in_power" msgid="8664089072534638709">"ত্রুটি প্রতিবেদনের শর্টকাট"</string> @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"সম্পূর্ণ চার্জ হতে <xliff:g id="TIME">%1$s</xliff:g> বাকি আছে"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>-এ সম্পূর্ণ চার্জ হয়ে যাবে"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ব্যাটারি কিছুক্ষণের জন্য সীমিত করা হয়েছে"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"অজানা"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"চার্জ হচ্ছে"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"দ্রুত চার্জ হচ্ছে"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index aeb7f2ecc567..4704ec8db79a 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Napunit će se za <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – napunit će se za <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Baterija je privremeno ograničena"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Punjenje"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo punjenje"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index df4c99e20505..5d04963d9b4a 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -66,7 +66,7 @@ <string name="bluetooth_disconnected" msgid="7739366554710388701">"Desconnectat"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"S\'està desconnectant..."</string> <string name="bluetooth_connecting" msgid="5871702668260192755">"S\'està connectant…"</string> - <string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connectat"</string> + <string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> Connectat"</string> <string name="bluetooth_pairing" msgid="4269046942588193600">"S\'està vinculant..."</string> <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connectat (sense accés al telèfon)"</string> <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connectat (sense contingut multimèdia)"</string> @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> per completar la càrrega"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>: bateria limitada temporalment"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Desconegut"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"S\'està carregant"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregant ràpidament"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index e563ca5a03f8..a99a4dabbc60 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Do nabití zbývá: <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do nabití"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Baterie dočasně omezena"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Neznámé"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Nabíjí se"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Rychlé nabíjení"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 8a3d054c3ae8..c90155eb2b68 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Opladet om <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – opladet om <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Batteriet er midlertidigt begrænset"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Ukendt"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Oplader"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Oplader hurtigt"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 624b299a96c3..b5b9fc480607 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -399,12 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inaktiv. Zum Wechseln tippen."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktiv. Zum Wechseln tippen."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Standby-Status der App:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <!-- no translation found for transcode_settings_title (2581975870429850549) --> - <skip /> - <!-- no translation found for transcode_enable_all (9102460144086871903) --> - <skip /> - <!-- no translation found for transcode_skip_apps (8249721984597390142) --> - <skip /> + <string name="transcode_settings_title" msgid="2581975870429850549">"Einstellungen für Medientranscodierung"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Transcodierung deaktivieren"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Transcodierung für Apps aktivieren"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Aktive Dienste"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Momentan ausgeführte Dienste anzeigen und steuern"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-Implementierung"</string> @@ -452,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Noch <xliff:g id="TIME">%1$s</xliff:g> bis zur Aufladung"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> bis zur Aufladung"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Akku vorübergehend eingeschränkt"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Unbekannt"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Wird aufgeladen"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Schnelles Aufladen"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 7ea90506ac06..203ec40c5a42 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Απομένουν <xliff:g id="TIME">%1$s</xliff:g> για ολοκλήρωση της φόρτισης"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για την ολοκλήρωση της φόρτισης"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Η μπαταρία περιορίστηκε προσωρινά."</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Άγνωστο"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Φόρτιση"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ταχεία φόρτιση"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 7affb5c89dfc..08ff55099c9c 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -196,7 +196,7 @@ <string name="choose_profile" msgid="343803890897657450">"Elegir perfil"</string> <string name="category_personal" msgid="6236798763159385225">"Personal"</string> <string name="category_work" msgid="4014193632325996115">"Trabajo"</string> - <string name="development_settings_title" msgid="140296922921597393">"Opciones para programadores"</string> + <string name="development_settings_title" msgid="140296922921597393">"Opciones para desarrolladores"</string> <string name="development_settings_enable" msgid="4285094651288242183">"Activar opciones para programador"</string> <string name="development_settings_summary" msgid="8718917813868735095">"Establecer opciones para desarrollar aplicaciones"</string> <string name="development_settings_not_available" msgid="355070198089140951">"Las opciones de programador no están disponibles para este usuario."</string> @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> para completar la carga"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la carga"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batería limitada temporalmente"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Desconocido"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rápido"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index ccdc1c443fbd..6f6a1a1c9b17 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> hasta cargarse completamente"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g> hasta cargarse completamente)"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>: batería limitada temporalmente"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Desconocido"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rápidamente"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 852d6df7d31e..9d6326e45706 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Täislaadimiseni on jäänud <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> täislaadimiseni"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – akutase on ajutiselt piiratud"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Tundmatu"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Laadimine"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Kiirlaadimine"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 6b6b9b63df65..01afa1706e3e 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> guztiz kargatu arte"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> guztiz kargatu arte"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>: bateria mugatuta egongo da aldi batez"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Ezezaguna"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Kargatzen"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Bizkor kargatzen"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index af1e55be67c5..dd7ab9672e96 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> مانده تا شارژ کامل"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - باتری موقتاً محدود شده است"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ناشناس"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"در حال شارژ شدن"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"درحال شارژ شدن سریع"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 8e819efe3557..b86b02d8c25f 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> jäljellä täyteen lataukseen"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> täyteen lataukseen"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Akun käyttöä rajoitettu tilapäisesti"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Tuntematon"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Ladataan"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Nopea lataus"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 4754d15b5a52..0bca1dba1aa4 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> jusqu\'à la charge complète"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> : <xliff:g id="TIME">%2$s</xliff:g> jusqu\'à la charge complète"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Pile limitée temporairement"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Inconnu"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Charge en cours…"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Recharge rapide"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index a3863f596067..ebde43ba52a2 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> jusqu\'à ce que la batterie soit chargée"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> jusqu\'à la charge complète"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batterie limitée temporairement"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Inconnu"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Batterie en charge"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charge rapide"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 81e79e4a27a9..b040817b2801 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> para completar a carga"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar a carga"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> (batería limitada temporalmente)"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Descoñecido"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rapidamente"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index 9bf09704a4f5..c1bd7cac30a6 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -399,12 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"નિષ્ક્રિય. ટોગલ કરવા માટે ટૅપ કરો."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"સક્રિય. ટોગલ કરવા માટે ટૅપ કરો."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"ઍપ સ્ટૅન્ડબાયની સ્થિતિ:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <!-- no translation found for transcode_settings_title (2581975870429850549) --> - <skip /> - <!-- no translation found for transcode_enable_all (9102460144086871903) --> - <skip /> - <!-- no translation found for transcode_skip_apps (8249721984597390142) --> - <skip /> + <string name="transcode_settings_title" msgid="2581975870429850549">"મીડિયાનું ફૉર્મેટ બદલવાની પ્રક્રિયાના સેટિંગ"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ફૉર્મેટ બદલવાની પ્રક્રિયા બંધ કરો"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ઍપ માટે ફૉર્મેટ બદલવાની પ્રક્રિયા ચાલુ કરો"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"ચાલુ સેવાઓ"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"હાલમાં ચાલતી સેવાઓ જુઓ અને નિયંત્રિત કરો"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView અમલીકરણ"</string> @@ -452,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ચાર્જ થવામાં <xliff:g id="TIME">%1$s</xliff:g> બાકી છે"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જ થવા માટે <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - બૅટરીનો વપરાશ હંગામી રૂપે મર્યાદિત છે"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"અજાણ્યું"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ચાર્જ થઈ રહ્યું છે"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ઝડપથી ચાર્જ થાય છે"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 151b861cbd3b..d9153a1ab542 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"चार्ज पूरा होने में <xliff:g id="TIME">%1$s</xliff:g> बचा है"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> में पूरा चार्ज हो जाएगा"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - कुछ समय के लिए, बैटरी का सीमित इस्तेमाल होगा"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज हो रही है"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"तेज़ चार्ज हो रही है"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index d37879f6e153..b51e096ca6b7 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Napunit će se za <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – napunit će se za <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Baterija je privremeno ograničena"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Punjenje"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo punjenje"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 4ac5308474c5..33770fc1bdd8 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> van hátra a feltöltésből"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a feltöltésig"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Akkumulátor ideiglenesen korlátozva"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Ismeretlen"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Töltés"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Gyorstöltés"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index b0734fdee27e..06d70f854f27 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> մինչև լիցքավորումը"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> մինչև լիցքավորումը"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Մարտկոցը ժամանակավորապես սահմանափակված է"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Անհայտ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Լիցքավորում"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Արագ լիցքավորում"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 9db21c796bbf..4123d9e3b755 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Sisa <xliff:g id="TIME">%1$s</xliff:g> hingga terisi penuh"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lagi terisi penuh"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Daya baterai terbatas untuk sementara"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Tidak diketahui"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Mengisi daya"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mengisi daya cepat"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index d9ee03adefa4..3b9bef872177 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> að fullri hleðslu"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> að fullri hleðslu"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Rafhlaða takmörkuð tímabundið"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Óþekkt"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Í hleðslu"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hröð hleðsla"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 57378e78af65..1ab6b24456f3 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -66,7 +66,7 @@ <string name="bluetooth_disconnected" msgid="7739366554710388701">"Disconnesso"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Disconnessione…"</string> <string name="bluetooth_connecting" msgid="5871702668260192755">"Connessione…"</string> - <string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso"</string> + <string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> Connesso"</string> <string name="bluetooth_pairing" msgid="4269046942588193600">"Accoppiamento…"</string> <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso (telefono escluso)"</string> <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso (contenuti multimediali esclusi)"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index fd53f14bd48f..15868d5fc997 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -399,12 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"אפליקציה לא פעילה. הקש כדי להחליף מצב."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"אפליקציה פעילה. הקש כדי להחליף מצב."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"אפליקציה במצב המתנה:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <!-- no translation found for transcode_settings_title (2581975870429850549) --> - <skip /> - <!-- no translation found for transcode_enable_all (9102460144086871903) --> - <skip /> - <!-- no translation found for transcode_skip_apps (8249721984597390142) --> - <skip /> + <string name="transcode_settings_title" msgid="2581975870429850549">"הגדרות של המרת קידוד למדיה"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"השבתה של המרת קידוד"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"הפעלה של המרת קידוד לאפליקציות"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"שירותים פועלים"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"הצגת השירותים הפועלים כעת ושליטה בהם"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"יישום WebView"</string> @@ -452,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"נשארו <xliff:g id="TIME">%1$s</xliff:g> עד הטעינה"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> עד הטעינה"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - הסוללה מוגבלת באופן זמני"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"לא ידוע"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"בטעינה"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"הסוללה נטענת מהר"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index c0aa74bafc56..89ee98eeeb42 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"充電完了まであと <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電完了まで <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - 電池の使用が一時的に制限されています"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"不明"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"急速充電中"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 09f3ff5c957a..2bc1c4d20728 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Зарядталғанға дейін <xliff:g id="TIME">%1$s</xliff:g> қалды"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядталғанға дейін <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батарея жұмысы уақытша шектелген"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Белгісіз"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Зарядталуда"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Жылдам зарядталуда"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 42e6a53a0078..7306cf8c5d3f 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ទៀតទើបសាកថ្មពេញ"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ទៀតទើបសាកថ្មពេញ"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - បានដាក់កម្រិតថ្មជាបណ្ដោះអាសន្ន"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"មិនស្គាល់"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"កំពុងបញ្ចូលថ្ម"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"កំពុងសាកថ្មយ៉ាងឆាប់រហ័ស"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 658b987dd833..0c1502ca620d 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ಚಾರ್ಜ್ ಆಗಲು <xliff:g id="TIME">%1$s</xliff:g> ಸಮಯ ಬಾಕಿ ಇದೆ"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಚಾರ್ಜ್ ಆಗಲು <xliff:g id="TIME">%2$s</xliff:g> ಸಮಯ ಬೇಕು"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಬ್ಯಾಟರಿ ತಾತ್ಕಾಲಿಕವಾಗಿ ಸೀಮಿತವಾಗಿದೆ"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ಅಪರಿಚಿತ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ವೇಗದ ಚಾರ್ಜಿಂಗ್"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 2b0843dcef39..13223440d098 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"충전 완료까지 <xliff:g id="TIME">%1$s</xliff:g> 남음"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - 충전 완료까지 <xliff:g id="TIME">%2$s</xliff:g> 남음"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - 일시적으로 배터리 사용 제한"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"알 수 없음"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"충전 중"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"고속 충전 중"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 499fd88f5a4f..9697aa70e44f 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> кийин толук кубатталып бүтөт"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> кийин толук кубатталып бүтөт"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батареяны колдонуу убактлуу чектелген"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Белгисиз"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Кубатталууда"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ыкчам кубатталууда"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index a5be6017ca62..720122262dcf 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"ບໍ່ໄດ້ນຳໃຊ້. ແຕະບໍ່ສັບປ່ຽນ."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"ນຳໃຊ້ຢູ່. ແຕະເພື່ອສັບປ່ຽນ."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"ສະຖານະສະແຕນບາຍແອັບ:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="2581975870429850549">"ການຕັ້ງຄ່າການປ່ຽນຮູບແບບລະຫັດມີເດຍ"</string> - <string name="transcode_enable_all" msgid="9102460144086871903">"ປິດການນຳໃຊ້ການປ່ຽນຮູບແບບລະຫັດ"</string> - <string name="transcode_skip_apps" msgid="8249721984597390142">"ເປີດການນຳໃຊ້ການປ່ຽນຮູບແບບລະຫັດສຳລັບແອັບ"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"ການຕັ້ງຄ່າການປ່ຽນຮູບແບບລະຫັດມີເດຍ"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ປິດການນຳໃຊ້ການປ່ຽນຮູບແບບລະຫັດ"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ເປີດການນຳໃຊ້ການປ່ຽນຮູບແບບລະຫັດສຳລັບແອັບ"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"ບໍລິການທີ່ເຮັດວຽກຢູ່"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"ເບິ່ງ ແລະຈັດການບໍລິການທີ່ກຳລັງເຮັດວຽກຢູ່ໃນປັດຈຸບັນ"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"ການຈັດຕັ້ງປະຕິບັດ WebView"</string> @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ຈົນກວ່າຈະສາກເຕັມ"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈົນກວ່າຈະສາກເຕັມ"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ຈຳກັດແບັດເຕີຣີຊົ່ວຄາວ"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ບໍ່ຮູ້ຈັກ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ກຳລັງສາກໄຟ"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ກຳລັງສາກໄຟດ່ວນ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 181fb10dd845..e62eeb2dbd97 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Iki visiškos įkrovos liko <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – iki visiškos įkrovos liko <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – akumuliatorius laikinai apribotas"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Nežinomas"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Kraunasi..."</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Greitai įkraunama"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 717e2b0c89d8..2f305612a5fc 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Vēl <xliff:g id="TIME">%1$s</xliff:g> līdz pilnai uzlādei"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai uzlādei"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>, akumulatora uzlāde pagaidām ierobežota"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Nezināms"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Uzlāde"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Notiek ātrā uzlāde"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 2ccb150b8ca3..1936fe2d32a4 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Уште <xliff:g id="TIME">%1$s</xliff:g> до целосно полнење"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> до целосно полнење"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батеријата е привремено ограничена"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Непознато"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Се полни"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо полнење"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 768ad5e70698..de4a49a2ed06 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -399,12 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"നിഷ്ക്രിയം. മാറ്റുന്നതിനു ടാപ്പുചെയ്യുക."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"സജീവം. മാറ്റുന്നതിന് ടാപ്പുചെയ്യുക."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"ആപ്പ് സ്റ്റാൻഡ്ബൈ നില:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <!-- no translation found for transcode_settings_title (2581975870429850549) --> - <skip /> - <!-- no translation found for transcode_enable_all (9102460144086871903) --> - <skip /> - <!-- no translation found for transcode_skip_apps (8249721984597390142) --> - <skip /> + <string name="transcode_settings_title" msgid="2581975870429850549">"മീഡിയ ട്രാൻസ്കോഡ് ചെയ്യൽ ക്രമീകരണം"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ട്രാൻസ്കോഡ് ചെയ്യൽ പ്രവർത്തനരഹിതമാക്കുക"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ആപ്പുകൾക്കായി ട്രാൻസ്കോഡ് ചെയ്യുന്നത് പ്രവർത്തനക്ഷമമാക്കുക"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"പ്രവർത്തിക്കുന്ന സേവനങ്ങൾ"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"നിലവിൽ പ്രവർത്തിക്കുന്ന സേവനങ്ങൾ കാണുക, നിയന്ത്രിക്കുക"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView നടപ്പാക്കൽ"</string> @@ -452,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"പൂർണ്ണമായി ചാർജാവാൻ <xliff:g id="TIME">%1$s</xliff:g> ശേഷിക്കുന്നു"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - പൂർണ്ണമായി ചാർജാവാൻ <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ബാറ്ററി താൽക്കാലം പരിമിതപ്പെടുത്തി"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"അജ്ഞാതം"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ചാർജ് ചെയ്യുന്നു"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"അതിവേഗ ചാർജിംഗ്"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 61ebca5992b8..dee8add685f9 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Цэнэглэх хүртэл үлдсэн <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - цэнэглэх хүртэл <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батарейг түр хугацаанд хязгаарласан"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Тодорхойгүй"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Цэнэглэж байна"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хурдан цэнэглэж байна"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index ef319c3a4fc1..60ad68e42c09 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -399,12 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"निष्क्रिय. टॉगल करण्यासाठी टॅप करा."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"सक्रिय. टॉगल करण्यासाठी टॅप करा."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"अॅप स्टँडबाय स्थिती: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <!-- no translation found for transcode_settings_title (2581975870429850549) --> - <skip /> - <!-- no translation found for transcode_enable_all (9102460144086871903) --> - <skip /> - <!-- no translation found for transcode_skip_apps (8249721984597390142) --> - <skip /> + <string name="transcode_settings_title" msgid="2581975870429850549">"मीडिया ट्रान्सकोडिंगची सेटिंग्ज"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ट्रान्सकोडिंग बंद करा"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ॲप्ससाठी ट्रान्सकोडिंग सुरू करा"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"सुरू सेवा"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"सध्या सुरू असलेल्या सेवा पहा आणि नियंत्रित करा"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"वेबदृश्य अंमलबजावणी"</string> @@ -452,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> पर्यंत पूर्ण चार्ज होईल"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> पर्यंत पूर्ण चार्ज होईल"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - बॅटरी तात्पुरती मर्यादित आहे"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज होत आहे"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"वेगाने चार्ज होत आहे"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 83c038453265..e4d591207937 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> lagi sehingga dicas penuh"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga dicas"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Bateri terhad untuk sementara"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Tidak diketahui"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Mengecas"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mengecas dgn cepat"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index f398487856bf..09a8bf87700b 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"အားပြည့်ရန် <xliff:g id="TIME">%1$s</xliff:g> ကျန်သည်"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားပြည့်ရန် <xliff:g id="TIME">%2$s</xliff:g> ကျန်သည်"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ဘက်ထရီ ယာယီကန့်သတ်ထားသည်"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"မသိ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"အားသွင်းနေပါသည်"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"အမြန် အားသွင်းနေသည်"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 0ef5809e2a6d..d1af28940f1b 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> til batteriet er fulladet"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til batteriet er fulladet"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Batteriet er midlertidig begrenset"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Ukjent"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Lader"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Lader raskt"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index b956e2104c82..0c6114b9e220 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -399,12 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"निष्क्रिय। टगल गर्न ट्याप गर्नुहोस्।"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"सक्रिय। टगल गर्न ट्याप गर्नुहोस्।"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"एपको स्ट्यान्डबाई अवस्था:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <!-- no translation found for transcode_settings_title (2581975870429850549) --> - <skip /> - <!-- no translation found for transcode_enable_all (9102460144086871903) --> - <skip /> - <!-- no translation found for transcode_skip_apps (8249721984597390142) --> - <skip /> + <string name="transcode_settings_title" msgid="2581975870429850549">"मिडिया ट्रान्सकोडिङ सेटिङ"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ट्रान्सकोडिङ अफ गर्नुहोस्"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"एपहरूमा ट्रान्सकोडिङ अन गर्नुहोस्"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"चलिरहेका सेवाहरू"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"हाल चालु भइरहेका सेवाहरू हेर्नुहोस् र नियन्त्रण गर्नुहोस्"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView कार्यान्वयन"</string> @@ -452,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"पूर्ण चार्ज हुन <xliff:g id="TIME">%1$s</xliff:g> बाँकी"</string> <string name="power_charging_duration" msgid="5005740040558984057">"पूर्ण चार्ज हुन <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> लाग्छ"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - केही समयका लागि ब्याट्री प्रयोग सीमित गरिएको छ"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज हुँदै"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"द्रुत गतिमा चार्ज गरिँदै"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 3223857a0949..a409756e5b5f 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Nog <xliff:g id="TIME">%1$s</xliff:g> tot opgeladen"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot opgeladen"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batterij tijdelijk beperkt"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Onbekend"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Opladen"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Snel opladen"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 11bb550caa49..0a0eeff6de60 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -399,12 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"ନିଷ୍କ୍ରିୟ। ଟୋଗଲ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"ସକ୍ରିୟ। ବଦଳାଇବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"ଆପ୍ ଷ୍ଟାଣ୍ଡବାଏ ଅବସ୍ଥା:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <!-- no translation found for transcode_settings_title (2581975870429850549) --> - <skip /> - <!-- no translation found for transcode_enable_all (9102460144086871903) --> - <skip /> - <!-- no translation found for transcode_skip_apps (8249721984597390142) --> - <skip /> + <string name="transcode_settings_title" msgid="2581975870429850549">"ମିଡିଆ ଟ୍ରାନ୍ସକୋଡିଂ ସେଟିଂସ୍"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ଟ୍ରାନ୍ସକୋଡିଂ ଅକ୍ଷମ କରନ୍ତୁ"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ଆପଗୁଡ଼ିକ ପାଇଁ ଟ୍ରାନ୍ସକୋଡିଂ ସକ୍ଷମ କରନ୍ତୁ"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"ଚାଲୁଥିବା ସେବାଗୁଡ଼ିକ"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"ଏବେ ଚାଲୁଥିବା ସେବାଗୁଡ଼ିକୁ ଦେଖନ୍ତୁ ଓ ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"ୱେବ୍ଭ୍ୟୁ ପ୍ରୟୋଗ"</string> @@ -452,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ଚାର୍ଜ ହେବା ପାଇଁ <xliff:g id="TIME">%1$s</xliff:g> ବାକି ଅଛି"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ଚାର୍ଜ ହେବା ପର୍ଯ୍ୟନ୍ତ"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ବ୍ୟାଟେରୀ ଅସ୍ଥାୟୀ ଭାବେ ସୀମିତ ଅଛି"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ଅଜ୍ଞାତ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ଚାର୍ଜ ହେଉଛି"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ଶୀଘ୍ର ଚାର୍ଜ ହେଉଛି"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 8d66f3ca4adc..c9c37d922537 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -399,12 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"ਅਕਿਰਿਆਸ਼ੀਲ। ਟੌਗਲ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"ਕਿਰਿਆਸ਼ੀਲ। ਟੌਗਲ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"ਐਪ ਸਟੈਂਡਬਾਈ ਸਥਿਤੀ:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <!-- no translation found for transcode_settings_title (2581975870429850549) --> - <skip /> - <!-- no translation found for transcode_enable_all (9102460144086871903) --> - <skip /> - <!-- no translation found for transcode_skip_apps (8249721984597390142) --> - <skip /> + <string name="transcode_settings_title" msgid="2581975870429850549">"ਮੀਡੀਆ ਟ੍ਰਾਂਸਕੋਡਿੰਗ ਸੈਟਿੰਗਾਂ"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ਟ੍ਰਾਂਸਕੋਡਿੰਗ ਬੰਦ ਕਰੋ"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ਐਪਾਂ ਲਈ ਟ੍ਰਾਂਸਕੋਡਿੰਗ ਚਾਲੂ ਕਰੋ"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"ਚੱਲ ਰਹੀਆਂ ਸੇਵਾਵਾਂ"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"ਇਸ ਵੇਲੇ ਚੱਲ ਰਹੀਆਂ ਸੇਵਾਵਾਂ ਦੇਖੋ ਅਤੇ ਇਹਨਾਂ ਨੂੰ ਕੰਟਰੋਲ ਕਰੋ"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ਅਮਲ"</string> @@ -452,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ਚਾਰਜ ਹੋਣ ਵਿੱਚ <xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ਤੱਕ ਚਾਰਜ ਹੋ ਜਾਵੇਗੀ"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਬੈਟਰੀ ਕੁਝ ਸਮੇਂ ਲਈ ਸੀਮਤ"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ਅਗਿਆਤ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ਤੇਜ਼ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index ed96c46848d9..e894c2cbc34c 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Do naładowania <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – do naładowania <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – bateria tymczasowo ograniczona"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Nieznane"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Ładowanie"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Szybkie ładowanie"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 46d6d0a89f5e..57a0454f3465 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Au mai rămas <xliff:g id="TIME">%1$s</xliff:g> până la încărcare"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> până la încărcare"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – baterie limitată temporar"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Necunoscut"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Se încarcă"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Se încarcă rapid"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 2a79ad3005ff..09b8de04388b 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> до полной зарядки"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> • Уровень заряда временно ограничен"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Неизвестно"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Идет зарядка"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Быстрая зарядка"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 78f355865ced..d36140f66b54 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ආරෝපණය වන තෙක් <xliff:g id="TIME">%1$s</xliff:g> ඇත"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණය වන තෙක් <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - බැටරිය තාවකාලිකව සීමිතයි"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"නොදනී"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ආරෝපණය වෙමින්"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ශීඝ්ර ආරෝපණය"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index 152c7a885457..096e95e7050b 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Zostávajúci čas do úplného nabitia: <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Batéria je dočasne obmedzená"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Neznáme"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Nabíja sa"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Rýchle nabíjanie"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index a2f093e89ad8..345fa36d3bd6 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Še <xliff:g id="TIME">%1$s</xliff:g> do polne napolnjenosti"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do polne napolnjenosti"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – baterija je začasno omejena"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Neznano"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Polnjenje"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hitro polnjenje"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index b4589173f234..3953a6a4f6e0 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> të mbetura deri në karikim"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> derisa të karikohet"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Bateria e kufizuar përkohësisht"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"I panjohur"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Po karikohet"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Po ngarkon me shpejtësi"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 970c70f48c68..70b287933c93 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Напуниће се за <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – напуниће се за <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – батерија је тренутно ограничена"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Непознато"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Пуни се"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо се пуни"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 42d907734e30..f466e882c2fc 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> kvar till full laddning"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> till full laddning"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – batteriet är tillfälligt begränsat"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Okänd"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Laddar"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laddas snabbt"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index f1a41994d2c8..e1060fec2a66 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Imebakisha <xliff:g id="TIME">%1$s</xliff:g> ijae chaji"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ijae chaji"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Betri imedhibitiwa kwa muda"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Haijulikani"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Inachaji"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Inachaji kwa kasi"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 2d696c5f79eb..ba98d79ff7e4 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"முழு சார்ஜாக <xliff:g id="TIME">%1$s</xliff:g> ஆகும்"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - முழு சார்ஜாக <xliff:g id="TIME">%2$s</xliff:g> ஆகும்"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>-பேட்டரி தற்காலிகக் கட்டுப்பாட்டிலுள்ளது"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"அறியப்படாத"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"சார்ஜ் ஆகிறது"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"வேகமாக சார்ஜாகிறது"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 8e0f0b603523..0b93979dbd07 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ఛార్జ్ అవ్వడానికి <xliff:g id="TIME">%1$s</xliff:g> సమయం మిగిలి ఉంది"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ఛార్జ్ అవ్వడానికి <xliff:g id="TIME">%2$s</xliff:g> పడుతుంది"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> -బ్యాటరీ తాత్కాలికంగా పరిమితం చేయబడింది"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"తెలియదు"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ఛార్జ్ అవుతోంది"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"వేగవంతమైన ఛార్జింగ్"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 81895b923a44..a31e14611bf6 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"เหลือ <xliff:g id="TIME">%1$s</xliff:g> จนกว่าจะชาร์จเต็ม"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> จนกว่าจะชาร์จ"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - การชาร์จแบตเตอรี่จำกัดชั่วคราว"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ไม่ทราบ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"กำลังชาร์จ"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"กำลังชาร์จอย่างเร็ว"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 5d1630f28675..bbb77790e68a 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ang natitira bago matapos mag-charge"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> hanggang matapos mag-charge"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pansamantalang limitado ang baterya"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Hindi Kilala"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Nagcha-charge"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mabilis na charge"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 34c96a815616..fba7f41bbbf5 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Şarj olmaya <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - şarj olmaya <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pil geçici olarak sınırlı"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Bilinmiyor"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Şarj oluyor"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hızlı şarj oluyor"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index de2456cce092..b633752d28a9 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> до повного заряду"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного заряду"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – дані акумулятора тимчасово недоступні"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Невідомо"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Заряджається"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Швидке заряджання"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 7842ba9bcdbf..b4d8ad2576bd 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -399,12 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"غیر فعال۔ ٹوگل کرنے کیلئے تھپتھپائیں۔"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"فعال۔ ٹوگل کرنے کیلئے تھپتھپائیں۔"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"ایپ اسٹینڈ بائی کی حالت:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <!-- no translation found for transcode_settings_title (2581975870429850549) --> - <skip /> - <!-- no translation found for transcode_enable_all (9102460144086871903) --> - <skip /> - <!-- no translation found for transcode_skip_apps (8249721984597390142) --> - <skip /> + <string name="transcode_settings_title" msgid="2581975870429850549">"میڈیا ٹرانسکوڈنگ کی ترتیبات"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ٹرانسکوڈنگ غیر فعال کریں"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ایپس کے لئے ٹرانسکوڈنگ فعال کریں"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"چل رہی سروسز"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"فی الحال چل رہی سروسز دیکھیں اور انہیں کنٹرول کریں"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView کا نفاذ"</string> @@ -452,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"چارج ہونے میں <xliff:g id="TIME">%1$s</xliff:g> باقی"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> چارج ہونے تک"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - بیٹری عارضی طور پر محدود ہے"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"نامعلوم"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"چارج ہو رہا ہے"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"تیزی سے چارج ہو رہا ہے"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 9457d9001a88..5bb422ecf96a 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ichida toʻladi"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> ichida toʻladi"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Quvvat darajasi vaqtincha cheklangan"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Noma’lum"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Quvvat olmoqda"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Tezkor quvvat olmoqda"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 235987c423d5..0acf9418b718 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Còn <xliff:g id="TIME">%1$s</xliff:g> nữa là sạc đầy"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> nữa là sạc đầy"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Thời lượng pin bị hạn chế tạm thời"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Không xác định"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Đang sạc"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Đang sạc nhanh"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 46188e9859e6..ca2ad87fb864 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"还剩 <xliff:g id="TIME">%1$s</xliff:g>充满电"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>后充满电"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - 暂时限用电池"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"未知"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"正在充电"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"正在快速充电"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 6f5c0253e94f..16ca19d313c1 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"未啟用。輕按即可切換。"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"已啟用。輕按即可切換。"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"備用應用程式狀態:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="2581975870429850549">"媒體轉碼功能設定"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"媒體轉碼設定"</string> <string name="transcode_enable_all" msgid="9102460144086871903">"停用轉碼功能"</string> - <string name="transcode_skip_apps" msgid="8249721984597390142">"替應用程式啟用轉碼功能"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"為應用程式啟用轉碼功能"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"執行中的服務"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"查看並控制目前正在執行中的服務"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView 設置"</string> @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"還需 <xliff:g id="TIME">%1$s</xliff:g>才能充滿電"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - 還需 <xliff:g id="TIME">%2$s</xliff:g>才能充滿電"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - 暫時限制電池充電"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"未知"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"正在快速充電"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index f966df68e0c3..810119b97777 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g>後充飽電"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽電"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - 暫時限制電池用量"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"不明"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"快速充電中"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 8ad37d330bc4..23beeb1aee82 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> esele ize ishaje"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ize igcwale"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ibhethri ikhawulelwe okwesikhashana"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Akwaziwa"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Iyashaja"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ishaja ngokushesha"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java index 9f16d033aea5..ac20ee14ced2 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java @@ -408,7 +408,8 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { } /** - * Checks if an admin has enforced minimum password quality requirements on the given user. + * Checks if an admin has enforced minimum password quality or complexity requirements on the + * given user. * * @return EnforcedAdmin Object containing the enforced admin component and admin user details, * or {@code null} if no quality requirements are set. If the requirements are set by @@ -428,6 +429,30 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { } LockPatternUtils lockPatternUtils = new LockPatternUtils(context); + final int aggregatedComplexity = dpm.getAggregatedPasswordComplexityForUser(userId); + if (aggregatedComplexity > DevicePolicyManager.PASSWORD_COMPLEXITY_NONE) { + // First, check if there's a Device Owner. If so, then only it can apply password + // complexity requiremnts (there can be no secondary profiles). + final UserHandle deviceOwnerUser = dpm.getDeviceOwnerUser(); + if (deviceOwnerUser != null) { + return new EnforcedAdmin(dpm.getDeviceOwnerComponentOnAnyUser(), deviceOwnerUser); + } + + // The complexity could be enforced by a Profile Owner - either in the current user + // or the current user is the parent user that is affected by the profile owner. + for (UserInfo userInfo : UserManager.get(context).getProfiles(userId)) { + final ComponentName profileOwnerComponent = dpm.getProfileOwnerAsUser(userInfo.id); + if (profileOwnerComponent != null) { + return new EnforcedAdmin(profileOwnerComponent, getUserHandleOf(userInfo.id)); + } + } + + // Should not get here: A Device Owner or Profile Owner should be found. + throw new IllegalStateException( + String.format("Could not find admin enforcing complexity %d for user %d", + aggregatedComplexity, userId)); + } + if (sProxy.isSeparateProfileChallengeEnabled(lockPatternUtils, userId)) { // userId is managed profile and has a separate challenge, only consider // the admins in that user. diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java index 59d8acb82196..8fd1910041c8 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java @@ -49,6 +49,7 @@ import java.util.concurrent.CopyOnWriteArrayList; */ public class BluetoothEventManager { private static final String TAG = "BluetoothEventManager"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final LocalBluetoothAdapter mLocalAdapter; private final CachedBluetoothDeviceManager mDeviceManager; @@ -366,6 +367,9 @@ public class BluetoothEventManager { * BluetoothDevice.UNBOND_REASON_* */ private void showUnbondMessage(Context context, String name, int reason) { + if (DEBUG) { + Log.d(TAG, "showUnbondMessage() name : " + name + ", reason : " + reason); + } int errorMsg; switch (reason) { @@ -382,6 +386,7 @@ public class BluetoothEventManager { case BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT: case BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS: case BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED: + case BluetoothDevice.UNBOND_REASON_REMOVED: errorMsg = R.string.bluetooth_pairing_error_message; break; default: 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 ba1dc64ce1e6..6a4d650ebf31 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 @@ -35,6 +35,8 @@ import android.content.IntentFilter; import android.os.UserHandle; import android.telephony.TelephonyManager; +import com.android.settingslib.R; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -49,6 +51,8 @@ import java.util.List; @RunWith(RobolectricTestRunner.class) public class BluetoothEventManagerTest { + private static final String DEVICE_NAME = "test_device_name"; + @Mock private LocalBluetoothAdapter mLocalAdapter; @Mock @@ -71,6 +75,8 @@ public class BluetoothEventManagerTest { private BluetoothDevice mDevice2; @Mock private LocalBluetoothProfileManager mLocalProfileManager; + @Mock + private BluetoothUtils.ErrorListener mErrorListener; private Context mContext; private Intent mIntent; @@ -92,6 +98,7 @@ public class BluetoothEventManagerTest { mCachedDevice1 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1); mCachedDevice2 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2); + BluetoothUtils.setErrorListener(mErrorListener); } @Test @@ -344,4 +351,80 @@ public class BluetoothEventManagerTest { assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse(); assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse(); } + + @Test + public void showUnbondMessage_reasonRemoved_showCorrectedErrorCode() { + mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); + mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); + mIntent.putExtra(BluetoothDevice.EXTRA_REASON, BluetoothDevice.UNBOND_REASON_REMOVED); + when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1); + when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME); + + mContext.sendBroadcast(mIntent); + + verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME), + eq(R.string.bluetooth_pairing_error_message)); + } + + @Test + public void showUnbondMessage_reasonAuthTimeout_showCorrectedErrorCode() { + mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); + mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); + mIntent.putExtra(BluetoothDevice.EXTRA_REASON, BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT); + when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1); + when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME); + + mContext.sendBroadcast(mIntent); + + verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME), + eq(R.string.bluetooth_pairing_error_message)); + } + + @Test + public void showUnbondMessage_reasonRemoteDeviceDown_showCorrectedErrorCode() { + mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); + mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); + mIntent.putExtra(BluetoothDevice.EXTRA_REASON, + BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN); + when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1); + when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME); + + mContext.sendBroadcast(mIntent); + + verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME), + eq(R.string.bluetooth_pairing_device_down_error_message)); + } + + @Test + public void showUnbondMessage_reasonAuthRejected_showCorrectedErrorCode() { + mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); + mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); + mIntent.putExtra(BluetoothDevice.EXTRA_REASON, BluetoothDevice.UNBOND_REASON_AUTH_REJECTED); + when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1); + when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME); + + mContext.sendBroadcast(mIntent); + + verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME), + eq(R.string.bluetooth_pairing_rejected_error_message)); + } + + @Test + public void showUnbondMessage_reasonAuthFailed_showCorrectedErrorCode() { + mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); + mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); + mIntent.putExtra(BluetoothDevice.EXTRA_REASON, BluetoothDevice.UNBOND_REASON_AUTH_FAILED); + when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1); + when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME); + + mContext.sendBroadcast(mIntent); + + verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME), + eq(R.string.bluetooth_pairing_pin_error_message)); + } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java b/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java index 5e5a9d9b2ec8..c33f02dff561 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java @@ -82,6 +82,7 @@ public class WifiSoftApConfigChangedNotifier { private static PendingIntent getPendingActivity(Context context) { Intent intent = new Intent("com.android.settings.WIFI_TETHER_SETTINGS") .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + return PendingIntent.getActivity(context, 0, intent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); } } diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index af12ddd8895b..48c0dc4fb2b8 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -319,7 +319,6 @@ public class SettingsBackupTest { Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS, Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST, Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST, - Settings.Global.LOCATION_GLOBAL_KILL_SWITCH, Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED, Settings.Global.LOCK_SOUND, Settings.Global.LOOPER_STATS, @@ -576,8 +575,6 @@ public class SettingsBackupTest { Settings.Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED, Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS, Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS, - Settings.Global.ISOLATED_STORAGE_LOCAL, - Settings.Global.ISOLATED_STORAGE_REMOTE, Settings.Global.APPOP_HISTORY_PARAMETERS, Settings.Global.APPOP_HISTORY_MODE, Settings.Global.APPOP_HISTORY_INTERVAL_MULTIPLIER, diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 2a699ea45abe..2e3ea24f62e2 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -70,6 +70,7 @@ <uses-permission android:name="android.permission.SET_PROCESS_LIMIT" /> <uses-permission android:name="android.permission.SET_ALWAYS_FINISH" /> <uses-permission android:name="android.permission.DUMP" /> + <uses-permission android:name="android.permission.CONTROL_UI_TRACING" /> <uses-permission android:name="android.permission.SIGNAL_PERSISTENT_PROCESSES" /> <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" /> <!-- Internal permissions granted to the shell. --> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 7120cc21c821..52b41a43c63e 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -530,6 +530,14 @@ androidprv:alwaysFocusable="true" android:excludeFromRecents="true" /> + <!-- started from TvNotificationPanel --> + <activity + android:name=".statusbar.tv.notifications.TvNotificationPanelActivity" + android:excludeFromRecents="true" + android:launchMode="singleTask" + android:noHistory="true" + android:theme="@style/TvSidePanelTheme" /> + <!-- started from SliceProvider --> <activity android:name=".SlicePermissionActivity" android:theme="@style/Theme.SystemUI.Dialog.Alert" diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS index 17d2f9c89c30..f884270eaba8 100644 --- a/packages/SystemUI/OWNERS +++ b/packages/SystemUI/OWNERS @@ -2,8 +2,11 @@ set noparent dsandler@android.com +aaliomer@google.com adamcohen@google.com +alexflo@google.com asc@google.com +awickham@google.com beverlyt@google.com brockman@google.com cinek@google.com @@ -11,11 +14,16 @@ cwren@google.com dupin@google.com ethibodeau@google.com evanlaird@google.com +gwasserman@google.com hwwang@google.com hyunyoungs@google.com jaggies@google.com +jamesoleary@google.com +jeffdq@google.com jjaggi@google.com +jonmiranda@google.com joshmcgrath@google.com +joshtrask@google.com juliacr@google.com juliatuttle@google.com kchyn@google.com @@ -24,28 +32,38 @@ kprevas@google.com lynhan@google.com madym@google.com mankoff@google.com +mett@google.com +mkephart@google.com +mpietal@google.com mrcasey@google.com mrenouf@google.com nbenbernou@google.com nesciosquid@google.com ogunwale@google.com peanutbutter@google.com +pinyaoting@google.com pixel@google.com roosa@google.com +santie@google.com snoeberger@google.com +sreyasr@google.com steell@google.com +sfufa@google.com stwu@google.com sunnygoyal@google.com susikp@google.com +thiruram@google.com tracyzhou@google.com tsuji@google.com twickham@google.com +vadimt@google.com +victortulias@google.com winsonc@google.com +xuqiu@google.com zakcohen@google.com #Android Auto hseog@google.com #Android TV -rgl@google.com - +rgl@google.com
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/drawable/circle_white.xml b/packages/SystemUI/res-keyguard/drawable/circle_white.xml new file mode 100644 index 000000000000..d1b20979c29e --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/circle_white.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="oval"> + <solid android:color="#33FFFFFF" /> +</shape>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index b75c2c4cea6c..c82bda6620bd 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -95,7 +95,7 @@ android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:gravity="center_horizontal" - android:textSize="170dp" + android:textSize="180dp" android:letterSpacing="0.02" android:lineSpacingMultiplier=".8" android:includeFontPadding="false" diff --git a/packages/SystemUI/res/layout/disabled_udfps_view.xml b/packages/SystemUI/res/layout/disabled_udfps_view.xml new file mode 100644 index 000000000000..aab86613ef45 --- /dev/null +++ b/packages/SystemUI/res/layout/disabled_udfps_view.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<com.android.keyguard.DisabledUdfpsView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res-auto" + android:id="@+id/disabled_udfps_view" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@drawable/circle_white" +/> diff --git a/packages/SystemUI/res/layout/global_actions_grid_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_v2.xml index 7d45de3fa50d..30ffc32ce1f8 100644 --- a/packages/SystemUI/res/layout/global_actions_grid_v2.xml +++ b/packages/SystemUI/res/layout/global_actions_grid_v2.xml @@ -1,67 +1,15 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/global_actions_container" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > - <com.android.systemui.globalactions.GlobalActionsFlatLayout - android:id="@id/global_actions_view" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:theme="@style/qs_theme" - android:clipChildren="false" - android:clipToPadding="false" - android:layout_marginStart="@dimen/global_actions_side_margin" - > - <LinearLayout - android:id="@android:id/list" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="@dimen/global_actions_grid_vertical_padding" - android:paddingBottom="@dimen/global_actions_grid_vertical_padding" - android:orientation="horizontal" - android:gravity="left | center_vertical" - android:translationZ="@dimen/global_actions_translate" - > - <RelativeLayout - android:id="@+id/global_actions_overflow_button" - android:contentDescription="@string/accessibility_menu" - android:layout_width="48dp" - android:layout_height="48dp" - > - <ImageView - android:src="@drawable/ic_more_vert" - android:layout_centerInParent="true" - android:layout_width="24dp" - android:layout_height="24dp" - android:tint="@color/control_more_vert" - /> - </RelativeLayout> - </LinearLayout> - </com.android.systemui.globalactions.GlobalActionsFlatLayout> - <androidx.constraintlayout.widget.ConstraintLayout - android:id="@+id/global_actions_lock_message_container" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:visibility="gone"> - <TextView - android:id="@+id/global_actions_lock_message" - style="@style/TextAppearance.Control.Title" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginHorizontal="@dimen/global_actions_side_margin" - android:drawablePadding="12dp" - android:gravity="center" - android:text="@string/global_action_lock_message" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintVertical_bias="0.35"/> - </androidx.constraintlayout.widget.ConstraintLayout> + <include layout="@layout/global_actions_view" /> + + <include layout="@layout/global_actions_lock_view" /> <com.android.systemui.globalactions.MinHeightScrollView android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout/global_actions_lock_view.xml b/packages/SystemUI/res/layout/global_actions_lock_view.xml new file mode 100644 index 000000000000..eccc63688065 --- /dev/null +++ b/packages/SystemUI/res/layout/global_actions_lock_view.xml @@ -0,0 +1,35 @@ +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<androidx.constraintlayout.widget.ConstraintLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/global_actions_lock_message_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:visibility="gone"> + <TextView + android:id="@+id/global_actions_lock_message" + style="@style/TextAppearance.Control.Title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="@dimen/global_actions_side_margin" + android:drawablePadding="12dp" + android:gravity="center" + android:text="@string/global_action_lock_message" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="0.35"/> +</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/global_actions_view.xml b/packages/SystemUI/res/layout/global_actions_view.xml new file mode 100644 index 000000000000..454707bc44e2 --- /dev/null +++ b/packages/SystemUI/res/layout/global_actions_view.xml @@ -0,0 +1,52 @@ +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<com.android.systemui.globalactions.GlobalActionsFlatLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@id/global_actions_view" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:theme="@style/qs_theme" + android:clipChildren="false" + android:clipToPadding="false" + android:layout_marginStart="@dimen/global_actions_side_margin" + > + <LinearLayout + android:id="@android:id/list" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="@dimen/global_actions_grid_vertical_padding" + android:paddingBottom="@dimen/global_actions_grid_vertical_padding" + android:orientation="horizontal" + android:gravity="left | center_vertical" + android:translationZ="@dimen/global_actions_translate" + > + <RelativeLayout + android:id="@+id/global_actions_overflow_button" + android:contentDescription="@string/accessibility_menu" + android:layout_width="48dp" + android:layout_height="48dp" + > + <ImageView + android:src="@drawable/ic_more_vert" + android:layout_centerInParent="true" + android:layout_width="24dp" + android:layout_height="24dp" + android:tint="@color/control_more_vert" + /> + </RelativeLayout> + </LinearLayout> +</com.android.systemui.globalactions.GlobalActionsFlatLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/quick_settings_footer.xml b/packages/SystemUI/res/layout/quick_settings_footer.xml index e7c7b5fbf890..13572fa9f4f3 100644 --- a/packages/SystemUI/res/layout/quick_settings_footer.xml +++ b/packages/SystemUI/res/layout/quick_settings_footer.xml @@ -26,11 +26,19 @@ android:layout_gravity="center_vertical|center_horizontal" android:background="@android:color/transparent"> + <ImageView + android:id="@+id/primary_footer_icon" + android:layout_width="@dimen/qs_footer_icon_size" + android:layout_height="@dimen/qs_footer_icon_size" + android:gravity="start" + android:layout_marginEnd="8dp" + android:contentDescription="@null" + android:tint="?android:attr/textColorPrimary" /> + <com.android.systemui.util.AutoMarqueeTextView android:id="@+id/footer_text" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:gravity="start" android:layout_weight="1" android:singleLine="true" android:ellipsize="marquee" diff --git a/packages/SystemUI/res/layout/quick_settings_footer_dialog_parental_controls.xml b/packages/SystemUI/res/layout/quick_settings_footer_dialog_parental_controls.xml index 1a356769d334..3e40321aba42 100644 --- a/packages/SystemUI/res/layout/quick_settings_footer_dialog_parental_controls.xml +++ b/packages/SystemUI/res/layout/quick_settings_footer_dialog_parental_controls.xml @@ -29,8 +29,8 @@ android:orientation="vertical"> <ImageView android:id="@+id/parental_controls_icon" - android:layout_width="36dip" - android:layout_height="36dip" + android:layout_width="24dip" + android:layout_height="24dip" android:layout_gravity="center_horizontal" /> diff --git a/packages/SystemUI/res/layout/tv_notification_item.xml b/packages/SystemUI/res/layout/tv_notification_item.xml new file mode 100644 index 000000000000..711cd4e4258a --- /dev/null +++ b/packages/SystemUI/res/layout/tv_notification_item.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="@dimen/tv_notification_panel_width" + android:layout_height="wrap_content" + android:background="?android:attr/selectableItemBackground" + android:orientation="vertical" + android:padding="12dp"> + + <TextView + android:id="@+id/tv_notification_title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingBottom="12dp" + android:textColor="@color/tv_notification_text_color" + android:textSize="18sp" /> + + <TextView + android:id="@+id/tv_notification_details" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textColor="@color/tv_notification_text_color" /> + +</LinearLayout> diff --git a/packages/SystemUI/res/layout/tv_notification_panel.xml b/packages/SystemUI/res/layout/tv_notification_panel.xml new file mode 100644 index 000000000000..8f00a727b912 --- /dev/null +++ b/packages/SystemUI/res/layout/tv_notification_panel.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/tv_notification_panel" + android:layout_width="@dimen/tv_notification_panel_width" + android:layout_height="match_parent" + android:layout_gravity="end" + android:background="@color/tv_notification_background_color" + android:orientation="vertical"> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="12dp" + android:paddingTop="24dp" + android:text="@string/tv_notification_panel_title" + android:textColor="@color/tv_notification_text_color" + android:textSize="24sp" + android:textStyle="bold" /> + + <TextView + android:id="@+id/no_tv_notifications" + style="?android:attr/titleTextStyle" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + android:gravity="top|center" + android:paddingTop="24dp" + android:text="@string/tv_notification_panel_no_notifications" + android:textColor="@color/tv_notification_text_color" + android:visibility="gone" /> + + <androidx.leanback.widget.VerticalGridView + android:id="@+id/notifications_list" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" /> +</LinearLayout> diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml index ccd235d54c0b..c0788051efed 100644 --- a/packages/SystemUI/res/layout/udfps_view.xml +++ b/packages/SystemUI/res/layout/udfps_view.xml @@ -1,4 +1,19 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> <com.android.systemui.biometrics.UdfpsView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml index 09ec439b183e..0c8c5c443c4a 100644 --- a/packages/SystemUI/res/values-television/config.xml +++ b/packages/SystemUI/res/values-television/config.xml @@ -29,7 +29,8 @@ <item>com.android.systemui.util.NotificationChannels</item> <item>com.android.systemui.volume.VolumeUI</item> <item>com.android.systemui.statusbar.tv.TvStatusBar</item> - <item>com.android.systemui.statusbar.tv.TvNotificationPanel</item> + <item>com.android.systemui.statusbar.tv.notifications.TvNotificationPanel</item> + <item>com.android.systemui.statusbar.tv.notifications.TvNotificationHandler</item> <item>com.android.systemui.statusbar.tv.VpnStatusObserver</item> <item>com.android.systemui.usb.StorageNotification</item> <item>com.android.systemui.power.PowerUI</item> diff --git a/packages/SystemUI/res/values/colors_tv.xml b/packages/SystemUI/res/values/colors_tv.xml index 9b0ae1d61de6..0961f503e7d4 100644 --- a/packages/SystemUI/res/values/colors_tv.xml +++ b/packages/SystemUI/res/values/colors_tv.xml @@ -33,4 +33,7 @@ <color name="tv_volume_dialog_seek_bar_background">#A03C4043</color> <color name="tv_volume_dialog_seek_bar_fill">#FFF8F9FA</color> <color name="tv_volume_dialog_accent">#FFDADCE0</color> + + <color name="tv_notification_background_color">#383838</color> + <color name="tv_notification_text_color">#FFFFFF</color> </resources> diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/packages/SystemUI/res/values/dimens_tv.xml new file mode 100644 index 000000000000..9545bfd088a0 --- /dev/null +++ b/packages/SystemUI/res/values/dimens_tv.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<resources> + <dimen name="tv_notification_panel_width">360dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/res/values/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml index 13271d6f7f2e..b51cb5619f0c 100644 --- a/packages/SystemUI/res/values/strings_tv.xml +++ b/packages/SystemUI/res/values/strings_tv.xml @@ -26,4 +26,6 @@ <!-- Disclosure text in the connected notification that indicates that the device is connected to a VPN. The placeholder is the VPN name. [CHAR LIMIT=40] --> <string name="notification_disclosure_vpn_text">Via <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string> + <string name="tv_notification_panel_title">Notifications</string> + <string name="tv_notification_panel_no_notifications">No Notifications</string> </resources> diff --git a/packages/SystemUI/res/values/styles_tv.xml b/packages/SystemUI/res/values/styles_tv.xml index 0c4fd23ca107..cb433f3a6009 100644 --- a/packages/SystemUI/res/values/styles_tv.xml +++ b/packages/SystemUI/res/values/styles_tv.xml @@ -23,4 +23,12 @@ <item name="android:backgroundDimEnabled">false</item> <item name="android:windowDisablePreview">true</item> </style> + + <style name="TvSidePanelTheme"> + <item name="android:windowIsTranslucent">true</item> + <item name="android:windowBackground">@android:color/transparent</item> + <item name="android:backgroundDimEnabled">false</item> + <item name="android:windowNoTitle">true</item> + <item name="android:windowContentOverlay">@null</item> + </style> </resources> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java index dd57af33b4dc..229d20b6543e 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -20,6 +20,7 @@ import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED; import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; +import static android.app.ActivityTaskManager.getService; import android.annotation.NonNull; import android.app.Activity; @@ -53,7 +54,6 @@ import com.android.internal.app.IVoiceInteractionManagerService; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; -import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; @@ -70,6 +70,7 @@ public class ActivityManagerWrapper { // Should match the value in AssistManager private static final String INVOCATION_TIME_MS_KEY = "invocation_time_ms"; + private final ActivityTaskManager mAtm = ActivityTaskManager.getInstance(); private ActivityManagerWrapper() { } public static ActivityManagerWrapper getInstance() { @@ -102,29 +103,19 @@ public class ActivityManagerWrapper { */ public ActivityManager.RunningTaskInfo getRunningTask(boolean filterOnlyVisibleRecents) { // Note: The set of running tasks from the system is ordered by recency - try { - List<ActivityManager.RunningTaskInfo> tasks = - ActivityTaskManager.getService().getFilteredTasks(1, filterOnlyVisibleRecents); - if (tasks.isEmpty()) { - return null; - } - return tasks.get(0); - } catch (RemoteException e) { + List<ActivityManager.RunningTaskInfo> tasks = + mAtm.getTasks(1, filterOnlyVisibleRecents); + if (tasks.isEmpty()) { return null; } + return tasks.get(0); } /** * @return a list of the recents tasks. */ public List<RecentTaskInfo> getRecentTasks(int numTasks, int userId) { - try { - return ActivityTaskManager.getService().getRecentTasks(numTasks, - RECENT_IGNORE_UNAVAILABLE, userId).getList(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get recent tasks", e); - return new ArrayList<>(); - } + return mAtm.getRecentTasks(numTasks, RECENT_IGNORE_UNAVAILABLE, userId); } /** @@ -133,7 +124,7 @@ public class ActivityManagerWrapper { public @NonNull ThumbnailData getTaskThumbnail(int taskId, boolean isLowResolution) { ActivityManager.TaskSnapshot snapshot = null; try { - snapshot = ActivityTaskManager.getService().getTaskSnapshot(taskId, isLowResolution); + snapshot = getService().getTaskSnapshot(taskId, isLowResolution); } catch (RemoteException e) { Log.w(TAG, "Failed to retrieve task snapshot", e); } @@ -149,8 +140,7 @@ public class ActivityManagerWrapper { */ public void invalidateHomeTaskSnapshot(final Activity homeActivity) { try { - ActivityTaskManager.getService().invalidateHomeTaskSnapshot( - homeActivity.getActivityToken()); + getService().invalidateHomeTaskSnapshot(homeActivity.getActivityToken()); } catch (RemoteException e) { Log.w(TAG, "Failed to invalidate home snapshot", e); } @@ -208,7 +198,7 @@ public class ActivityManagerWrapper { } }; } - ActivityTaskManager.getService().startRecentsActivity(intent, eventTime, runner); + getService().startRecentsActivity(intent, eventTime, runner); return true; } catch (Exception e) { return false; @@ -220,7 +210,7 @@ public class ActivityManagerWrapper { */ public void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition) { try { - ActivityTaskManager.getService().cancelRecentsAnimation(restoreHomeRootTaskPosition); + getService().cancelRecentsAnimation(restoreHomeRootTaskPosition); } catch (RemoteException e) { Log.e(TAG, "Failed to cancel recents animation", e); } @@ -259,7 +249,7 @@ public class ActivityManagerWrapper { public boolean startActivityFromRecents(int taskId, ActivityOptions options) { try { Bundle optsBundle = options == null ? null : options.toBundle(); - ActivityTaskManager.getService().startActivityFromRecents(taskId, optsBundle); + getService().startActivityFromRecents(taskId, optsBundle); return true; } catch (Exception e) { return false; @@ -296,7 +286,7 @@ public class ActivityManagerWrapper { */ public void removeTask(final int taskId) { try { - ActivityTaskManager.getService().removeTask(taskId); + getService().removeTask(taskId); } catch (RemoteException e) { Log.w(TAG, "Failed to remove task=" + taskId, e); } @@ -307,7 +297,7 @@ public class ActivityManagerWrapper { */ public void removeAllRecentTasks() { try { - ActivityTaskManager.getService().removeAllVisibleRecentTasks(); + getService().removeAllVisibleRecentTasks(); } catch (RemoteException e) { Log.w(TAG, "Failed to remove all tasks", e); } @@ -318,7 +308,7 @@ public class ActivityManagerWrapper { */ public boolean isScreenPinningActive() { try { - return ActivityTaskManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED; + return getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED; } catch (RemoteException e) { return false; } @@ -337,7 +327,7 @@ public class ActivityManagerWrapper { */ public boolean isLockToAppActive() { try { - return ActivityTaskManager.getService().getLockTaskModeState() != LOCK_TASK_MODE_NONE; + return getService().getLockTaskModeState() != LOCK_TASK_MODE_NONE; } catch (RemoteException e) { return false; } @@ -348,7 +338,7 @@ public class ActivityManagerWrapper { */ public boolean isLockTaskKioskModeActive() { try { - return ActivityTaskManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_LOCKED; + return getService().getLockTaskModeState() == LOCK_TASK_MODE_LOCKED; } catch (RemoteException e) { return false; } diff --git a/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java new file mode 100644 index 000000000000..f01b67b7b5c2 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.keyguard; + +import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT; + +import android.hardware.biometrics.BiometricSourceType; +import android.view.View; + +import androidx.annotation.NonNull; + +import com.android.systemui.Dumpable; +import com.android.systemui.biometrics.AuthController; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.util.ViewController; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + +/** + * Controls when to show the DisabledUdfpsView to unlock the device on the lockscreen. + * If the device is not authenticated, the bouncer will show. + * + * This tap target will only show when: + * - User has UDFPS enrolled + * - UDFPS is currently unavailable see {@link KeyguardUpdateMonitor#shouldListenForUdfps} + */ +@SysUISingleton +public class DisabledUdfpsController extends ViewController<DisabledUdfpsView> implements Dumpable { + @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @NonNull private final KeyguardViewController mKeyguardViewController; + @NonNull private final StatusBarStateController mStatusBarStateController; + + private boolean mIsDozing; + private boolean mIsBouncerShowing; + private boolean mIsKeyguardShowing; + private boolean mRunningFPS; + private boolean mAuthenticated; + + private boolean mShowButton; + + public DisabledUdfpsController( + @NonNull DisabledUdfpsView view, + @NonNull StatusBarStateController statusBarStateController, + @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor, + @NonNull AuthController authController, + @NonNull KeyguardViewController keyguardViewController + ) { + super(view); + mView.setOnClickListener(mOnClickListener); + mView.setSensorProperties(authController.getUdfpsProps().get(0)); + + mStatusBarStateController = statusBarStateController; + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mKeyguardViewController = keyguardViewController; + } + + @Override + protected void onViewAttached() { + mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback); + mIsBouncerShowing = mKeyguardViewController.isBouncerShowing(); + + mStatusBarStateController.addCallback(mStatusBarStateListener); + mIsKeyguardShowing = mStatusBarStateController.getState() == StatusBarState.KEYGUARD; + mIsDozing = mStatusBarStateController.isDozing(); + mAuthenticated = false; + } + + @Override + protected void onViewDetached() { + mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback); + mStatusBarStateController.removeCallback(mStatusBarStateListener); + } + + private void updateButtonVisibility() { + mShowButton = !mAuthenticated && !mIsDozing && mIsKeyguardShowing + && !mIsBouncerShowing && !mRunningFPS; + if (mShowButton) { + mView.setVisibility(View.VISIBLE); + } else { + mView.setVisibility(View.INVISIBLE); + } + } + + @Override + public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { + pw.println("DisabledUdfpsController state:"); + pw.println(" mShowBouncerButton: " + mShowButton); + pw.println(" mIsDozing: " + mIsDozing); + pw.println(" mIsKeyguardShowing: " + mIsKeyguardShowing); + pw.println(" mIsBouncerShowing: " + mIsBouncerShowing); + pw.println(" mRunningFPS: " + mRunningFPS); + pw.println(" mAuthenticated: " + mAuthenticated); + } + + private final View.OnClickListener mOnClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + mKeyguardViewController.showBouncer(/* scrim */ true); + } + }; + + private StatusBarStateController.StateListener mStatusBarStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onStateChanged(int newState) { + mIsKeyguardShowing = newState == StatusBarState.KEYGUARD; + updateButtonVisibility(); + } + + @Override + public void onDozingChanged(boolean isDozing) { + mIsDozing = isDozing; + updateButtonVisibility(); + } + }; + + private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback = + new KeyguardUpdateMonitorCallback() { + @Override + public void onKeyguardBouncerChanged(boolean bouncer) { + mIsBouncerShowing = bouncer; + updateButtonVisibility(); + } + + @Override + public void onBiometricRunningStateChanged(boolean running, + BiometricSourceType biometricSourceType) { + mRunningFPS = running && biometricSourceType == FINGERPRINT; + updateButtonVisibility(); + } + + @Override + public void onUserUnlocked() { + mAuthenticated = true; + updateButtonVisibility(); + } + }; +} diff --git a/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsView.java b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsView.java new file mode 100644 index 000000000000..d8ab780eb6bd --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsView.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.keyguard; + +import android.annotation.NonNull; +import android.content.Context; +import android.graphics.RectF; +import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.util.AttributeSet; +import android.view.Surface; +import android.widget.Button; +import android.widget.FrameLayout; + +/** + * A full screen view with an oval target where the UDFPS sensor is. + * Controlled by {@link DisabledUdfpsController}. + */ +public class DisabledUdfpsView extends Button { + @NonNull private final RectF mSensorRect; + @NonNull private final Context mContext; + + // Used to obtain the sensor location. + @NonNull private FingerprintSensorPropertiesInternal mSensorProps; + + public DisabledUdfpsView(Context context, AttributeSet attrs) { + super(context, attrs); + mContext = context; + mSensorRect = new RectF(); + } + + public void setSensorProperties(@NonNull FingerprintSensorPropertiesInternal properties) { + mSensorProps = properties; + } + + // The "h" and "w" are the display's height and width relative to its current rotation. + private void updateSensorRect(int h, int w) { + // mSensorProps coordinates assume portrait mode. + mSensorRect.set(mSensorProps.sensorLocationX - mSensorProps.sensorRadius, + mSensorProps.sensorLocationY - mSensorProps.sensorRadius, + mSensorProps.sensorLocationX + mSensorProps.sensorRadius, + mSensorProps.sensorLocationY + mSensorProps.sensorRadius); + + // Transform mSensorRect if the device is in landscape mode. + switch (mContext.getDisplay().getRotation()) { + case Surface.ROTATION_90: + mSensorRect.set(mSensorRect.top, h - mSensorRect.right, mSensorRect.bottom, + h - mSensorRect.left); + break; + case Surface.ROTATION_270: + mSensorRect.set(w - mSensorRect.bottom, mSensorRect.left, w - mSensorRect.top, + mSensorRect.right); + break; + default: + // Do nothing to stay in portrait mode. + } + + setX(mSensorRect.left); + setY(mSensorRect.top); + setLayoutParams(new FrameLayout.LayoutParams( + (int) (mSensorRect.right - mSensorRect.left), + (int) (mSensorRect.bottom - mSensorRect.top))); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + // Always re-compute the layout regardless of whether "changed" is true. It is usually false + // when the device goes from landscape to seascape and vice versa, but mSensorRect and + // its dependencies need to be recalculated to stay at the same physical location on the + // screen. + final int w = getLayoutParams().width; + final int h = getLayoutParams().height; + updateSensorRect(h, w); + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java index a32cd1420fdc..3cbab8e66fdb 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java @@ -244,7 +244,7 @@ public class KeyguardSliceView extends LinearLayout { iconDrawable.setBounds(0, 0, Math.max(width, 1), iconSize); } } - button.setCompoundDrawables(iconDrawable, null, null, null); + button.setCompoundDrawablesRelative(iconDrawable, null, null, null); button.setOnClickListener(mOnClickListener); button.setClickable(pendingIntent != null); } @@ -536,9 +536,9 @@ public class KeyguardSliceView extends LinearLayout { } @Override - public void setCompoundDrawables(Drawable left, Drawable top, Drawable right, + public void setCompoundDrawablesRelative(Drawable start, Drawable top, Drawable end, Drawable bottom) { - super.setCompoundDrawables(left, top, right, bottom); + super.setCompoundDrawablesRelative(start, top, end, bottom); updateDrawableColors(); updatePadding(); } @@ -558,9 +558,9 @@ public class KeyguardSliceView extends LinearLayout { public void setLockScreenMode(int mode) { mLockScreenMode = mode; if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) { - setGravity(Gravity.START); + setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START); } else { - setGravity(Gravity.CENTER); + setTextAlignment(View.TEXT_ALIGNMENT_CENTER); } updatePadding(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 611131f6e216..a7e51951e9cd 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -1925,6 +1925,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } // TODO: Add support for multiple fingerprint sensors, b/173730729 + updateUdfpsEnrolled(getCurrentUser()); boolean shouldListenForFingerprint = isUdfpsEnrolled() ? shouldListenForUdfps() : shouldListenForFingerprint(); boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING @@ -2137,7 +2138,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } if (DEBUG) Log.v(TAG, "startListeningForFingerprint()"); int userId = getCurrentUser(); - updateUdfpsEnrolled(userId); if (isUnlockWithFingerprintPossible(userId)) { if (mFingerprintCancelSignal != null) { mFingerprintCancelSignal.cancel(); @@ -2627,7 +2627,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab Assert.isMainThread(); if (DEBUG) Log.d(TAG, "handleKeyguardBouncerChanged(" + bouncerVisible + ")"); mBouncer = bouncerVisible == 1; - if (mBouncer) { // If the bouncer is shown, always clear this flag. This can happen in the following // situations: 1) Default camera with SHOW_WHEN_LOCKED is not chosen yet. 2) Secure diff --git a/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt b/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt index f5e01dedfe3e..0d41a2f56618 100644 --- a/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt +++ b/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt @@ -19,8 +19,9 @@ import android.graphics.Canvas import android.graphics.Paint import android.graphics.fonts.Font import android.graphics.text.PositionedGlyphs -import android.graphics.text.TextRunShaper import android.text.Layout +import android.text.TextPaint +import android.text.TextShaper import android.util.MathUtils import com.android.internal.graphics.ColorUtils import java.lang.Math.max @@ -57,10 +58,10 @@ class TextInterpolator( */ val targetPaint = createDefaultPaint(layout.paint, lines) - private fun createDefaultPaint(paint: Paint, lines: Int): ArrayList<Paint> { - val paintList = ArrayList<Paint>() + private fun createDefaultPaint(paint: TextPaint, lines: Int): ArrayList<TextPaint> { + val paintList = ArrayList<TextPaint>() for (i in 0 until lines) - paintList.add(Paint(paint)) + paintList.add(TextPaint(paint)) return paintList } @@ -79,9 +80,9 @@ class TextInterpolator( } /** - * A class represents text layout of a single line. + * A class represents text layout of a single run. */ - private class Line( + private class Run( val glyphIds: IntArray, val baseX: FloatArray, // same length as glyphIds val baseY: FloatArray, // same length as glyphIds @@ -90,11 +91,18 @@ class TextInterpolator( val fontRuns: List<FontRun> ) + /** + * A class represents text layout of a single line. + */ + private class Line( + val runs: List<Run> + ) + private var lines = listOf<Line>() private val fontInterpolator = FontInterpolator() // Recycling object for glyph drawing. Will be extended for the longest font run if needed. - private val tmpDrawPaints = ArrayList<Paint>() + private val tmpDrawPaints = ArrayList<TextPaint>() private var tmpPositionArray = FloatArray(20) /** @@ -215,12 +223,14 @@ class TextInterpolator( } lines.forEach { line -> - for (i in line.baseX.indices) { - line.baseX[i] = MathUtils.lerp(line.baseX[i], line.targetX[i], progress) - line.baseY[i] = MathUtils.lerp(line.baseY[i], line.targetY[i], progress) - } - line.fontRuns.forEach { - it.baseFont = fontInterpolator.lerp(it.baseFont, it.targetFont, progress) + line.runs.forEach { run -> + for (i in run.baseX.indices) { + run.baseX[i] = MathUtils.lerp(run.baseX[i], run.targetX[i], progress) + run.baseY[i] = MathUtils.lerp(run.baseY[i], run.targetY[i], progress) + } + run.fontRuns.forEach { + it.baseFont = fontInterpolator.lerp(it.baseFont, it.targetFont, progress) + } } } @@ -228,10 +238,10 @@ class TextInterpolator( } companion object { - fun updatePaint(toUpdate: ArrayList<Paint>, newValues: ArrayList<Paint>) { + fun updatePaint(toUpdate: ArrayList<TextPaint>, newValues: ArrayList<TextPaint>) { toUpdate.clear() for (paint in newValues) - toUpdate.add(Paint(paint)) + toUpdate.add(TextPaint(paint)) } } @@ -243,20 +253,22 @@ class TextInterpolator( fun draw(canvas: Canvas) { lerp(basePaint, targetPaint, progress, tmpDrawPaints) lines.forEachIndexed { lineNo, line -> - canvas.save() - try { - // Move to drawing origin. - val origin = layout.getDrawOrigin(lineNo) - canvas.translate(origin, layout.getLineBaseline(lineNo).toFloat()) - - line.fontRuns.forEach { run -> - if (lineNo >= tmpDrawPaints.size) - drawFontRun(canvas, line, run, tmpDrawPaints[0]) - else - drawFontRun(canvas, line, run, tmpDrawPaints[lineNo]) + line.runs.forEach { run -> + canvas.save() + try { + // Move to drawing origin. + val origin = layout.getDrawOrigin(lineNo) + canvas.translate(origin, layout.getLineBaseline(lineNo).toFloat()) + + run.fontRuns.forEach { fontRun -> + if (lineNo >= tmpDrawPaints.size) + drawFontRun(canvas, run, fontRun, tmpDrawPaints[0]) + else + drawFontRun(canvas, run, fontRun, tmpDrawPaints[lineNo]) + } + } finally { + canvas.restore() } - } finally { - canvas.restore() } } } @@ -271,64 +283,68 @@ class TextInterpolator( } var maxRunLength = 0 - lines = baseLayout.zip(targetLayout) { base, target -> - require(base.glyphCount() == target.glyphCount()) { - "Inconsistent glyph count at line ${lines.size}" - } - - val glyphCount = base.glyphCount() + lines = baseLayout.zip(targetLayout) { baseLine, targetLine -> + val runs = baseLine.zip(targetLine) { base, target -> - // Good to recycle the array if the existing array can hold the new layout result. - val glyphIds = IntArray(glyphCount) { - base.getGlyphId(it).also { baseGlyphId -> - require(baseGlyphId == target.getGlyphId(it)) { - "Inconsistent glyph ID at $it in line ${lines.size}" - } + require(base.glyphCount() == target.glyphCount()) { + "Inconsistent glyph count at line ${lines.size}" } - } - val baseX = FloatArray(glyphCount) { base.getGlyphX(it) } - val baseY = FloatArray(glyphCount) { base.getGlyphY(it) } - val targetX = FloatArray(glyphCount) { target.getGlyphX(it) } - val targetY = FloatArray(glyphCount) { target.getGlyphY(it) } - - // Calculate font runs - val fontRun = mutableListOf<FontRun>() - if (glyphCount != 0) { - var start = 0 - var baseFont = base.getFont(start) - var targetFont = target.getFont(start) - require(FontInterpolator.canInterpolate(baseFont, targetFont)) { - "Cannot interpolate font at $start ($baseFont vs $targetFont)" + val glyphCount = base.glyphCount() + + // Good to recycle the array if the existing array can hold the new layout result. + val glyphIds = IntArray(glyphCount) { + base.getGlyphId(it).also { baseGlyphId -> + require(baseGlyphId == target.getGlyphId(it)) { + "Inconsistent glyph ID at $it in line ${lines.size}" + } + } } - for (i in 1 until glyphCount) { - val nextBaseFont = base.getFont(i) - val nextTargetFont = target.getFont(i) + val baseX = FloatArray(glyphCount) { base.getGlyphX(it) } + val baseY = FloatArray(glyphCount) { base.getGlyphY(it) } + val targetX = FloatArray(glyphCount) { target.getGlyphX(it) } + val targetY = FloatArray(glyphCount) { target.getGlyphY(it) } + + // Calculate font runs + val fontRun = mutableListOf<FontRun>() + if (glyphCount != 0) { + var start = 0 + var baseFont = base.getFont(start) + var targetFont = target.getFont(start) + require(FontInterpolator.canInterpolate(baseFont, targetFont)) { + "Cannot interpolate font at $start ($baseFont vs $targetFont)" + } - if (baseFont !== nextBaseFont) { - require(targetFont !== nextTargetFont) { - "Base font has changed at $i but target font has not changed." - } - // Font transition point. push run and reset context. - fontRun.add(FontRun(start, i, baseFont, targetFont)) - maxRunLength = max(maxRunLength, i - start) - baseFont = nextBaseFont - targetFont = nextTargetFont - start = i - require(FontInterpolator.canInterpolate(baseFont, targetFont)) { - "Cannot interpolate font at $start ($baseFont vs $targetFont)" - } - } else { // baseFont === nextBaseFont - require(targetFont === nextTargetFont) { - "Base font has not changed at $i but target font has changed." + for (i in 1 until glyphCount) { + val nextBaseFont = base.getFont(i) + val nextTargetFont = target.getFont(i) + + if (baseFont !== nextBaseFont) { + require(targetFont !== nextTargetFont) { + "Base font has changed at $i but target font has not changed." + } + // Font transition point. push run and reset context. + fontRun.add(FontRun(start, i, baseFont, targetFont)) + maxRunLength = max(maxRunLength, i - start) + baseFont = nextBaseFont + targetFont = nextTargetFont + start = i + require(FontInterpolator.canInterpolate(baseFont, targetFont)) { + "Cannot interpolate font at $start ($baseFont vs $targetFont)" + } + } else { // baseFont === nextBaseFont + require(targetFont === nextTargetFont) { + "Base font has not changed at $i but target font has changed." + } } } + fontRun.add(FontRun(start, glyphCount, baseFont, targetFont)) + maxRunLength = max(maxRunLength, glyphCount - start) } - fontRun.add(FontRun(start, glyphCount, baseFont, targetFont)) - maxRunLength = max(maxRunLength, glyphCount - start) + Run(glyphIds, baseX, baseY, targetX, targetY, fontRun) } - Line(glyphIds, baseX, baseY, targetX, targetY, fontRun) + Line(runs) } // Update float array used for drawing. @@ -338,7 +354,7 @@ class TextInterpolator( } // Draws single font run. - private fun drawFontRun(c: Canvas, line: Line, run: FontRun, paint: Paint) { + private fun drawFontRun(c: Canvas, line: Run, run: FontRun, paint: Paint) { var arrayIndex = 0 for (i in run.start until run.end) { tmpPositionArray[arrayIndex++] = @@ -358,7 +374,7 @@ class TextInterpolator( } private fun updatePositionsAndFonts( - layoutResult: List<PositionedGlyphs>, + layoutResult: List<List<PositionedGlyphs>>, updateBase: Boolean ) { // Update target positions with newly calculated text layout. @@ -366,45 +382,48 @@ class TextInterpolator( "The new layout result has different line count." } - lines.zip(layoutResult) { line, newGlyphs -> - require(newGlyphs.glyphCount() == line.glyphIds.size) { - "The new layout has different glyph count." - } + lines.zip(layoutResult) { line, runs -> + line.runs.zip(runs) { lineRun, newGlyphs -> + require(newGlyphs.glyphCount() == lineRun.glyphIds.size) { + "The new layout has different glyph count." + } - line.fontRuns.forEach { run -> - val newFont = newGlyphs.getFont(run.start) - for (i in run.start until run.end) { - require(newGlyphs.getGlyphId(run.start) == line.glyphIds[run.start]) { - "The new layout has different glyph ID at ${run.start}" + lineRun.fontRuns.forEach { run -> + val newFont = newGlyphs.getFont(run.start) + for (i in run.start until run.end) { + require(newGlyphs.getGlyphId(run.start) == lineRun.glyphIds[run.start]) { + "The new layout has different glyph ID at ${run.start}" + } + require(newFont === newGlyphs.getFont(i)) { + "The new layout has different font run." + + " $newFont vs ${newGlyphs.getFont(i)} at $i" + } } - require(newFont === newGlyphs.getFont(i)) { - "The new layout has different font run." + - " $newFont vs ${newGlyphs.getFont(i)} at $i" + + // The passing base font and target font is already interpolatable, so just + // check new font can be interpolatable with base font. + require(FontInterpolator.canInterpolate(newFont, run.baseFont)) { + "New font cannot be interpolated with existing font. $newFont," + + " ${run.baseFont}" } - } - // The passing base font and target font is already interpolatable, so just check - // new font can be interpolatable with base font. - require(FontInterpolator.canInterpolate(newFont, run.baseFont)) { - "New font cannot be interpolated with existing font. $newFont, ${run.baseFont}" + if (updateBase) { + run.baseFont = newFont + } else { + run.targetFont = newFont + } } if (updateBase) { - run.baseFont = newFont + for (i in lineRun.baseX.indices) { + lineRun.baseX[i] = newGlyphs.getGlyphX(i) + lineRun.baseY[i] = newGlyphs.getGlyphY(i) + } } else { - run.targetFont = newFont - } - } - - if (updateBase) { - for (i in line.baseX.indices) { - line.baseX[i] = newGlyphs.getGlyphX(i) - line.baseY[i] = newGlyphs.getGlyphY(i) - } - } else { - for (i in line.baseX.indices) { - line.targetX[i] = newGlyphs.getGlyphX(i) - line.targetY[i] = newGlyphs.getGlyphY(i) + for (i in lineRun.baseX.indices) { + lineRun.targetX[i] = newGlyphs.getGlyphX(i) + lineRun.targetY[i] = newGlyphs.getGlyphY(i) + } } } } @@ -412,16 +431,16 @@ class TextInterpolator( // Linear interpolate the paint. private fun lerp( - from: ArrayList<Paint>, - to: ArrayList<Paint>, + from: ArrayList<TextPaint>, + to: ArrayList<TextPaint>, progress: Float, - out: ArrayList<Paint> + out: ArrayList<TextPaint> ) { out.clear() // Currently only font size & colors are interpolated. // TODO(172943390): Add other interpolation or support custom interpolator. for (index in from.indices) { - val paint = Paint(from[index]) + val paint = TextPaint(from[index]) paint.textSize = MathUtils.lerp(from[index].textSize, to[index].textSize, progress) paint.color = ColorUtils.blendARGB(from[index].color, to[index].color, progress) out.add(paint) @@ -429,18 +448,20 @@ class TextInterpolator( } // Shape the text and stores the result to out argument. - private fun shapeText(layout: Layout, paints: ArrayList<Paint>): List<PositionedGlyphs> { - val out = mutableListOf<PositionedGlyphs>() + private fun shapeText( + layout: Layout, + paints: ArrayList<TextPaint> + ): List<List<PositionedGlyphs>> { + val out = mutableListOf<List<PositionedGlyphs>>() for (lineNo in 0 until layout.lineCount) { // Shape all lines. val lineStart = layout.getLineStart(lineNo) val count = layout.getLineEnd(lineNo) - lineStart - out.add(TextRunShaper.shapeTextRun( - layout.text, // Styles are ignored. - lineStart, count, // shape range - lineStart, count, // shape context = shape range. - 0f, 0f, // the layout offset. Not changed. - layout.getParagraphDirection(lineNo) == Layout.DIR_RIGHT_TO_LEFT, - paints[lineNo])) // Use given paint instead of layout's for style interpolation. + val runs = mutableListOf<PositionedGlyphs>() + TextShaper.shapeText(layout.text, lineStart, count, layout.textDirectionHeuristic, + paints[lineNo]) { _, _, glyphs, _ -> + runs.add(glyphs) + } + out.add(runs) } return out } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 8e5e9ea257ba..6b4e8bdef72a 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -113,8 +113,10 @@ public class SystemUIFactory { .setShellCommandHandler(mWMComponent.getShellCommandHandler()) .setAppPairs(mWMComponent.getAppPairs()); } else { - // TODO: Call on prepareSysUIComponentBuilder but not with real components. - builder = builder.setPip(Optional.ofNullable(null)) + // TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option + // is separating this logic into newly creating SystemUITestsFactory. + builder = prepareSysUIComponentBuilder(builder, mWMComponent) + .setPip(Optional.ofNullable(null)) .setSplitScreen(Optional.ofNullable(null)) .setOneHanded(Optional.ofNullable(null)) .setBubbles(Optional.ofNullable(null)) @@ -194,4 +196,4 @@ public class SystemUIFactory { AssetManager am, String modelName) { return new BackGestureTfClassifierProvider(); } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index dc841d91b1ab..9edfee73936e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -22,6 +22,7 @@ import static android.hardware.biometrics.BiometricManager.Authenticators; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityTaskManager; import android.app.IActivityTaskManager; import android.app.TaskStackListener; import android.content.BroadcastReceiver; @@ -74,7 +75,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, private final CommandQueue mCommandQueue; private final StatusBarStateController mStatusBarStateController; - private final IActivityTaskManager mActivityTaskManager; + private final ActivityTaskManager mActivityTaskManager; @Nullable private final FingerprintManager mFingerprintManager; @Nullable private final FaceManager mFaceManager; private final Provider<UdfpsController> mUdfpsControllerFactory; @@ -313,7 +314,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, @Inject public AuthController(Context context, CommandQueue commandQueue, StatusBarStateController statusBarStateController, - IActivityTaskManager activityTaskManager, + ActivityTaskManager activityTaskManager, @Nullable FingerprintManager fingerprintManager, @Nullable FaceManager faceManager, Provider<UdfpsController> udfpsControllerFactory) { @@ -356,12 +357,8 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, mUdfpsController = mUdfpsControllerFactory.get(); } - try { - mTaskStackListener = new BiometricTaskStackListener(); - mActivityTaskManager.registerTaskStackListener(mTaskStackListener); - } catch (RemoteException e) { - Log.w(TAG, "Unable to register task stack listener", e); - } + mTaskStackListener = new BiometricTaskStackListener(); + mActivityTaskManager.registerTaskStackListener(mTaskStackListener); } @Override @@ -413,6 +410,11 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, mCurrentDialog.onHelp(message); } + @Nullable + public List<FingerprintSensorPropertiesInternal> getUdfpsProps() { + return mUdfpsProps; + } + private String getErrorString(int modality, int error, int vendorCode) { switch (modality) { case TYPE_FACE: diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java index f6b8b4c92049..70a57cc8bd2a 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java @@ -185,8 +185,12 @@ public class BrightLineFalsingManager implements FalsingManager { return true; } - // TODO(b/172655679): we always reject single-taps when doing a robust check for now. - return robustCheck; + // TODO(b/172655679): More heuristics to come. For now, allow touches through if face-authed + if (robustCheck) { + return !mDataProvider.isJustUnlockedWithFace(); + } + + return false; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java index 13ff3f5c4e6c..ec4a91c24464 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java @@ -22,6 +22,7 @@ import com.android.systemui.ForegroundServicesDialog; import com.android.systemui.keyguard.WorkLockActivity; import com.android.systemui.screenrecord.ScreenRecordDialog; import com.android.systemui.settings.brightness.BrightnessDialog; +import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity; import com.android.systemui.tuner.TunerActivity; import com.android.systemui.usb.UsbDebuggingActivity; import com.android.systemui.usb.UsbDebuggingSecondaryUserActivity; @@ -85,4 +86,10 @@ public abstract class DefaultActivityBinder { @IntoMap @ClassKey(CreateUserActivity.class) public abstract Activity bindCreateUserActivity(CreateUserActivity activity); + + /** Inject into TvNotificationPanelActivity. */ + @Binds + @IntoMap + @ClassKey(TvNotificationPanelActivity.class) + public abstract Activity bindTvNotificationPanelActivity(TvNotificationPanelActivity activity); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java index c3f2e1848abd..2c06c7b96831 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java @@ -151,6 +151,12 @@ public class FrameworkServicesModule { @Provides @Singleton + static ActivityTaskManager provideActivityTaskManager() { + return ActivityTaskManager.getInstance(); + } + + @Provides + @Singleton static IActivityTaskManager provideIActivityTaskManager() { return ActivityTaskManager.getService(); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java index c0013d8cb981..9f6c19bdf06f 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java @@ -34,8 +34,8 @@ import com.android.systemui.shortcut.ShortcutKeyDispatcher; import com.android.systemui.statusbar.dagger.StatusBarModule; import com.android.systemui.statusbar.notification.InstantAppNotifier; import com.android.systemui.statusbar.phone.StatusBar; -import com.android.systemui.statusbar.tv.TvNotificationPanel; import com.android.systemui.statusbar.tv.TvStatusBar; +import com.android.systemui.statusbar.tv.notifications.TvNotificationPanel; import com.android.systemui.theme.ThemeOverlayController; import com.android.systemui.toast.ToastUI; import com.android.systemui.util.leak.GarbageMonitor; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java index 478923994af8..5afe526be918 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java @@ -68,6 +68,7 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen private final View mRootView; private final TextView mFooterText; private final ImageView mFooterIcon; + private final ImageView mPrimaryFooterIcon; private final Context mContext; private final Callback mCallback = new Callback(); private final SecurityController mSecurityController; @@ -83,6 +84,7 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen private CharSequence mFooterTextContent = null; private int mFooterTextId; private int mFooterIconId; + private Drawable mPrimaryFooterIconDrawable; @Inject QSSecurityFooter(@Named(QS_SECURITY_FOOTER_VIEW) View rootView, Context context, @@ -92,6 +94,7 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen mRootView.setOnClickListener(this); mFooterText = mRootView.findViewById(R.id.footer_text); mFooterIcon = mRootView.findViewById(R.id.footer_icon); + mPrimaryFooterIcon = mRootView.findViewById(R.id.primary_footer_icon); mFooterIconId = R.drawable.ic_info_outline; mContext = context; mMainHandler = mainHandler; @@ -188,6 +191,18 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen mFooterIconId = footerIconId; mMainHandler.post(mUpdateIcon); } + + // Update the primary icon + if (isParentalControlsEnabled) { + if (mPrimaryFooterIconDrawable == null) { + DeviceAdminInfo info = mSecurityController.getDeviceAdminInfo(); + mPrimaryFooterIconDrawable = mSecurityController.getIcon(info); + } + } else { + mPrimaryFooterIconDrawable = null; + } + mMainHandler.post(mUpdatePrimaryIcon); + mMainHandler.post(mUpdateDisplayState); } @@ -532,6 +547,15 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen } }; + private final Runnable mUpdatePrimaryIcon = new Runnable() { + @Override + public void run() { + mPrimaryFooterIcon.setVisibility(mPrimaryFooterIconDrawable != null + ? View.VISIBLE : View.GONE); + mPrimaryFooterIcon.setImageDrawable(mPrimaryFooterIconDrawable); + } + }; + private final Runnable mUpdateDisplayState = new Runnable() { @Override public void run() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java index 84818eea8a23..dbee0ee8f5d5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -11,19 +11,22 @@ * 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 + * limitations under the License. */ package com.android.systemui.statusbar; import android.app.Notification; +import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.text.TextUtils; +import android.view.NotificationHeaderView; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; +import com.android.internal.R; import com.android.internal.widget.CachingIconView; import com.android.internal.widget.ConversationLayout; import com.android.internal.widget.NotificationExpandButton; @@ -36,35 +39,38 @@ import java.util.List; import java.util.Objects; /** - * A Util to manage {@link android.view.NotificationHeaderView} objects and their redundancies. + * A utility to manage notification views when they are placed in a group by adjusting elements + * to reduce redundancies and occasionally tweak layouts to highlight the unique content. */ -public class NotificationHeaderUtil { - - private static final TextViewComparator sTextViewComparator = new TextViewComparator(); - private static final TextViewComparator sAppNameComparator = new AppNameComparator(); - private static final VisibilityApplicator sVisibilityApplicator = new VisibilityApplicator(); - private static final VisibilityApplicator sAppNameApplicator = new AppNameApplicator(); - private static final DataExtractor sIconExtractor = new DataExtractor() { +public class NotificationGroupingUtil { + + private static final TextViewComparator TEXT_VIEW_COMPARATOR = new TextViewComparator(); + private static final TextViewComparator APP_NAME_COMPARATOR = new AppNameComparator(); + private static final ViewComparator BADGE_COMPARATOR = new BadgeComparator(); + private static final VisibilityApplicator VISIBILITY_APPLICATOR = new VisibilityApplicator(); + private static final VisibilityApplicator APP_NAME_APPLICATOR = new AppNameApplicator(); + private static final ResultApplicator LEFT_ICON_APPLICATOR = new LeftIconApplicator(); + private static final DataExtractor ICON_EXTRACTOR = new DataExtractor() { @Override public Object extractData(ExpandableNotificationRow row) { return row.getEntry().getSbn().getNotification(); } }; - private static final IconComparator sIconVisibilityComparator = new IconComparator() { + private static final IconComparator ICON_VISIBILITY_COMPARATOR = new IconComparator() { public boolean compare(View parent, View child, Object parentData, Object childData) { return hasSameIcon(parentData, childData) && hasSameColor(parentData, childData); } }; - private static final IconComparator sGreyComparator = new IconComparator() { + private static final IconComparator GREY_COMPARATOR = new IconComparator() { public boolean compare(View parent, View child, Object parentData, Object childData) { return !hasSameIcon(parentData, childData) || hasSameColor(parentData, childData); } }; - private final static ResultApplicator mGreyApplicator = new ResultApplicator() { + private static final ResultApplicator GREY_APPLICATOR = new ResultApplicator() { @Override public void apply(View parent, View view, boolean apply, boolean reset) { CachingIconView icon = view.findViewById(com.android.internal.R.id.icon); @@ -80,87 +86,79 @@ public class NotificationHeaderUtil { }; private final ExpandableNotificationRow mRow; - private final ArrayList<HeaderProcessor> mComparators = new ArrayList<>(); + private final ArrayList<Processor> mProcessors = new ArrayList<>(); private final HashSet<Integer> mDividers = new HashSet<>(); - public NotificationHeaderUtil(ExpandableNotificationRow row) { + public NotificationGroupingUtil(ExpandableNotificationRow row) { mRow = row; // To hide the icons if they are the same and the color is the same - mComparators.add(new HeaderProcessor(mRow, + mProcessors.add(new Processor(mRow, com.android.internal.R.id.icon, - sIconExtractor, - sIconVisibilityComparator, - sVisibilityApplicator)); + ICON_EXTRACTOR, + ICON_VISIBILITY_COMPARATOR, + VISIBILITY_APPLICATOR)); // To grey them out the icons and expand button when the icons are not the same - mComparators.add(new HeaderProcessor(mRow, - com.android.internal.R.id.notification_header, - sIconExtractor, - sGreyComparator, - mGreyApplicator)); - mComparators.add(new HeaderProcessor(mRow, + mProcessors.add(new Processor(mRow, + com.android.internal.R.id.status_bar_latest_event_content, + ICON_EXTRACTOR, + GREY_COMPARATOR, + GREY_APPLICATOR)); + mProcessors.add(new Processor(mRow, + com.android.internal.R.id.status_bar_latest_event_content, + ICON_EXTRACTOR, + ICON_VISIBILITY_COMPARATOR, + LEFT_ICON_APPLICATOR)); + mProcessors.add(new Processor(mRow, com.android.internal.R.id.profile_badge, null /* Extractor */, - new ViewComparator() { - @Override - public boolean compare(View parent, View child, Object parentData, - Object childData) { - return parent.getVisibility() != View.GONE; - } - - @Override - public boolean isEmpty(View view) { - if (view instanceof ImageView) { - return ((ImageView) view).getDrawable() == null; - } - return false; - } - }, - sVisibilityApplicator)); - mComparators.add(new HeaderProcessor( - mRow, + BADGE_COMPARATOR, + VISIBILITY_APPLICATOR)); + mProcessors.add(new Processor(mRow, com.android.internal.R.id.app_name_text, null, - sAppNameComparator, - sAppNameApplicator)); - mComparators.add(HeaderProcessor.forTextView(mRow, - com.android.internal.R.id.header_text)); + APP_NAME_COMPARATOR, + APP_NAME_APPLICATOR)); + mProcessors.add(Processor.forTextView(mRow, com.android.internal.R.id.header_text)); mDividers.add(com.android.internal.R.id.header_text_divider); mDividers.add(com.android.internal.R.id.header_text_secondary_divider); mDividers.add(com.android.internal.R.id.time_divider); } - public void updateChildrenHeaderAppearance() { + /** + * Update the appearance of the children in this group to reduce redundancies. + */ + public void updateChildrenAppearance() { List<ExpandableNotificationRow> notificationChildren = mRow.getAttachedChildren(); - if (notificationChildren == null) { + if (notificationChildren == null || !mRow.isSummaryWithChildren()) { return; } - // Initialize the comparators - for (int compI = 0; compI < mComparators.size(); compI++) { - mComparators.get(compI).init(); + // Initialize the processors + for (int compI = 0; compI < mProcessors.size(); compI++) { + mProcessors.get(compI).init(); } // Compare all notification headers for (int i = 0; i < notificationChildren.size(); i++) { ExpandableNotificationRow row = notificationChildren.get(i); - for (int compI = 0; compI < mComparators.size(); compI++) { - mComparators.get(compI).compareToHeader(row); + for (int compI = 0; compI < mProcessors.size(); compI++) { + mProcessors.get(compI).compareToGroupParent(row); } } // Apply the comparison to the row for (int i = 0; i < notificationChildren.size(); i++) { ExpandableNotificationRow row = notificationChildren.get(i); - for (int compI = 0; compI < mComparators.size(); compI++) { - mComparators.get(compI).apply(row); + for (int compI = 0; compI < mProcessors.size(); compI++) { + mProcessors.get(compI).apply(row); } // We need to sanitize the dividers since they might be off-balance now - sanitizeHeaderViews(row); + sanitizeTopLineViews(row); } } - private void sanitizeHeaderViews(ExpandableNotificationRow row) { + private void sanitizeTopLineViews(ExpandableNotificationRow row) { if (row.isSummaryWithChildren()) { - sanitizeHeader(row.getNotificationViewWrapper().getNotificationHeader()); + sanitizeTopLine(row.getNotificationViewWrapper().getNotificationHeader()); return; } final NotificationContentView layout = row.getPrivateLayout(); @@ -171,13 +169,11 @@ public class NotificationHeaderUtil { private void sanitizeChild(View child) { if (child != null) { - ViewGroup header = child.findViewById( - com.android.internal.R.id.notification_top_line); - sanitizeHeader(header); + sanitizeTopLine(child.findViewById(R.id.notification_top_line)); } } - private void sanitizeHeader(ViewGroup rowHeader) { + private void sanitizeTopLine(ViewGroup rowHeader) { if (rowHeader == null) { return; } @@ -225,28 +221,31 @@ public class NotificationHeaderUtil { } } - public void restoreNotificationHeader(ExpandableNotificationRow row) { - for (int compI = 0; compI < mComparators.size(); compI++) { - mComparators.get(compI).apply(row, true /* reset */); + /** + * Reset the modifications to this row for removing it from the group. + */ + public void restoreChildNotification(ExpandableNotificationRow row) { + for (int compI = 0; compI < mProcessors.size(); compI++) { + mProcessors.get(compI).apply(row, true /* reset */); } - sanitizeHeaderViews(row); + sanitizeTopLineViews(row); } - private static class HeaderProcessor { + private static class Processor { private final int mId; private final DataExtractor mExtractor; + private final ViewComparator mComparator; private final ResultApplicator mApplicator; private final ExpandableNotificationRow mParentRow; private boolean mApply; private View mParentView; - private ViewComparator mComparator; private Object mParentData; - public static HeaderProcessor forTextView(ExpandableNotificationRow row, int id) { - return new HeaderProcessor(row, id, null, sTextViewComparator, sVisibilityApplicator); + public static Processor forTextView(ExpandableNotificationRow row, int id) { + return new Processor(row, id, null, TEXT_VIEW_COMPARATOR, VISIBILITY_APPLICATOR); } - HeaderProcessor(ExpandableNotificationRow row, int id, DataExtractor extractor, + Processor(ExpandableNotificationRow row, int id, DataExtractor extractor, ViewComparator comparator, ResultApplicator applicator) { mId = id; @@ -257,12 +256,12 @@ public class NotificationHeaderUtil { } public void init() { - mParentView = mParentRow.getNotificationViewWrapper().getNotificationHeader() - .findViewById(mId); + View header = mParentRow.getNotificationViewWrapper().getNotificationHeader(); + mParentView = header == null ? null : header.findViewById(mId); mParentData = mExtractor == null ? null : mExtractor.extractData(mParentRow); mApply = !mComparator.isEmpty(mParentView); } - public void compareToHeader(ExpandableNotificationRow row) { + public void compareToGroupParent(ExpandableNotificationRow row) { if (!mApply) { return; } @@ -308,8 +307,8 @@ public class NotificationHeaderUtil { private interface ViewComparator { /** - * @param parent the parent view - * @param child the child view + * @param parent the view with the given id in the group header + * @param child the view with the given id in the child notification * @param parentData optional data for the parent * @param childData optional data for the child * @return whether to views are the same @@ -322,6 +321,21 @@ public class NotificationHeaderUtil { Object extractData(ExpandableNotificationRow row); } + private static class BadgeComparator implements ViewComparator { + @Override + public boolean compare(View parent, View child, Object parentData, Object childData) { + return parent.getVisibility() != View.GONE; + } + + @Override + public boolean isEmpty(View view) { + if (view instanceof ImageView) { + return ((ImageView) view).getDrawable() == null; + } + return false; + } + } + private static class TextViewComparator implements ViewComparator { @Override public boolean compare(View parent, View child, Object parentData, Object childData) { @@ -338,7 +352,7 @@ public class NotificationHeaderUtil { } } - private static abstract class IconComparator implements ViewComparator { + private abstract static class IconComparator implements ViewComparator { @Override public boolean compare(View parent, View child, Object parentData, Object childData) { return false; @@ -366,6 +380,12 @@ public class NotificationHeaderUtil { } private interface ResultApplicator { + /** + * @param parent the root view of the child notification + * @param view the view with the given id in the child notification + * @param apply whether the state should be applied or removed + * @param reset if [de]application is the result of a reset + */ void apply(View parent, View view, boolean apply, boolean reset); } @@ -403,4 +423,54 @@ public class NotificationHeaderUtil { return super.compare(parent, child, parentData, childData); } } + + private static class LeftIconApplicator implements ResultApplicator { + + public static final int[] MARGIN_ADJUSTED_VIEWS = { + R.id.notification_headerless_view_column, + R.id.line1, + R.id.notification_main_column, + R.id.notification_header}; + + @Override + public void apply(View parent, View child, boolean apply, boolean reset) { + ImageView rightIcon = child.findViewById(com.android.internal.R.id.right_icon); + ImageView leftIcon = child.findViewById(com.android.internal.R.id.left_icon); + if (rightIcon == null || leftIcon == null) { + return; + } + Drawable iconDrawable = rightIcon.getDrawable(); + if (iconDrawable == null) { + return; + } + rightIcon.setVisibility(apply ? View.GONE : View.VISIBLE); + leftIcon.setVisibility(apply ? View.VISIBLE : View.GONE); + leftIcon.setImageDrawable(apply ? iconDrawable : null); + + for (int viewId : MARGIN_ADJUSTED_VIEWS) { + adjustMargins(!apply, child.findViewById(viewId)); + } + } + + void adjustMargins(boolean iconVisible, View target) { + if (target == null) { + return; + } + Integer value = (Integer) target.getTag(iconVisible + ? com.android.internal.R.id.tag_margin_end_when_icon_visible + : com.android.internal.R.id.tag_margin_end_when_icon_gone); + if (value == null) { + return; + } + if (target instanceof NotificationHeaderView) { + ((NotificationHeaderView) target).setTopLineExtraMarginEnd(value); + } else { + ViewGroup.LayoutParams layoutParams = target.getLayoutParams(); + if (layoutParams instanceof ViewGroup.MarginLayoutParams) { + ((ViewGroup.MarginLayoutParams) layoutParams).setMarginEnd(value); + target.setLayoutParams(layoutParams); + } + } + } + } } 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 44b9bd26aa38..d1ab7ea55d57 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt @@ -19,18 +19,25 @@ package com.android.systemui.statusbar.notification import android.app.Notification import android.content.Context import android.content.pm.LauncherApps +import android.graphics.drawable.AnimatedImageDrawable 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 import com.android.systemui.dagger.SysUISingleton 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.legacy.NotificationGroupManagerLegacy import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationContentView import com.android.systemui.statusbar.notification.stack.StackStateAnimator +import com.android.systemui.statusbar.policy.HeadsUpManager +import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener +import com.android.systemui.util.children import java.util.concurrent.ConcurrentHashMap import javax.inject.Inject @@ -58,6 +65,71 @@ class ConversationNotificationProcessor @Inject constructor( } /** + * Tracks state related to animated images inside of notifications. Ex: starting and stopping + * animations to conserve CPU and memory. + */ +@SysUISingleton +class AnimatedImageNotificationManager @Inject constructor( + private val notificationEntryManager: NotificationEntryManager, + private val headsUpManager: HeadsUpManager, + private val statusBarStateController: StatusBarStateController +) { + + private var isStatusBarExpanded = false + + /** Begins listening to state changes and updating animations accordingly. */ + fun bind() { + headsUpManager.addListener(object : OnHeadsUpChangedListener { + override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) { + entry.row?.let { row -> + updateAnimatedImageDrawables(row, animating = isHeadsUp || isStatusBarExpanded) + } + } + }) + 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) + } + } + } + }) + 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) + }) + } + + private fun updateAnimatedImageDrawables(row: ExpandableNotificationRow, animating: Boolean) = + (row.layouts?.asSequence() ?: emptySequence()) + .flatMap { layout -> layout.allViews.asSequence() } + .flatMap { view -> + (view as? ConversationLayout)?.messagingGroups?.asSequence() + ?: (view as? MessagingLayout)?.messagingGroups?.asSequence() + ?: emptySequence() + } + .flatMap { messagingGroup -> messagingGroup.messageContainer.children } + .mapNotNull { view -> + (view as? MessagingImageMessage) + ?.let { imageMessage -> + imageMessage.drawable as? AnimatedImageDrawable + } + } + .forEach { animatedImageDrawable -> + if (animating) animatedImageDrawable.start() + else animatedImageDrawable.stop() + } +} + +/** * Tracks state related to conversation notifications, and updates the UI of existing notifications * when necessary. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java index 363a08566fd3..1f9bc77b0b2f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java @@ -364,18 +364,13 @@ public class InstantAppNotifier extends SystemUI @Nullable private Intent getTaskIntent(int taskId, int userId) { - try { - final List<ActivityManager.RecentTaskInfo> tasks = - ActivityTaskManager.getService() - .getRecentTasks(NUM_TASKS_FOR_INSTANT_APP_INFO, 0, userId) - .getList(); - for (int i = 0; i < tasks.size(); i++) { - if (tasks.get(i).id == taskId) { - return tasks.get(i).baseIntent; - } + final List<ActivityManager.RecentTaskInfo> tasks = + ActivityTaskManager.getInstance().getRecentTasks( + NUM_TASKS_FOR_INSTANT_APP_INFO, 0, userId); + for (int i = 0; i < tasks.size(); i++) { + if (tasks.get(i).id == taskId) { + return tasks.get(i).baseIntent; } - } catch (RemoteException e) { - // Fall through } return null; } 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 8f352ad55041..54ce4ede9770 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 @@ -22,6 +22,7 @@ import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.Snoo import com.android.systemui.statusbar.FeatureFlags import com.android.systemui.statusbar.NotificationListener import com.android.systemui.statusbar.NotificationPresenter +import com.android.systemui.statusbar.notification.AnimatedImageNotificationManager import com.android.systemui.statusbar.notification.NotificationActivityStarter import com.android.systemui.statusbar.notification.NotificationClicker import com.android.systemui.statusbar.notification.NotificationEntryManager @@ -71,7 +72,8 @@ class NotificationsControllerImpl @Inject constructor( private val headsUpManager: HeadsUpManager, private val headsUpController: HeadsUpController, private val headsUpViewBinder: HeadsUpViewBinder, - private val clickerBuilder: NotificationClicker.Builder + private val clickerBuilder: NotificationClicker.Builder, + private val animatedImageNotificationManager: AnimatedImageNotificationManager ) : NotificationsController { override fun initialize( @@ -100,6 +102,7 @@ class NotificationsControllerImpl @Inject constructor( bindRowCallback) headsUpViewBinder.setPresenter(presenter) notifBindPipelineInitializer.initialize() + animatedImageNotificationManager.bind() if (featureFlags.isNewNotifPipelineEnabled) { newNotifPipeline.get().initialize( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 5ed17f1ee07f..10118e4733ba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -558,7 +558,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView setChronometerRunning(true); } if (mNotificationParent != null) { - mNotificationParent.updateChildrenHeaderAppearance(); + mNotificationParent.updateChildrenAppearance(); } onAttachedChildrenCountChanged(); // The public layouts expand button is always visible @@ -2337,7 +2337,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } getShowingLayout().updateBackgroundColor(false /* animate */); mPrivateLayout.updateExpandButtons(isExpandable()); - updateChildrenHeaderAppearance(); + updateChildrenAppearance(); updateChildrenVisibility(); applyChildrenRoundness(); } @@ -2382,9 +2382,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return channels; } - public void updateChildrenHeaderAppearance() { + /** + * If this is a group, update the appearance of the children. + */ + public void updateChildrenAppearance() { if (mIsSummaryWithChildren) { - mChildrenContainer.updateChildrenHeaderAppearance(); + mChildrenContainer.updateChildrenAppearance(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java index 7bd192d850c1..44ccb68cce4a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java @@ -19,10 +19,7 @@ package com.android.systemui.statusbar.notification.row; import android.app.ActivityManager; import android.app.Notification; import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; -import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Bundle; import android.os.Parcelable; @@ -81,7 +78,7 @@ public class NotificationInlineImageResolver implements ImageResolver { * @return True if has its internal cache, false otherwise. */ public boolean hasCache() { - return mImageCache != null && !ActivityManager.isLowRamDeviceStatic(); + return mImageCache != null && !isLowRam(); } private boolean isLowRam() { @@ -110,11 +107,6 @@ public class NotificationInlineImageResolver implements ImageResolver { : R.dimen.notification_custom_view_max_image_height); } - @VisibleForTesting - protected BitmapDrawable resolveImageInternal(Uri uri) throws IOException { - return (BitmapDrawable) LocalImageResolver.resolveImage(uri, mContext); - } - /** * To resolve image from specified uri directly. If the resulting image is larger than the * maximum allowed size, scale it down. @@ -123,13 +115,7 @@ public class NotificationInlineImageResolver implements ImageResolver { * @throws IOException Throws if failed at resolving the image. */ Drawable resolveImage(Uri uri) throws IOException { - BitmapDrawable image = resolveImageInternal(uri); - if (image == null || image.getBitmap() == null) { - throw new IOException("resolveImageInternal returned null for uri: " + uri); - } - Bitmap bitmap = image.getBitmap(); - image.setBitmap(Icon.scaleDownIfNecessary(bitmap, mMaxImageWidth, mMaxImageHeight)); - return image; + return LocalImageResolver.resolveImage(uri, mContext, mMaxImageWidth, mMaxImageHeight); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index 05db67d706cf..37d5da24a704 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -64,6 +64,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { private ImageView mWorkProfileImage; private View mAudiblyAlertedIcon; private View mFeedbackIcon; + private View mLeftIcon; private View mRightIcon; private boolean mIsLowPriority; @@ -108,6 +109,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { mAppNameText = mView.findViewById(com.android.internal.R.id.app_name_text); mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button); mAltExpandTarget = mView.findViewById(com.android.internal.R.id.alternate_expand_target); + mLeftIcon = mView.findViewById(com.android.internal.R.id.left_icon); mRightIcon = mView.findViewById(com.android.internal.R.id.right_icon); mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge); mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header); @@ -146,6 +148,9 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { updateCropToPaddingForImageViews(); Notification notification = row.getEntry().getSbn().getNotification(); mIcon.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon()); + if (mLeftIcon != null) { + mLeftIcon.setClipToOutline(true); + } if (mRightIcon != null) { mRightIcon.setClipToOutline(true); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index b04f94ce9c1d..601fc197cfa6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -34,7 +34,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.CachingIconView; import com.android.systemui.R; import com.android.systemui.statusbar.CrossFadeHelper; -import com.android.systemui.statusbar.NotificationHeaderUtil; +import com.android.systemui.statusbar.NotificationGroupingUtil; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -94,7 +94,7 @@ public class NotificationChildrenContainer extends ViewGroup { private NotificationViewWrapper mNotificationHeaderWrapper; private NotificationHeaderView mNotificationHeaderLowPriority; private NotificationViewWrapper mNotificationHeaderWrapperLowPriority; - private NotificationHeaderUtil mHeaderUtil; + private NotificationGroupingUtil mGroupingUtil; private ViewState mHeaderViewState; private int mClipBottomAmount; private boolean mIsLowPriority; @@ -299,7 +299,7 @@ public class NotificationChildrenContainer extends ViewGroup { row.setSystemChildExpanded(false); row.setUserLocked(false); if (!row.isRemoved()) { - mHeaderUtil.restoreNotificationHeader(row); + mGroupingUtil.restoreChildNotification(row); } } @@ -341,7 +341,7 @@ public class NotificationChildrenContainer extends ViewGroup { } recreateLowPriorityHeader(builder, isConversation); updateHeaderVisibility(false /* animate */); - updateChildrenHeaderAppearance(); + updateChildrenAppearance(); } /** @@ -389,8 +389,11 @@ public class NotificationChildrenContainer extends ViewGroup { } } - public void updateChildrenHeaderAppearance() { - mHeaderUtil.updateChildrenHeaderAppearance(); + /** + * Update the appearance of the children to reduce redundancies. + */ + public void updateChildrenAppearance() { + mGroupingUtil.updateChildrenAppearance(); } public void updateGroupOverflow() { @@ -861,7 +864,7 @@ public class NotificationChildrenContainer extends ViewGroup { public void setContainingNotification(ExpandableNotificationRow parent) { mContainingNotification = parent; - mHeaderUtil = new NotificationHeaderUtil(mContainingNotification); + mGroupingUtil = new NotificationGroupingUtil(mContainingNotification); } public ExpandableNotificationRow getContainingNotification() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index f7139aa2acce..bb46172cd714 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -68,6 +68,7 @@ import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.LatencyTracker; +import com.android.keyguard.DisabledUdfpsController; import com.android.keyguard.KeyguardClockSwitchController; import com.android.keyguard.KeyguardStatusView; import com.android.keyguard.KeyguardStatusViewController; @@ -256,7 +257,25 @@ public class NotificationPanelViewController extends PanelViewController { mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled(); mDelayShowingKeyguardStatusBar = false; } - }; + + @Override + public void onKeyguardVisibilityChanged(boolean showing) { + if (mDisabledUdfpsController == null + && mAuthController.getUdfpsRegion() != null + && mAuthController.isUdfpsEnrolled( + KeyguardUpdateMonitor.getCurrentUser())) { + LayoutInflater.from(mView.getContext()) + .inflate(R.layout.disabled_udfps_view, mView); + mDisabledUdfpsController = new DisabledUdfpsController( + mView.findViewById(R.id.disabled_udfps_view), + mStatusBarStateController, + mUpdateMonitor, + mAuthController, + mStatusBarKeyguardViewManager); + mDisabledUdfpsController.init(); + } + } + }; private final InjectionInflationController mInjectionInflationController; private final PowerManager mPowerManager; @@ -284,6 +303,7 @@ public class NotificationPanelViewController extends PanelViewController { private QS mQs; private FrameLayout mQsFrame; private KeyguardStatusViewController mKeyguardStatusViewController; + private DisabledUdfpsController mDisabledUdfpsController; private View mQsNavbarScrim; private NotificationsQuickSettingsContainer mNotificationContainerParent; private boolean mAnimateNextPositionUpdate; @@ -3044,6 +3064,9 @@ public class NotificationPanelViewController extends PanelViewController { if (mKeyguardStatusBar != null) { mKeyguardStatusBar.dump(fd, pw, args); } + if (mDisabledUdfpsController != null) { + mDisabledUdfpsController.dump(fd, pw, args); + } } public boolean hasActiveClearableNotifications() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index 79f09158fc67..adbc85b8e2e5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -42,6 +42,7 @@ import android.text.SpannedString; import android.text.TextWatcher; import android.util.AttributeSet; import android.util.Log; +import android.view.ContentInfo; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -590,12 +591,12 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene new OnReceiveContentListener() { @Override @Nullable - public Payload onReceiveContent(@NonNull View view, - @NonNull Payload payload) { - Map<Boolean, Payload> split = payload.partition( + public ContentInfo onReceiveContent(@NonNull View view, + @NonNull ContentInfo payload) { + Map<Boolean, ContentInfo> split = payload.partition( item -> item.getUri() != null); - Payload uriItems = split.get(true); - Payload remainingItems = split.get(false); + ContentInfo uriItems = split.get(true); + ContentInfo remainingItems = split.get(false); if (uriItems != null) { ClipData clip = uriItems.getClip(); ClipDescription description = clip.getDescription(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java index 2795857383e8..bdf2b0c24ba5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -71,7 +71,6 @@ public class TvStatusBar extends SystemUI implements CommandQueue.Callbacks { // Creating AudioRecordingDisclosureBar and just letting it run new AudioRecordingDisclosureBar(mContext); } - } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationAdapter.java new file mode 100644 index 000000000000..3b1a4db067a3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationAdapter.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.tv.notifications; + +import android.app.Notification; +import android.app.PendingIntent; +import android.service.notification.StatusBarNotification; +import android.util.Log; +import android.util.SparseArray; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.systemui.R; + +/** + * Adapter for the VerticalGridView of the TvNotificationsPanelView. + */ +public class TvNotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { + private static final String TAG = "TvNotificationAdapter"; + private SparseArray<StatusBarNotification> mNotifications; + + public TvNotificationAdapter() { + setHasStableIds(true); + } + + @NonNull + @Override + public TvNotificationViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.tv_notification_item, + parent, false); + return new TvNotificationViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { + if (mNotifications == null) { + Log.e(TAG, "Could not bind view holder because the notification is missing"); + return; + } + + TvNotificationViewHolder holder = (TvNotificationViewHolder) viewHolder; + Notification notification = mNotifications.valueAt(position).getNotification(); + holder.mTitle.setText(notification.extras.getString(Notification.EXTRA_TITLE)); + holder.mDetails.setText(notification.extras.getString(Notification.EXTRA_TEXT)); + holder.mPendingIntent = notification.contentIntent; + } + + @Override + public int getItemCount() { + return mNotifications == null ? 0 : mNotifications.size(); + } + + @Override + public long getItemId(int position) { + // the item id is the notification id + return mNotifications.keyAt(position); + } + + /** + * Updates the notifications and calls notifyDataSetChanged(). + */ + public void setNotifications(SparseArray<StatusBarNotification> notifications) { + this.mNotifications = notifications; + notifyDataSetChanged(); + } + + private static class TvNotificationViewHolder extends RecyclerView.ViewHolder implements + View.OnClickListener { + final TextView mTitle; + final TextView mDetails; + PendingIntent mPendingIntent; + + protected TvNotificationViewHolder(View itemView) { + super(itemView); + mTitle = itemView.findViewById(R.id.tv_notification_title); + mDetails = itemView.findViewById(R.id.tv_notification_details); + itemView.setOnClickListener(this); + } + + @Override + public void onClick(View v) { + try { + if (mPendingIntent != null) { + mPendingIntent.send(); + } + } catch (PendingIntent.CanceledException e) { + Log.d(TAG, "Pending intent canceled for : " + mPendingIntent); + } + } + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java new file mode 100644 index 000000000000..d985803c2b39 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.tv.notifications; + +import android.annotation.Nullable; +import android.app.Notification; +import android.content.Context; +import android.service.notification.NotificationListenerService; +import android.service.notification.StatusBarNotification; +import android.util.Log; +import android.util.SparseArray; + +import com.android.systemui.SystemUI; +import com.android.systemui.statusbar.NotificationListener; + +import javax.inject.Inject; + +/** + * Keeps track of the notifications on TV. + */ +public class TvNotificationHandler extends SystemUI implements + NotificationListener.NotificationHandler { + private static final String TAG = "TvNotificationHandler"; + private final NotificationListener mNotificationListener; + private final SparseArray<StatusBarNotification> mNotifications = new SparseArray<>(); + @Nullable + private Listener mUpdateListener; + + @Inject + public TvNotificationHandler(Context context, NotificationListener notificationListener) { + super(context); + mNotificationListener = notificationListener; + } + + public SparseArray<StatusBarNotification> getCurrentNotifications() { + return mNotifications; + } + + public void setTvNotificationListener(Listener listener) { + mUpdateListener = listener; + } + + @Override + public void start() { + mNotificationListener.addNotificationHandler(this); + mNotificationListener.registerAsSystemService(); + } + + @Override + public void onNotificationPosted(StatusBarNotification sbn, + NotificationListenerService.RankingMap rankingMap) { + if (!new Notification.TvExtender(sbn.getNotification()).isAvailableOnTv()) { + Log.v(TAG, "Notification not added because it isn't relevant for tv"); + return; + } + + mNotifications.put(sbn.getId(), sbn); + if (mUpdateListener != null) { + mUpdateListener.notificationsUpdated(mNotifications); + } + Log.d(TAG, "Notification added"); + } + + @Override + public void onNotificationRemoved(StatusBarNotification sbn, + NotificationListenerService.RankingMap rankingMap) { + + if (mNotifications.contains(sbn.getId())) { + mNotifications.remove(sbn.getId()); + Log.d(TAG, "Notification removed"); + + if (mUpdateListener != null) { + mUpdateListener.notificationsUpdated(mNotifications); + } + } + } + + @Override + public void onNotificationRemoved(StatusBarNotification sbn, + NotificationListenerService.RankingMap rankingMap, int reason) { + onNotificationRemoved(sbn, rankingMap); + } + + @Override + public void onNotificationRankingUpdate(NotificationListenerService.RankingMap rankingMap) { + // noop + } + + @Override + public void onNotificationsInitialized() { + // noop + } + + /** + * Get notified when the notifications are updated. + */ + interface Listener { + void notificationsUpdated(SparseArray<StatusBarNotification> sbns); + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java index 0bd36240a366..477424c55a73 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.tv; +package com.android.systemui.statusbar.tv.notifications; import android.Manifest; import android.app.NotificationManager; @@ -59,9 +59,8 @@ public class TvNotificationPanel extends SystemUI implements CommandQueue.Callba startNotificationHandlerActivity( new Intent(NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL)); } else { - Log.w(TAG, - "Not toggling notification panel: config_notificationHandlerPackage is " - + "empty"); + openInternalNotificationPanel( + NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL); } } @@ -71,9 +70,8 @@ public class TvNotificationPanel extends SystemUI implements CommandQueue.Callba startNotificationHandlerActivity( new Intent(NotificationManager.ACTION_OPEN_NOTIFICATION_HANDLER_PANEL)); } else { - Log.w(TAG, - "Not expanding notification panel: config_notificationHandlerPackage is " - + "empty"); + openInternalNotificationPanel( + NotificationManager.ACTION_OPEN_NOTIFICATION_HANDLER_PANEL); } } @@ -86,11 +84,17 @@ public class TvNotificationPanel extends SystemUI implements CommandQueue.Callba closeNotificationIntent.setPackage(mNotificationHandlerPackage); mContext.sendBroadcastAsUser(closeNotificationIntent, UserHandle.CURRENT); } else { - Log.w(TAG, - "Not closing notification panel: config_notificationHandlerPackage is empty"); + openInternalNotificationPanel( + NotificationManager.ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL); } } + private void openInternalNotificationPanel(String action) { + Intent intent = new Intent(mContext, TvNotificationPanelActivity.class); + intent.setAction(action); + mContext.startActivityAsUser(intent, UserHandle.SYSTEM); + } + /** * Starts the activity intent if all of the following are true * <ul> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanelActivity.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanelActivity.java new file mode 100644 index 000000000000..30f401b91d25 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanelActivity.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.tv.notifications; + +import android.annotation.NonNull; +import android.app.Activity; +import android.app.NotificationManager; +import android.content.Intent; +import android.os.Bundle; +import android.service.notification.StatusBarNotification; +import android.util.SparseArray; +import android.view.View; + +import androidx.leanback.widget.VerticalGridView; + +import com.android.systemui.R; + +import javax.inject.Inject; + +/** + * This Activity shows a notification panel for tv. It is used if no other app (e.g. a launcher) can + * be found to show the notifications. + */ +public class TvNotificationPanelActivity extends Activity implements + TvNotificationHandler.Listener { + private final TvNotificationHandler mTvNotificationHandler; + private TvNotificationAdapter mTvNotificationAdapter; + private VerticalGridView mNotificationListView; + private View mNotificationPlaceholder; + private boolean mPanelAlreadyOpen = false; + + @Inject + public TvNotificationPanelActivity(TvNotificationHandler tvNotificationHandler) { + super(); + mTvNotificationHandler = tvNotificationHandler; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (maybeClosePanel(getIntent())) { + return; + } + mPanelAlreadyOpen = true; + + setContentView(R.layout.tv_notification_panel); + + mNotificationPlaceholder = findViewById(R.id.no_tv_notifications); + mTvNotificationAdapter = new TvNotificationAdapter(); + + mNotificationListView = findViewById(R.id.notifications_list); + mNotificationListView.setAdapter(mTvNotificationAdapter); + mNotificationListView.setColumnWidth(R.dimen.tv_notification_panel_width); + + mTvNotificationHandler.setTvNotificationListener(this); + notificationsUpdated(mTvNotificationHandler.getCurrentNotifications()); + } + + @Override + public void notificationsUpdated(@NonNull SparseArray<StatusBarNotification> notificationList) { + mTvNotificationAdapter.setNotifications(notificationList); + + boolean noNotifications = notificationList.size() == 0; + mNotificationListView.setVisibility(noNotifications ? View.GONE : View.VISIBLE); + mNotificationPlaceholder.setVisibility(noNotifications ? View.VISIBLE : View.GONE); + } + + @Override + public void onNewIntent(Intent intent) { + super.onNewIntent(intent); + maybeClosePanel(intent); + } + + /** + * Handles intents from onCreate and onNewIntent. + * + * @return true if the panel is being closed, false if it is being opened + */ + private boolean maybeClosePanel(Intent intent) { + if (NotificationManager.ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL.equals(intent.getAction()) + || (mPanelAlreadyOpen + && NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL.equals( + intent.getAction()))) { + finish(); + return true; + } + return false; + } + + @Override + public void onDestroy() { + super.onDestroy(); + mTvNotificationHandler.setTvNotificationListener(null); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java index 2c3ea4f452bb..353333f714d4 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java @@ -16,14 +16,22 @@ package com.android.systemui.tv; +import com.android.systemui.SystemUI; import com.android.systemui.dagger.GlobalRootComponent; -import com.android.systemui.wmshell.TvPipModule; +import com.android.systemui.statusbar.tv.notifications.TvNotificationHandler; import dagger.Binds; import dagger.Module; +import dagger.multibindings.ClassKey; +import dagger.multibindings.IntoMap; @Module interface TvSystemUIBinder { @Binds GlobalRootComponent bindGlobalRootComponent(TvGlobalRootComponent globalRootComponent); + + @Binds + @IntoMap + @ClassKey(TvNotificationHandler.class) + SystemUI bindTvNotificationHandler(TvNotificationHandler systemui); } diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java index 8ffc7cf568ff..56a4c203e840 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java @@ -43,6 +43,7 @@ import com.android.systemui.qs.tileimpl.QSFactoryImpl; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsImplementation; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl; import com.android.systemui.statusbar.NotificationShadeWindowController; @@ -62,6 +63,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.tv.notifications.TvNotificationHandler; import javax.inject.Named; @@ -164,4 +166,11 @@ public abstract class TvSystemUIModule { @Binds abstract DozeHost provideDozeHost(DozeServiceHost dozeServiceHost); + + @Provides + @SysUISingleton + static TvNotificationHandler provideTvNotificationHandler(Context context, + NotificationListener notificationListener) { + return new TvNotificationHandler(context, notificationListener); + } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt index 2e8eb0014f30..53d84dba6fb2 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt @@ -17,7 +17,6 @@ package com.android.keyguard import android.animation.ValueAnimator -import android.graphics.Paint import android.testing.AndroidTestingRunner import android.text.Layout import android.text.StaticLayout @@ -53,7 +52,7 @@ class TextAnimatorTest : SysuiTestCase() { val layout = makeLayout("Hello, World", PAINT[0]) val valueAnimator = mock(ValueAnimator::class.java) val textInterpolator = mock(TextInterpolator::class.java) - val paint = arrayListOf(mock(Paint::class.java)) + val paint = arrayListOf(mock(TextPaint::class.java)) `when`(textInterpolator.targetPaint).thenReturn(paint) val textAnimator = TextAnimator(layout, {}).apply { @@ -85,7 +84,7 @@ class TextAnimatorTest : SysuiTestCase() { val layout = makeLayout("Hello, World", PAINT[0]) val valueAnimator = mock(ValueAnimator::class.java) val textInterpolator = mock(TextInterpolator::class.java) - val paint = arrayListOf(mock(Paint::class.java)) + val paint = arrayListOf(mock(TextPaint::class.java)) `when`(textInterpolator.targetPaint).thenReturn(paint) val textAnimator = TextAnimator(layout, {}).apply { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt index 002ba364e9d4..1206dab7b56d 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt @@ -18,11 +18,12 @@ package com.android.keyguard import android.graphics.Bitmap import android.graphics.Canvas -import android.graphics.Paint import android.testing.AndroidTestingRunner import android.text.Layout import android.text.StaticLayout import android.text.TextPaint +import android.text.TextDirectionHeuristic +import android.text.TextDirectionHeuristics import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -31,6 +32,7 @@ import org.junit.runner.RunWith import kotlin.math.ceil private const val TEXT = "Hello, World." +private const val BIDI_TEXT = "abc\u05D0\u05D1\u05D2" private const val BMP_WIDTH = 400 private const val BMP_HEIGHT = 300 @@ -38,11 +40,11 @@ private val PAINT = TextPaint().apply { textSize = 32f } -private val START_PAINT = arrayListOf<Paint>(TextPaint(PAINT).apply { +private val START_PAINT = arrayListOf(TextPaint(PAINT).apply { fontVariationSettings = "'wght' 400" }) -private val END_PAINT = arrayListOf<Paint>(TextPaint(PAINT).apply { +private val END_PAINT = arrayListOf(TextPaint(PAINT).apply { fontVariationSettings = "'wght' 700" }) @@ -50,9 +52,14 @@ private val END_PAINT = arrayListOf<Paint>(TextPaint(PAINT).apply { @SmallTest class TextInterpolatorTest : SysuiTestCase() { - private fun makeLayout(text: String, paint: TextPaint): Layout { + private fun makeLayout( + text: String, + paint: TextPaint, + dir: TextDirectionHeuristic = TextDirectionHeuristics.LTR + ): Layout { val width = ceil(Layout.getDesiredWidth(text, 0, text.length, paint)).toInt() - return StaticLayout.Builder.obtain(text, 0, text.length, paint, width).build() + return StaticLayout.Builder.obtain(text, 0, text.length, paint, width) + .setTextDirection(dir).build() } @Test @@ -69,7 +76,7 @@ class TextInterpolatorTest : SysuiTestCase() { // Just after created TextInterpolator, it should have 0 progress. assertThat(interp.progress).isEqualTo(0f) val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT) - val expected = makeLayout(TEXT, START_PAINT[0] as TextPaint).toBitmap(BMP_WIDTH, BMP_HEIGHT) + val expected = makeLayout(TEXT, START_PAINT[0]).toBitmap(BMP_WIDTH, BMP_HEIGHT) assertThat(expected.sameAs(actual)).isTrue() } @@ -87,7 +94,7 @@ class TextInterpolatorTest : SysuiTestCase() { interp.progress = 1f val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT) - val expected = makeLayout(TEXT, END_PAINT[0] as TextPaint).toBitmap(BMP_WIDTH, BMP_HEIGHT) + val expected = makeLayout(TEXT, END_PAINT[0]).toBitmap(BMP_WIDTH, BMP_HEIGHT) assertThat(expected.sameAs(actual)).isTrue() } @@ -108,9 +115,9 @@ class TextInterpolatorTest : SysuiTestCase() { // end state. interp.progress = 0.5f val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT) - assertThat(actual.sameAs(makeLayout(TEXT, START_PAINT[0] as TextPaint) + assertThat(actual.sameAs(makeLayout(TEXT, START_PAINT[0]) .toBitmap(BMP_WIDTH, BMP_HEIGHT))).isFalse() - assertThat(actual.sameAs(makeLayout(TEXT, END_PAINT[0] as TextPaint) + assertThat(actual.sameAs(makeLayout(TEXT, END_PAINT[0]) .toBitmap(BMP_WIDTH, BMP_HEIGHT))).isFalse() } @@ -135,10 +142,50 @@ class TextInterpolatorTest : SysuiTestCase() { assertThat(expected.sameAs(actual)).isTrue() } + + @Test + fun testBidi_LTR() { + val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.LTR) + + val interp = TextInterpolator(layout) + TextInterpolator.updatePaint(interp.basePaint, START_PAINT) + interp.onBasePaintModified() + + TextInterpolator.updatePaint(interp.targetPaint, END_PAINT) + interp.onTargetPaintModified() + + // Just after created TextInterpolator, it should have 0 progress. + assertThat(interp.progress).isEqualTo(0f) + val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT) + val expected = makeLayout(BIDI_TEXT, START_PAINT[0], TextDirectionHeuristics.LTR) + .toBitmap(BMP_WIDTH, BMP_HEIGHT) + + assertThat(expected.sameAs(actual)).isTrue() + } + + @Test + fun testBidi_RTL() { + val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL) + + val interp = TextInterpolator(layout) + TextInterpolator.updatePaint(interp.basePaint, START_PAINT) + interp.onBasePaintModified() + + TextInterpolator.updatePaint(interp.targetPaint, END_PAINT) + interp.onTargetPaintModified() + + // Just after created TextInterpolator, it should have 0 progress. + assertThat(interp.progress).isEqualTo(0f) + val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT) + val expected = makeLayout(BIDI_TEXT, START_PAINT[0], TextDirectionHeuristics.RTL) + .toBitmap(BMP_WIDTH, BMP_HEIGHT) + + assertThat(expected.sameAs(actual)).isTrue() + } } private fun Layout.toBitmap(width: Int, height: Int) = - Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8).also { draw(Canvas(it)) }!! + Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) }!! private fun TextInterpolator.toBitmap(width: Int, height: Int) = - Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8).also { draw(Canvas(it)) }
\ No newline at end of file + Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index ec0aa4ca89ed..30c4cf6da8d4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -33,6 +33,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; +import android.app.ActivityTaskManager; import android.app.IActivityTaskManager; import android.content.ComponentName; import android.content.Context; @@ -92,7 +93,7 @@ public class AuthControllerTest extends SysuiTestCase { @Mock private StatusBarStateController mStatusBarStateController; @Mock - private IActivityTaskManager mActivityTaskManager; + private ActivityTaskManager mActivityTaskManager; @Mock private FingerprintManager mFingerprintManager; @Mock @@ -553,7 +554,7 @@ public class AuthControllerTest extends SysuiTestCase { TestableAuthController(Context context, CommandQueue commandQueue, StatusBarStateController statusBarStateController, - IActivityTaskManager activityTaskManager, + ActivityTaskManager activityTaskManager, FingerprintManager fingerprintManager, FaceManager faceManager, Provider<UdfpsController> udfpsControllerFactory) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java index 3e44fa4a9b2b..477fe6316399 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java @@ -16,6 +16,7 @@ package com.android.systemui.people.widget; +import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_HIGH; import static org.mockito.ArgumentMatchers.any; @@ -164,7 +165,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); NotificationChannel channel = - mNoMan.createNotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME); + new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT); mNoMan.issueChannelModification(TEST_PACKAGE_A, UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH); @@ -181,7 +182,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); NotificationChannel channel = - mNoMan.createNotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME); + new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT); channel.setConversationId(TEST_PARENT_CHANNEL_ID, TEST_CONVERSATION_ID); mNoMan.issueChannelModification(TEST_PACKAGE_A, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java index 6fa6f31984f2..fd0715bbca29 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java @@ -15,6 +15,7 @@ package com.android.systemui.qs; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; import static org.junit.Assert.assertFalse; import static org.mockito.Matchers.any; @@ -24,6 +25,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.pm.UserInfo; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.VectorDrawable; import android.os.Handler; import android.os.Looper; import android.provider.Settings; @@ -75,6 +78,7 @@ public class QSSecurityFooterTest extends SysuiTestCase { private ViewGroup mRootView; private TextView mFooterText; private TestableImageView mFooterIcon; + private TestableImageView mPrimaryFooterIcon; private QSSecurityFooter mFooter; @Mock private SecurityController mSecurityController; @@ -95,6 +99,7 @@ public class QSSecurityFooterTest extends SysuiTestCase { mActivityStarter, mSecurityController, looper); mFooterText = mRootView.findViewById(R.id.footer_text); mFooterIcon = mRootView.findViewById(R.id.footer_icon); + mPrimaryFooterIcon = mRootView.findViewById(R.id.primary_footer_icon); mFooter.setHostEnvironment(null); } @@ -119,6 +124,7 @@ public class QSSecurityFooterTest extends SysuiTestCase { mFooterText.getText()); assertEquals(View.VISIBLE, mRootView.getVisibility()); assertEquals(View.VISIBLE, mFooterIcon.getVisibility()); + assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility()); // -1 == never set. assertEquals(-1, mFooterIcon.getLastImageResource()); } @@ -136,6 +142,7 @@ public class QSSecurityFooterTest extends SysuiTestCase { mFooterText.getText()); assertEquals(View.VISIBLE, mRootView.getVisibility()); assertEquals(View.VISIBLE, mFooterIcon.getVisibility()); + assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility()); // -1 == never set. assertEquals(-1, mFooterIcon.getLastImageResource()); } @@ -165,6 +172,7 @@ public class QSSecurityFooterTest extends SysuiTestCase { assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring), mFooterText.getText()); assertEquals(View.VISIBLE, mFooterIcon.getVisibility()); + assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility()); // -1 == never set. assertEquals(-1, mFooterIcon.getLastImageResource()); @@ -203,6 +211,7 @@ public class QSSecurityFooterTest extends SysuiTestCase { VPN_PACKAGE), mFooterText.getText()); assertEquals(View.VISIBLE, mFooterIcon.getVisibility()); + assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility()); assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource()); // Same situation, but with organization name set @@ -229,6 +238,7 @@ public class QSSecurityFooterTest extends SysuiTestCase { assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_vpns), mFooterText.getText()); assertEquals(View.VISIBLE, mFooterIcon.getVisibility()); + assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility()); assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource()); // Same situation, but with organization name set @@ -252,6 +262,7 @@ public class QSSecurityFooterTest extends SysuiTestCase { TestableLooper.get(this).processAllMessages(); assertEquals(View.VISIBLE, mFooterIcon.getVisibility()); + assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility()); assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource()); assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring), mFooterText.getText()); @@ -534,12 +545,27 @@ public class QSSecurityFooterTest extends SysuiTestCase { @Test public void testParentalControls() { when(mSecurityController.isParentalControlsEnabled()).thenReturn(true); + + Drawable testDrawable = new VectorDrawable(); + when(mSecurityController.getIcon(any())).thenReturn(testDrawable); + assertNotNull(mSecurityController.getIcon(null)); + mFooter.refreshState(); TestableLooper.get(this).processAllMessages(); assertEquals(mContext.getString(R.string.quick_settings_disclosure_parental_controls), mFooterText.getText()); + assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility()); + + assertEquals(testDrawable, mPrimaryFooterIcon.getDrawable()); + + // Ensure the primary icon is set to gone when parental controls is disabled. + when(mSecurityController.isParentalControlsEnabled()).thenReturn(false); + mFooter.refreshState(); + TestableLooper.get(this).processAllMessages(); + + assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java index 1bfe10c5263b..4507366e3073 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.notification.collection; -import static android.app.NotificationManager.IMPORTANCE_DEFAULT; - import static org.junit.Assert.assertNotNull; import android.app.NotificationChannel; @@ -76,10 +74,6 @@ public class NoManSimulator { } } - public NotificationChannel createNotificationChannel(String id, String name) { - return new NotificationChannel(id, name, IMPORTANCE_DEFAULT); - } - public void issueChannelModification( String pkg, UserHandle user, NotificationChannel channel, int modificationType) { for (NotificationHandler listener : mListeners) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java index 7f48cd1313fe..edf2b4c30ce4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java @@ -69,32 +69,4 @@ public class NotificationInlineImageResolverTest extends SysuiTestCase { assertEquals("Height matches new config", mResolver.mMaxImageHeight, 20); assertEquals("Width matches new config", mResolver.mMaxImageWidth, 15); } - - @Test - public void resolveImage_sizeTooBig() throws IOException { - doReturn(mBitmapDrawable).when(mResolver).resolveImageInternal(mUri); - mResolver.mMaxImageHeight = 5; - mResolver.mMaxImageWidth = 5; - - // original bitmap size is 10x10 - BitmapDrawable resolved = (BitmapDrawable) mResolver.resolveImage(mUri); - Bitmap resolvedBitmap = resolved.getBitmap(); - assertEquals("Bitmap width reduced", 5, resolvedBitmap.getWidth()); - assertEquals("Bitmap height reduced", 5, resolvedBitmap.getHeight()); - assertNotSame("Bitmap replaced", resolvedBitmap, mBitmap); - } - - @Test - public void resolveImage_sizeOK() throws IOException { - doReturn(mBitmapDrawable).when(mResolver).resolveImageInternal(mUri); - mResolver.mMaxImageWidth = 15; - mResolver.mMaxImageHeight = 15; - - // original bitmap size is 10x10 - BitmapDrawable resolved = (BitmapDrawable) mResolver.resolveImage(mUri); - Bitmap resolvedBitmap = resolved.getBitmap(); - assertEquals("Bitmap width unchanged", 10, resolvedBitmap.getWidth()); - assertEquals("Bitmap height unchanged", 10, resolvedBitmap.getHeight()); - assertSame("Bitmap not replaced", resolvedBitmap, mBitmap); - } } diff --git a/services/core/Android.bp b/services/core/Android.bp index fec7ac0dd5bd..614863d6b8f5 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -188,6 +188,13 @@ prebuilt_etc { src: ":services.core.json.gz", } +filegroup { + name: "services.core-sources-deviceconfig-interface", + srcs: [ + "java/com/android/server/utils/DeviceConfigInterface.java" + ], +} + // TODO: Move connectivity service sources to independent directory. filegroup { name: "connectivity-service-srcs", diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 031cc42f99b9..e8ee18c6f4d4 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -4821,15 +4821,6 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private void updateVpnCapabilities(Vpn vpn, @Nullable NetworkCapabilities nc) { - ensureRunningOnConnectivityServiceThread(); - NetworkAgentInfo vpnNai = getNetworkAgentInfoForNetId(vpn.getNetId()); - if (vpnNai == null || nc == null) { - return; - } - updateCapabilities(vpnNai.getCurrentScore(), vpnNai, nc); - } - @Override public boolean updateLockdownVpn() { if (Binder.getCallingUid() != Process.SYSTEM_UID) { @@ -5140,7 +5131,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private void onUserStart(int userId) { + private void onUserStarted(int userId) { synchronized (mVpns) { Vpn userVpn = mVpns.get(userId); if (userVpn != null) { @@ -5155,7 +5146,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private void onUserStop(int userId) { + private void onUserStopped(int userId) { synchronized (mVpns) { Vpn userVpn = mVpns.get(userId); if (userVpn == null) { @@ -5169,28 +5160,22 @@ public class ConnectivityService extends IConnectivityManager.Stub private void onUserAdded(int userId) { mPermissionMonitor.onUserAdded(userId); - Network defaultNetwork = getNetwork(getDefaultNetwork()); synchronized (mVpns) { final int vpnsSize = mVpns.size(); for (int i = 0; i < vpnsSize; i++) { Vpn vpn = mVpns.valueAt(i); vpn.onUserAdded(userId); - NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork); - updateVpnCapabilities(vpn, nc); } } } private void onUserRemoved(int userId) { mPermissionMonitor.onUserRemoved(userId); - Network defaultNetwork = getNetwork(getDefaultNetwork()); synchronized (mVpns) { final int vpnsSize = mVpns.size(); for (int i = 0; i < vpnsSize; i++) { Vpn vpn = mVpns.valueAt(i); vpn.onUserRemoved(userId); - NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork); - updateVpnCapabilities(vpn, nc); } } } @@ -5272,9 +5257,9 @@ public class ConnectivityService extends IConnectivityManager.Stub if (userId == UserHandle.USER_NULL) return; if (Intent.ACTION_USER_STARTED.equals(action)) { - onUserStart(userId); + onUserStarted(userId); } else if (Intent.ACTION_USER_STOPPED.equals(action)) { - onUserStop(userId); + onUserStopped(userId); } else if (Intent.ACTION_USER_ADDED.equals(action)) { onUserAdded(userId); } else if (Intent.ACTION_USER_REMOVED.equals(action)) { @@ -8276,13 +8261,12 @@ public class ConnectivityService extends IConnectivityManager.Stub return false; } - final Network[] underlyingNetworks; - synchronized (mVpns) { - final Vpn vpn = getVpnIfOwner(callbackUid); - underlyingNetworks = (vpn == null) ? null : vpn.getUnderlyingNetworks(); - } - if (underlyingNetworks != null) { - if (Arrays.asList(underlyingNetworks).contains(nai.network)) return true; + for (NetworkAgentInfo virtual : mNetworkAgentInfos.values()) { + if (virtual.supportsUnderlyingNetworks() + && virtual.networkCapabilities.getOwnerUid() == callbackUid + && ArrayUtils.contains(virtual.declaredUnderlyingNetworks, nai.network)) { + return true; + } } // Administrator UIDs also contains the Owner UID diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index f3c5fd8d1064..9bf63cbbb25e 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -1043,7 +1043,7 @@ public class PackageWatchdog { TypedXmlSerializer out = Xml.resolveSerializer(stream); out.startDocument(null, true); out.startTag(null, TAG_PACKAGE_WATCHDOG); - out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION)); + out.attributeInt(null, ATTR_VERSION, DB_VERSION); for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) { mAllObservers.valueAt(oIndex).writeLocked(out); } @@ -1107,7 +1107,7 @@ public class PackageWatchdog { * Does not persist any package failure thresholds. */ @GuardedBy("mLock") - public boolean writeLocked(XmlSerializer out) { + public boolean writeLocked(TypedXmlSerializer out) { try { out.startTag(null, TAG_OBSERVER); out.attribute(null, ATTR_NAME, name); @@ -1225,7 +1225,7 @@ public class PackageWatchdog { * #loadFromFile which in turn is only called on construction of the * singleton PackageWatchdog. **/ - public static ObserverInternal read(XmlPullParser parser, PackageWatchdog watchdog) { + public static ObserverInternal read(TypedXmlPullParser parser, PackageWatchdog watchdog) { String observerName = null; if (TAG_OBSERVER.equals(parser.getName())) { observerName = parser.getAttributeValue(null, ATTR_NAME); @@ -1240,14 +1240,14 @@ public class PackageWatchdog { while (XmlUtils.nextElementWithin(parser, innerDepth)) { if (TAG_PACKAGE.equals(parser.getName())) { try { - String packageName = parser.getAttributeValue(null, ATTR_NAME); - long duration = Long.parseLong( - parser.getAttributeValue(null, ATTR_DURATION)); - long healthCheckDuration = Long.parseLong( - parser.getAttributeValue(null, - ATTR_EXPLICIT_HEALTH_CHECK_DURATION)); - boolean hasPassedHealthCheck = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_PASSED_HEALTH_CHECK)); + String packageName = parser.getAttributeValue( + null, ATTR_NAME); + long duration = parser.getAttributeLong( + null, ATTR_DURATION); + long healthCheckDuration = parser.getAttributeLong( + null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION); + boolean hasPassedHealthCheck = parser.getAttributeBoolean( + null, ATTR_PASSED_HEALTH_CHECK, false); MonitoredPackage pkg = watchdog.newMonitoredPackage(packageName, duration, healthCheckDuration, hasPassedHealthCheck); if (pkg != null) { @@ -1364,14 +1364,12 @@ public class PackageWatchdog { /** Writes the salient fields to disk using {@code out}. */ @GuardedBy("mLock") - public void writeLocked(XmlSerializer out) throws IOException { + public void writeLocked(TypedXmlSerializer out) throws IOException { out.startTag(null, TAG_PACKAGE); out.attribute(null, ATTR_NAME, getName()); - out.attribute(null, ATTR_DURATION, String.valueOf(mDurationMs)); - out.attribute(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION, - String.valueOf(mHealthCheckDurationMs)); - out.attribute(null, ATTR_PASSED_HEALTH_CHECK, - String.valueOf(mHasPassedHealthCheck)); + out.attributeLong(null, ATTR_DURATION, mDurationMs); + out.attributeLong(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION, mHealthCheckDurationMs); + out.attributeBoolean(null, ATTR_PASSED_HEALTH_CHECK, mHasPassedHealthCheck); out.endTag(null, TAG_PACKAGE); } diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java index 2455e76d69a8..d30e9fb002e0 100644 --- a/services/core/java/com/android/server/SensorPrivacyService.java +++ b/services/core/java/com/android/server/SensorPrivacyService.java @@ -107,7 +107,7 @@ public final class SensorPrivacyService extends SystemService { TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream); serializer.startDocument(null, true); serializer.startTag(null, XML_TAG_SENSOR_PRIVACY); - serializer.attribute(null, XML_ATTRIBUTE_ENABLED, String.valueOf(enable)); + serializer.attributeBoolean(null, XML_ATTRIBUTE_ENABLED, enable); serializer.endTag(null, XML_TAG_SENSOR_PRIVACY); serializer.endDocument(); mAtomicFile.finishWrite(outputStream); @@ -180,7 +180,7 @@ public final class SensorPrivacyService extends SystemService { TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream); serializer.startDocument(null, true); serializer.startTag(null, XML_TAG_SENSOR_PRIVACY); - serializer.attribute(null, XML_ATTRIBUTE_ENABLED, String.valueOf(mEnabled)); + serializer.attributeBoolean(null, XML_ATTRIBUTE_ENABLED, mEnabled); serializer.endTag(null, XML_TAG_SENSOR_PRIVACY); serializer.endDocument(); mAtomicFile.finishWrite(outputStream); diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 04a52e049474..dbd27af49b17 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -38,13 +38,8 @@ import static android.os.storage.OnObbStateChangeListener.ERROR_NOT_MOUNTED; import static android.os.storage.OnObbStateChangeListener.ERROR_PERMISSION_DENIED; import static android.os.storage.OnObbStateChangeListener.MOUNTED; import static android.os.storage.OnObbStateChangeListener.UNMOUNTED; -import static android.os.storage.StorageManager.PROP_FORCED_SCOPED_STORAGE_WHITELIST; -import static com.android.internal.util.XmlUtils.readIntAttribute; -import static com.android.internal.util.XmlUtils.readLongAttribute; import static com.android.internal.util.XmlUtils.readStringAttribute; -import static com.android.internal.util.XmlUtils.writeIntAttribute; -import static com.android.internal.util.XmlUtils.writeLongAttribute; import static com.android.internal.util.XmlUtils.writeStringAttribute; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; @@ -144,7 +139,6 @@ import com.android.internal.os.Zygote; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; import com.android.internal.util.DumpUtils; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.HexDump; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; @@ -160,9 +154,7 @@ import com.android.server.wm.ActivityTaskManagerInternal.ScreenObserver; import libcore.io.IoUtils; import libcore.util.EmptyArray; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileDescriptor; @@ -172,7 +164,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.math.BigInteger; -import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.spec.KeySpec; import java.util.ArrayList; @@ -186,7 +177,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -212,21 +202,34 @@ class StorageManagerService extends IStorageManager.Stub private static final String ZRAM_ENABLED_PROPERTY = "persist.sys.zram_enabled"; - private static final boolean ENABLE_ISOLATED_STORAGE = StorageManager.hasIsolatedStorage(); - // A system property to control if obb app data isolation is enabled in vold. private static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY = "persist.sys.vold_app_data_isolation_enabled"; + // TODO(b/169327180): Will be fetched from the server, but for now, we emulate this in + // the system_server since it can write to DeviceConfig and MediaProvider can read it + private static final String PROP_TRANSCODE_ENABLED = "transcode_enabled"; + private static final String PROP_TRANSCODE_DEFAULT = "transcode_default"; + private static final String PROP_TRANSCODE_COMPAT_MANIFEST = "transcode_compat_manifest"; + private static final boolean TRANSCODE_ENABLED_VALUE = false; + // Determines the default behavior of apps when transcode is enabled, AKA, Option A/Option B. + // If true, transcode by default (Option B). If false, don't transcode by default (Option A) + // For dogfood, we go with Option B + private static final boolean TRANSCODE_DEFAULT_VALUE = true; + // Format is <package_name>,<media_capability_bit_mask>,... + // media_capability_bit_mask is defined in MediaProvider/../TranscodeHelper.java: + // FLAG_HEVC = 1 << 0; + // FLAG_SLOW_MOTION = 1 << 1; + // FLAG_HDR_10 = 1 << 2; + // FLAG_HDR_10_PLUS = 1 << 3; + // FLAG_HDR_HLG = 1 << 4; + // FLAG_HDR_DOLBY_VISION = 1 << 5; + private static final String TRANSCODE_COMPAT_MANIFEST_VALUE = + "com.google.android.apps.photos,1"; + // How long we wait to reset storage, if we failed to call onMount on the // external storage service. public static final int FAILED_MOUNT_RESET_TIMEOUT_SECONDS = 10; - /** - * If {@code 1}, enables the isolated storage feature. If {@code -1}, - * disables the isolated storage feature. If {@code 0}, uses the default - * value from the build system. - */ - private static final String ISOLATED_STORAGE_ENABLED = "isolated_storage_enabled"; @GuardedBy("mLock") private final Set<Integer> mFuseMountedUser = new ArraySet<>(); @@ -898,22 +901,18 @@ class StorageManagerService extends IStorageManager.Stub com.android.internal.R.bool.config_zramWriteback)) { ZramWriteback.scheduleZramWriteback(mContext); } - // Toggle isolated-enable system property in response to settings - mContext.getContentResolver().registerContentObserver( - Settings.Global.getUriFor(Settings.Global.ISOLATED_STORAGE_REMOTE), - false /*notifyForDescendants*/, - new ContentObserver(null /* current thread */) { - @Override - public void onChange(boolean selfChange) { - refreshIsolatedStorageSettings(); - } - }); - // For now, simply clone property when it changes - DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, - mContext.getMainExecutor(), (properties) -> { - refreshIsolatedStorageSettings(); - }); - refreshIsolatedStorageSettings(); + + // TODO(b/169327180): Remove after setting up server-side DeviceConfig flags + // Set DeviceConfig values for transcoding that will be read by MediaProvider + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, + PROP_TRANSCODE_ENABLED, String.valueOf(TRANSCODE_ENABLED_VALUE), + false /* makeDefault */); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, + PROP_TRANSCODE_DEFAULT, String.valueOf(TRANSCODE_DEFAULT_VALUE), + false /* makeDefault */); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, + PROP_TRANSCODE_COMPAT_MANIFEST, TRANSCODE_COMPAT_MANIFEST_VALUE, + false /* makeDefault */); } /** @@ -945,38 +944,6 @@ class StorageManagerService extends IStorageManager.Stub } } - private void refreshIsolatedStorageSettings() { - // Always copy value from newer DeviceConfig location - Settings.Global.putString(mResolver, - Settings.Global.ISOLATED_STORAGE_REMOTE, - DeviceConfig.getProperty(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, - ISOLATED_STORAGE_ENABLED)); - - final int local = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.ISOLATED_STORAGE_LOCAL, 0); - final int remote = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.ISOLATED_STORAGE_REMOTE, 0); - - // Walk down precedence chain; we prefer local settings first, then - // remote settings, before finally falling back to hard-coded default. - final boolean res; - if (local == -1) { - res = false; - } else if (local == 1) { - res = true; - } else if (remote == -1) { - res = false; - } else if (remote == 1) { - res = true; - } else { - res = true; - } - - Slog.d(TAG, "Isolated storage local flag " + local + " and remote flag " - + remote + " resolved to " + res); - SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE, Boolean.toString(res)); - } - /** * MediaProvider has a ton of code that makes assumptions about storage * paths never changing, so we outright kill them to pick up new state. @@ -1345,12 +1312,13 @@ class StorageManagerService extends IStorageManager.Stub final int oldState = vol.state; final int newState = state; vol.state = newState; + final VolumeInfo vInfo = new VolumeInfo(vol); final SomeArgs args = SomeArgs.obtain(); - args.arg1 = vol; + args.arg1 = vInfo; args.arg2 = oldState; args.arg3 = newState; mHandler.obtainMessage(H_VOLUME_STATE_CHANGED, args).sendToTarget(); - onVolumeStateChangedLocked(vol, oldState, newState); + onVolumeStateChangedLocked(vInfo, oldState, newState); } } } @@ -1762,11 +1730,6 @@ class StorageManagerService extends IStorageManager.Stub */ public StorageManagerService(Context context) { sSelf = this; - - // Snapshot feature flag used for this boot - SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT, Boolean.toString( - SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, true))); - mVoldAppDataIsolationEnabled = SystemProperties.getBoolean( ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false); mContext = context; @@ -2032,7 +1995,7 @@ class StorageManagerService extends IStorageManager.Stub if (type == START_TAG) { final String tag = in.getName(); if (TAG_VOLUMES.equals(tag)) { - final int version = readIntAttribute(in, ATTR_VERSION, VERSION_INIT); + final int version = in.getAttributeInt(null, ATTR_VERSION, VERSION_INIT); final boolean primaryPhysical = SystemProperties.getBoolean( StorageManager.PROP_PRIMARY_PHYSICAL, false); final boolean validAttr = (version >= VERSION_FIX_PRIMARY) @@ -2067,7 +2030,7 @@ class StorageManagerService extends IStorageManager.Stub TypedXmlSerializer out = Xml.resolveSerializer(fos); out.startDocument(null, true); out.startTag(null, TAG_VOLUMES); - writeIntAttribute(out, ATTR_VERSION, VERSION_FIX_PRIMARY); + out.attributeInt(null, ATTR_VERSION, VERSION_FIX_PRIMARY); writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid); final int size = mRecords.size(); for (int i = 0; i < size; i++) { @@ -2085,31 +2048,33 @@ class StorageManagerService extends IStorageManager.Stub } } - public static VolumeRecord readVolumeRecord(XmlPullParser in) throws IOException { - final int type = readIntAttribute(in, ATTR_TYPE); + public static VolumeRecord readVolumeRecord(TypedXmlPullParser in) + throws IOException, XmlPullParserException { + final int type = in.getAttributeInt(null, ATTR_TYPE); final String fsUuid = readStringAttribute(in, ATTR_FS_UUID); final VolumeRecord meta = new VolumeRecord(type, fsUuid); meta.partGuid = readStringAttribute(in, ATTR_PART_GUID); meta.nickname = readStringAttribute(in, ATTR_NICKNAME); - meta.userFlags = readIntAttribute(in, ATTR_USER_FLAGS); - meta.createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS, 0); - meta.lastSeenMillis = readLongAttribute(in, ATTR_LAST_SEEN_MILLIS, 0); - meta.lastTrimMillis = readLongAttribute(in, ATTR_LAST_TRIM_MILLIS, 0); - meta.lastBenchMillis = readLongAttribute(in, ATTR_LAST_BENCH_MILLIS, 0); + meta.userFlags = in.getAttributeInt(null, ATTR_USER_FLAGS); + meta.createdMillis = in.getAttributeLong(null, ATTR_CREATED_MILLIS, 0); + meta.lastSeenMillis = in.getAttributeLong(null, ATTR_LAST_SEEN_MILLIS, 0); + meta.lastTrimMillis = in.getAttributeLong(null, ATTR_LAST_TRIM_MILLIS, 0); + meta.lastBenchMillis = in.getAttributeLong(null, ATTR_LAST_BENCH_MILLIS, 0); return meta; } - public static void writeVolumeRecord(XmlSerializer out, VolumeRecord rec) throws IOException { + public static void writeVolumeRecord(TypedXmlSerializer out, VolumeRecord rec) + throws IOException { out.startTag(null, TAG_VOLUME); - writeIntAttribute(out, ATTR_TYPE, rec.type); + out.attributeInt(null, ATTR_TYPE, rec.type); writeStringAttribute(out, ATTR_FS_UUID, rec.fsUuid); writeStringAttribute(out, ATTR_PART_GUID, rec.partGuid); writeStringAttribute(out, ATTR_NICKNAME, rec.nickname); - writeIntAttribute(out, ATTR_USER_FLAGS, rec.userFlags); - writeLongAttribute(out, ATTR_CREATED_MILLIS, rec.createdMillis); - writeLongAttribute(out, ATTR_LAST_SEEN_MILLIS, rec.lastSeenMillis); - writeLongAttribute(out, ATTR_LAST_TRIM_MILLIS, rec.lastTrimMillis); - writeLongAttribute(out, ATTR_LAST_BENCH_MILLIS, rec.lastBenchMillis); + out.attributeInt(null, ATTR_USER_FLAGS, rec.userFlags); + out.attributeLong(null, ATTR_CREATED_MILLIS, rec.createdMillis); + out.attributeLong(null, ATTR_LAST_SEEN_MILLIS, rec.lastSeenMillis); + out.attributeLong(null, ATTR_LAST_TRIM_MILLIS, rec.lastTrimMillis); + out.attributeLong(null, ATTR_LAST_BENCH_MILLIS, rec.lastBenchMillis); out.endTag(null, TAG_VOLUME); } @@ -2604,32 +2569,6 @@ class StorageManagerService extends IStorageManager.Stub Binder.restoreCallingIdentity(token); } } - - if ((mask & (StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_ON - | StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_OFF)) != 0) { - final int value; - if ((flags & StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_ON) != 0) { - value = 1; - } else if ((flags & StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_OFF) != 0) { - value = -1; - } else { - value = 0; - } - - final long token = Binder.clearCallingIdentity(); - try { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.ISOLATED_STORAGE_LOCAL, value); - refreshIsolatedStorageSettings(); - - // Perform hard reboot to kick policy into place - mHandler.post(() -> { - mContext.getSystemService(PowerManager.class).reboot(null); - }); - } finally { - Binder.restoreCallingIdentity(token); - } - } } @Override @@ -4149,15 +4088,6 @@ class StorageManagerService extends IStorageManager.Stub } } - private int getMountMode(int uid, String packageName) { - final int mode = getMountModeInternal(uid, packageName); - if (LOCAL_LOGV) { - Slog.v(TAG, "Resolved mode " + mode + " for " + packageName + "/" - + UserHandle.formatUid(uid)); - } - return mode; - } - private int getMountModeInternal(int uid, String packageName) { try { // Get some easy cases out of the way first @@ -4398,17 +4328,6 @@ class StorageManagerService extends IStorageManager.Stub pw.println(); pw.println("Local unlocked users: " + mLocalUnlockedUsers); pw.println("System unlocked users: " + Arrays.toString(mSystemUnlockedUsers)); - - final ContentResolver cr = mContext.getContentResolver(); - pw.println(); - pw.println("Isolated storage, local feature flag: " - + Settings.Global.getInt(cr, Settings.Global.ISOLATED_STORAGE_LOCAL, 0)); - pw.println("Isolated storage, remote feature flag: " - + Settings.Global.getInt(cr, Settings.Global.ISOLATED_STORAGE_REMOTE, 0)); - pw.println("Isolated storage, resolved: " + StorageManager.hasIsolatedStorage()); - pw.println("Forced scoped storage app list: " - + DeviceConfig.getProperty(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, - PROP_FORCED_SCOPED_STORAGE_WHITELIST)); pw.println("isAutomotive:" + mIsAutomotive); } @@ -4460,20 +4379,10 @@ class StorageManagerService extends IStorageManager.Stub } private final class StorageManagerInternalImpl extends StorageManagerInternal { - // Not guarded by a lock. - private final CopyOnWriteArrayList<ExternalStorageMountPolicy> mPolicies = - new CopyOnWriteArrayList<>(); - @GuardedBy("mResetListeners") private final List<StorageManagerInternal.ResetListener> mResetListeners = new ArrayList<>(); - @Override - public void addExternalStoragePolicy(ExternalStorageMountPolicy policy) { - // No locking - CopyOnWriteArrayList - mPolicies.add(policy); - } - /** * Check if fuse is running in target user, if it's running then setup its storage dirs. * Return true if storage dirs are mounted. @@ -4518,30 +4427,12 @@ class StorageManagerService extends IStorageManager.Stub @Override public int getExternalStorageMountMode(int uid, String packageName) { - if (ENABLE_ISOLATED_STORAGE) { - return getMountMode(uid, packageName); - } - try { - if (packageName == null) { - final String[] packagesForUid = mIPackageManager.getPackagesForUid(uid); - packageName = packagesForUid[0]; - } - } catch (RemoteException e) { - // Should not happen - same process - } - // No locking - CopyOnWriteArrayList - int mountMode = Integer.MAX_VALUE; - for (ExternalStorageMountPolicy policy : mPolicies) { - final int policyMode = policy.getMountMode(uid, packageName); - if (policyMode == Zygote.MOUNT_EXTERNAL_NONE) { - return Zygote.MOUNT_EXTERNAL_NONE; - } - mountMode = Math.min(mountMode, policyMode); - } - if (mountMode == Integer.MAX_VALUE) { - return Zygote.MOUNT_EXTERNAL_NONE; + final int mode = getMountModeInternal(uid, packageName); + if (LOCAL_LOGV) { + Slog.v(TAG, "Resolved mode " + mode + " for " + packageName + "/" + + UserHandle.formatUid(uid)); } - return mountMode; + return mode; } @Override @@ -4611,17 +4502,8 @@ class StorageManagerService extends IStorageManager.Stub if (uid == Process.SYSTEM_UID) { return true; } - if (ENABLE_ISOLATED_STORAGE) { - return getMountMode(uid, packageName) != Zygote.MOUNT_EXTERNAL_NONE; - } - // No locking - CopyOnWriteArrayList - for (ExternalStorageMountPolicy policy : mPolicies) { - final boolean policyHasStorage = policy.hasExternalStorage(uid, packageName); - if (!policyHasStorage) { - return false; - } - } - return true; + + return getExternalStorageMountMode(uid, packageName) != Zygote.MOUNT_EXTERNAL_NONE; } private void killAppForOpChange(int code, int uid) { diff --git a/services/core/java/com/android/server/SystemUpdateManagerService.java b/services/core/java/com/android/server/SystemUpdateManagerService.java index 61a7d00faeb8..fcba9b56a541 100644 --- a/services/core/java/com/android/server/SystemUpdateManagerService.java +++ b/services/core/java/com/android/server/SystemUpdateManagerService.java @@ -201,7 +201,7 @@ public class SystemUpdateManagerService extends ISystemUpdateManager.Stub { // Performs I/O work only, without validating the loaded info. @Nullable - private PersistableBundle readInfoFileLocked(XmlPullParser parser) + private PersistableBundle readInfoFileLocked(TypedXmlPullParser parser) throws XmlPullParserException, IOException { int type; while ((type = parser.next()) != END_DOCUMENT) { diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING index ebeec39d6ae6..95af84293377 100644 --- a/services/core/java/com/android/server/TEST_MAPPING +++ b/services/core/java/com/android/server/TEST_MAPPING @@ -29,6 +29,10 @@ { "name": "CtsScopedStorageHostTest", "file_patterns": ["StorageManagerService\\.java"] + }, + { + "name": "CtsScopedStorageDeviceOnlyTest", + "file_patterns": ["StorageManagerService\\.java"] } ] } diff --git a/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java b/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java index 1c77a7f451b4..b379b5dc681a 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java +++ b/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java @@ -23,25 +23,21 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.content.pm.Signature; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import android.util.PackageUtils; import android.util.Pair; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; + import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; -import com.android.server.accounts.AccountsDb.DeDatabaseHelper; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -163,7 +159,7 @@ public final class AccountManagerBackupHelper { } try { ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); - final XmlSerializer serializer = new FastXmlSerializer(); + final TypedXmlSerializer serializer = Xml.newFastSerializer(); serializer.setOutput(dataStream, StandardCharsets.UTF_8.name()); serializer.startDocument(null, true); serializer.startTag(null, TAG_PERMISSIONS); @@ -216,7 +212,7 @@ public final class AccountManagerBackupHelper { public void restoreAccountAccessPermissions(byte[] data, int userId) { try { ByteArrayInputStream dataStream = new ByteArrayInputStream(data); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(dataStream, StandardCharsets.UTF_8.name()); PackageManager packageManager = mAccountManagerService.mContext.getPackageManager(); diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java index f59af76ead8f..d99b195351f1 100644 --- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java +++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java @@ -1947,8 +1947,7 @@ public class AdbDebuggingManager { + tagName); return keyMap; } - int keystoreVersion = Integer.parseInt( - parser.getAttributeValue(null, XML_ATTRIBUTE_VERSION)); + int keystoreVersion = parser.getAttributeInt(null, XML_ATTRIBUTE_VERSION); if (keystoreVersion > MAX_SUPPORTED_KEYSTORE_VERSION) { Slog.e(TAG, "Keystore version=" + keystoreVersion + " not supported (max_supported=" @@ -2068,8 +2067,7 @@ public class AdbDebuggingManager { + tagName); return trustedNetworks; } - int keystoreVersion = Integer.parseInt( - parser.getAttributeValue(null, XML_ATTRIBUTE_VERSION)); + int keystoreVersion = parser.getAttributeInt(null, XML_ATTRIBUTE_VERSION); if (keystoreVersion > MAX_SUPPORTED_KEYSTORE_VERSION) { Slog.e(TAG, "Keystore version=" + keystoreVersion + " not supported (max_supported=" @@ -2148,7 +2146,7 @@ public class AdbDebuggingManager { serializer.startDocument(null, true); serializer.startTag(null, XML_KEYSTORE_START_TAG); - serializer.attribute(null, XML_ATTRIBUTE_VERSION, String.valueOf(KEYSTORE_VERSION)); + serializer.attributeInt(null, XML_ATTRIBUTE_VERSION, KEYSTORE_VERSION); for (Map.Entry<String, Long> keyEntry : mKeyMap.entrySet()) { serializer.startTag(null, XML_TAG_ADB_KEY); serializer.attribute(null, XML_ATTRIBUTE_KEY, keyEntry.getKey()); diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 872902626fb4..4165200d837a 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -4987,14 +4987,16 @@ public final class ActiveServices { * - the first arg isn't the flattened component name of an existing service: * dump all services whose component contains the first arg as a substring */ - protected boolean dumpService(FileDescriptor fd, PrintWriter pw, final String name, + protected boolean dumpService(FileDescriptor fd, PrintWriter pw, String name, int[] users, String[] args, int opti, boolean dumpAll) { final ArrayList<ServiceRecord> services = new ArrayList<>(); final Predicate<ServiceRecord> filter = DumpUtils.filterRecord(name); synchronized (mAm) { - int[] users = mAm.mUserController.getUsers(); + if (users == null) { + users = mAm.mUserController.getUsers(); + } for (int user : users) { ServiceMap smap = mServiceMap.get(user); @@ -5039,11 +5041,13 @@ public final class ActiveServices { String innerPrefix = prefix + " "; synchronized (mAm) { pw.print(prefix); pw.print("SERVICE "); - pw.print(r.shortInstanceName); pw.print(" "); - pw.print(Integer.toHexString(System.identityHashCode(r))); - pw.print(" pid="); - if (r.app != null) pw.println(r.app.pid); - else pw.println("(not running)"); + pw.print(r.shortInstanceName); pw.print(" "); + pw.print(Integer.toHexString(System.identityHashCode(r))); + pw.print(" pid="); + if (r.app != null) { + pw.print(r.app.pid); + pw.print(" user="); pw.println(r.userId); + } else pw.println("(not running)"); if (dumpAll) { r.dump(pw, innerPrefix); } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 75e8b13ccc0d..df1081e43262 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -5644,7 +5644,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public List<RunningTaskInfo> getTasks(int maxNum) { - return mActivityTaskManager.getTasks(maxNum); + return mActivityTaskManager.getTasks(maxNum, false /* filterForVisibleRecents */); } @Override @@ -8664,17 +8664,30 @@ public class ActivityManagerService extends IActivityManager.Stub } else if ("service".equals(cmd)) { String[] newArgs; String name; + int[] users = null; if (opti >= args.length) { name = null; newArgs = EMPTY_STRING_ARRAY; } else { name = args[opti]; opti++; + if ("--user".equals(name) && opti < args.length) { + int userId = UserHandle.parseUserArg(args[opti]); + opti++; + if (userId != UserHandle.USER_ALL) { + if (userId == UserHandle.USER_CURRENT) { + userId = getCurrentUser().id; + } + users = new int[] { userId }; + } + name = args[opti]; + opti++; + } newArgs = new String[args.length - opti]; if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti); } - if (!mServices.dumpService(fd, pw, name, newArgs, 0, dumpAll)) { + if (!mServices.dumpService(fd, pw, name, users, newArgs, 0, dumpAll)) { pw.println("No services match: " + name); pw.println("Use -h for help."); } diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 28afcbbb2a86..7299e814b020 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -88,6 +88,8 @@ public class SettingsToPropertiesMapper { DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT, DeviceConfig.NAMESPACE_RUNTIME_NATIVE, DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT, + DeviceConfig.NAMESPACE_STATSD_NATIVE, + DeviceConfig.NAMESPACE_STATSD_NATIVE_BOOT, DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT, }; diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index da4704097ad0..d4e2d27ca7a1 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -124,7 +124,6 @@ import android.os.ShellCommand; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; -import android.os.storage.StorageManager; import android.os.storage.StorageManagerInternal; import android.provider.Settings; import android.util.ArrayMap; @@ -155,10 +154,8 @@ import com.android.internal.app.IAppOpsService; import com.android.internal.app.IAppOpsStartedCallback; import com.android.internal.app.MessageSamplingConfig; import com.android.internal.compat.IPlatformCompat; -import com.android.internal.os.Zygote; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.internal.util.function.pooled.PooledLambda; @@ -175,7 +172,6 @@ import org.json.JSONException; import org.json.JSONObject; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileDescriptor; @@ -185,7 +181,6 @@ import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.time.Instant; import java.time.temporal.ChronoUnit; @@ -1057,19 +1052,20 @@ public class AppOpsService extends IAppOpsService.Stub { } int numInProgressEvents = mInProgressEvents.size(); + List<IBinder> binders = new ArrayList<>(mInProgressEvents.keySet()); for (int i = 0; i < numInProgressEvents; i++) { - InProgressStartOpEvent event = mInProgressEvents.valueAt(i); + InProgressStartOpEvent event = mInProgressEvents.get(binders.get(i)); - if (event.getUidState() != newState) { + if (event != null && event.getUidState() != newState) { try { // Remove all but one unfinished start count and then call finished() to // remove start event object int numPreviousUnfinishedStarts = event.numUnfinishedStarts; event.numUnfinishedStarts = 1; - finished(event.getClientId(), false); - OpEventProxyInfo proxy = event.getProxy(); + finished(event.getClientId(), false); + // Call started() to add a new start event object and then add the // previously removed unfinished start counts back if (proxy != null) { @@ -1079,7 +1075,11 @@ public class AppOpsService extends IAppOpsService.Stub { started(event.getClientId(), Process.INVALID_UID, null, null, newState, OP_FLAG_SELF, false); } - event.numUnfinishedStarts += numPreviousUnfinishedStarts - 1; + + InProgressStartOpEvent newEvent = mInProgressEvents.get(binders.get(i)); + if (newEvent != null) { + newEvent.numUnfinishedStarts += numPreviousUnfinishedStarts - 1; + } } catch (RemoteException e) { if (DEBUG) Slog.e(TAG, "Cannot switch to new uidState " + newState); } @@ -1764,26 +1764,6 @@ public class AppOpsService extends IAppOpsService.Stub { } }); - if (!StorageManager.hasIsolatedStorage()) { - StorageManagerInternal storageManagerInternal = LocalServices.getService( - StorageManagerInternal.class); - storageManagerInternal.addExternalStoragePolicy( - new StorageManagerInternal.ExternalStorageMountPolicy() { - @Override - public int getMountMode(int uid, String packageName) { - if (Process.isIsolated(uid)) { - return Zygote.MOUNT_EXTERNAL_NONE; - } - return Zygote.MOUNT_EXTERNAL_DEFAULT; - } - - @Override - public boolean hasExternalStorage(int uid, String packageName) { - final int mountMode = getMountMode(uid, packageName); - return mountMode != Zygote.MOUNT_EXTERNAL_NONE; - } - }); - } mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); } @@ -4285,10 +4265,7 @@ public class AppOpsService extends IAppOpsService.Stub { throw new IllegalStateException("no start tag found"); } - final String versionString = parser.getAttributeValue(null, "v"); - if (versionString != null) { - oldVersion = Integer.parseInt(versionString); - } + oldVersion = parser.getAttributeInt(null, "v", NO_VERSION); int outerDepth = parser.getDepth(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT @@ -4388,9 +4365,9 @@ public class AppOpsService extends IAppOpsService.Stub { scheduleFastWriteLocked(); } - private void readUidOps(XmlPullParser parser) throws NumberFormatException, + private void readUidOps(TypedXmlPullParser parser) throws NumberFormatException, XmlPullParserException, IOException { - final int uid = Integer.parseInt(parser.getAttributeValue(null, "n")); + final int uid = parser.getAttributeInt(null, "n"); int outerDepth = parser.getDepth(); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT @@ -4401,8 +4378,8 @@ public class AppOpsService extends IAppOpsService.Stub { String tagName = parser.getName(); if (tagName.equals("op")) { - final int code = Integer.parseInt(parser.getAttributeValue(null, "n")); - final int mode = Integer.parseInt(parser.getAttributeValue(null, "m")); + final int code = parser.getAttributeInt(null, "n"); + final int mode = parser.getAttributeInt(null, "m"); setUidMode(code, uid, mode); } else { Slog.w(TAG, "Unknown element under <uid-ops>: " @@ -4412,7 +4389,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } - private void readPackage(XmlPullParser parser) + private void readPackage(TypedXmlPullParser parser) throws NumberFormatException, XmlPullParserException, IOException { String pkgName = parser.getAttributeValue(null, "n"); int outerDepth = parser.getDepth(); @@ -4434,9 +4411,9 @@ public class AppOpsService extends IAppOpsService.Stub { } } - private void readUid(XmlPullParser parser, String pkgName) + private void readUid(TypedXmlPullParser parser, String pkgName) throws NumberFormatException, XmlPullParserException, IOException { - int uid = Integer.parseInt(parser.getAttributeValue(null, "n")); + int uid = parser.getAttributeInt(null, "n"); final UidState uidState = getUidStateLocked(uid, true); int outerDepth = parser.getDepth(); int type; @@ -4457,19 +4434,20 @@ public class AppOpsService extends IAppOpsService.Stub { uidState.evalForegroundOps(mOpModeWatchers); } - private void readAttributionOp(XmlPullParser parser, @NonNull Op parent, - @Nullable String attribution) throws NumberFormatException, IOException { + private void readAttributionOp(TypedXmlPullParser parser, @NonNull Op parent, + @Nullable String attribution) + throws NumberFormatException, IOException, XmlPullParserException { final AttributedOp attributedOp = parent.getOrCreateAttribution(parent, attribution); - final long key = XmlUtils.readLongAttribute(parser, "n"); + final long key = parser.getAttributeLong(null, "n"); final int uidState = extractUidStateFromKey(key); final int opFlags = extractFlagsFromKey(key); - final long accessTime = XmlUtils.readLongAttribute(parser, "t", 0); - final long rejectTime = XmlUtils.readLongAttribute(parser, "r", 0); - final long accessDuration = XmlUtils.readLongAttribute(parser, "d", -1); + final long accessTime = parser.getAttributeLong(null, "t", 0); + final long rejectTime = parser.getAttributeLong(null, "r", 0); + final long accessDuration = parser.getAttributeLong(null, "d", -1); final String proxyPkg = XmlUtils.readStringAttribute(parser, "pp"); - final int proxyUid = XmlUtils.readIntAttribute(parser, "pu", Process.INVALID_UID); + final int proxyUid = parser.getAttributeInt(null, "pu", Process.INVALID_UID); final String proxyAttributionTag = XmlUtils.readStringAttribute(parser, "pc"); if (accessTime > 0) { @@ -4481,14 +4459,13 @@ public class AppOpsService extends IAppOpsService.Stub { } } - private void readOp(XmlPullParser parser, @NonNull UidState uidState, @NonNull String pkgName) - throws NumberFormatException, - XmlPullParserException, IOException { - int opCode = Integer.parseInt(parser.getAttributeValue(null, "n")); + private void readOp(TypedXmlPullParser parser, + @NonNull UidState uidState, @NonNull String pkgName) + throws NumberFormatException, XmlPullParserException, IOException { + int opCode = parser.getAttributeInt(null, "n"); Op op = new Op(uidState, pkgName, opCode, uidState.uid); - final int mode = XmlUtils.readIntAttribute(parser, "m", - AppOpsManager.opToDefaultMode(op.op)); + final int mode = parser.getAttributeInt(null, "m", AppOpsManager.opToDefaultMode(op.op)); op.mode = mode; int outerDepth = parser.getDepth(); @@ -4535,7 +4512,7 @@ public class AppOpsService extends IAppOpsService.Stub { TypedXmlSerializer out = Xml.resolveSerializer(stream); out.startDocument(null, true); out.startTag(null, "app-ops"); - out.attribute(null, "v", String.valueOf(CURRENT_VERSION)); + out.attributeInt(null, "v", CURRENT_VERSION); SparseArray<SparseIntArray> uidStatesClone; synchronized (this) { @@ -4565,15 +4542,14 @@ public class AppOpsService extends IAppOpsService.Stub { SparseIntArray opModes = uidStatesClone.valueAt(uidStateNum); if (opModes != null && opModes.size() > 0) { out.startTag(null, "uid"); - out.attribute(null, "n", - Integer.toString(uidStatesClone.keyAt(uidStateNum))); + out.attributeInt(null, "n", uidStatesClone.keyAt(uidStateNum)); final int opCount = opModes.size(); for (int opCountNum = 0; opCountNum < opCount; opCountNum++) { final int op = opModes.keyAt(opCountNum); final int mode = opModes.valueAt(opCountNum); out.startTag(null, "op"); - out.attribute(null, "n", Integer.toString(op)); - out.attribute(null, "m", Integer.toString(mode)); + out.attributeInt(null, "n", op); + out.attributeInt(null, "m", mode); out.endTag(null, "op"); } out.endTag(null, "uid"); @@ -4593,14 +4569,14 @@ public class AppOpsService extends IAppOpsService.Stub { out.attribute(null, "n", lastPkg); } out.startTag(null, "uid"); - out.attribute(null, "n", Integer.toString(pkg.getUid())); + out.attributeInt(null, "n", pkg.getUid()); List<AppOpsManager.OpEntry> ops = pkg.getOps(); for (int j=0; j<ops.size(); j++) { AppOpsManager.OpEntry op = ops.get(j); out.startTag(null, "op"); - out.attribute(null, "n", Integer.toString(op.getOp())); + out.attributeInt(null, "n", op.getOp()); if (op.getMode() != AppOpsManager.opToDefaultMode(op.getOp())) { - out.attribute(null, "m", Integer.toString(op.getMode())); + out.attributeInt(null, "m", op.getMode()); } for (String attributionTag : op.getAttributedOpEntries().keySet()) { @@ -4644,15 +4620,15 @@ public class AppOpsService extends IAppOpsService.Stub { if (attributionTag != null) { out.attribute(null, "id", attributionTag); } - out.attribute(null, "n", Long.toString(key)); + out.attributeLong(null, "n", key); if (accessTime > 0) { - out.attribute(null, "t", Long.toString(accessTime)); + out.attributeLong(null, "t", accessTime); } if (rejectTime > 0) { - out.attribute(null, "r", Long.toString(rejectTime)); + out.attributeLong(null, "r", rejectTime); } if (accessDuration > 0) { - out.attribute(null, "d", Long.toString(accessDuration)); + out.attributeLong(null, "d", accessDuration); } if (proxyPkg != null) { out.attribute(null, "pp", proxyPkg); @@ -4661,7 +4637,7 @@ public class AppOpsService extends IAppOpsService.Stub { out.attribute(null, "pc", proxyAttributionTag); } if (proxyUid >= 0) { - out.attribute(null, "pu", Integer.toString(proxyUid)); + out.attributeInt(null, "pu", proxyUid); } out.endTag(null, "st"); } diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java index f49b5dca2b08..676fcd0bdc87 100644 --- a/services/core/java/com/android/server/appop/HistoricalRegistry.java +++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java @@ -49,6 +49,8 @@ import android.util.ArraySet; import android.util.LongSparseArray; import android.util.Slog; import android.util.TimeUtils; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.GuardedBy; @@ -1184,19 +1186,18 @@ final class HistoricalRegistry { } List<HistoricalOps> allOps = null; try (FileInputStream stream = new FileInputStream(file)) { - final XmlPullParser parser = Xml.newPullParser(); - parser.setInput(stream, StandardCharsets.UTF_8.name()); + final TypedXmlPullParser parser = Xml.resolvePullParser(stream); XmlUtils.beginDocument(parser, TAG_HISTORY); // We haven't released version 1 and have more detailed // accounting - just nuke the current state - final int version = XmlUtils.readIntAttribute(parser, ATTR_VERSION); + final int version = parser.getAttributeInt(null, ATTR_VERSION); if (CURRENT_VERSION == 2 && version < CURRENT_VERSION) { throw new IllegalStateException("Dropping unsupported history " + "version 1 for file:" + file); } - final long overflowMillis = XmlUtils.readLongAttribute(parser, ATTR_OVERFLOW, 0); + final long overflowMillis = parser.getAttributeLong(null, ATTR_OVERFLOW, 0); final int depth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, depth)) { if (TAG_OPS.equals(parser.getName())) { @@ -1235,15 +1236,16 @@ final class HistoricalRegistry { } private @Nullable HistoricalOps readeHistoricalOpsDLocked( - @NonNull XmlPullParser parser, int filterUid, @Nullable String filterPackageName, + @NonNull TypedXmlPullParser parser, int filterUid, + @Nullable String filterPackageName, @Nullable String filterAttributionTag, @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter, long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags, @Nullable long[] cumulativeOverflowMillis) throws IOException, XmlPullParserException { - final long beginTimeMillis = XmlUtils.readLongAttribute(parser, ATTR_BEGIN_TIME, 0) + final long beginTimeMillis = parser.getAttributeLong(null, ATTR_BEGIN_TIME, 0) + (cumulativeOverflowMillis != null ? cumulativeOverflowMillis[0] : 0); - final long endTimeMillis = XmlUtils.readLongAttribute(parser, ATTR_END_TIME, 0) + final long endTimeMillis = parser.getAttributeLong(null, ATTR_END_TIME, 0) + (cumulativeOverflowMillis != null ? cumulativeOverflowMillis[0] : 0); // Keep reading as subsequent records may start matching if (filterEndTimeMillis < beginTimeMillis) { @@ -1280,12 +1282,12 @@ final class HistoricalRegistry { } private @Nullable HistoricalOps readHistoricalUidOpsDLocked( - @Nullable HistoricalOps ops, @NonNull XmlPullParser parser, int filterUid, + @Nullable HistoricalOps ops, @NonNull TypedXmlPullParser parser, int filterUid, @Nullable String filterPackageName, @Nullable String filterAttributionTag, @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter, @OpFlags int filterFlags, double filterScale) throws IOException, XmlPullParserException { - final int uid = XmlUtils.readIntAttribute(parser, ATTR_NAME); + final int uid = parser.getAttributeInt(null, ATTR_NAME); if ((filter & FILTER_BY_UID) != 0 && filterUid != uid) { XmlUtils.skipCurrentTag(parser); return null; @@ -1305,7 +1307,7 @@ final class HistoricalRegistry { } private @Nullable HistoricalOps readHistoricalPackageOpsDLocked( - @Nullable HistoricalOps ops, int uid, @NonNull XmlPullParser parser, + @Nullable HistoricalOps ops, int uid, @NonNull TypedXmlPullParser parser, @Nullable String filterPackageName, @Nullable String filterAttributionTag, @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter, @OpFlags int filterFlags, double filterScale) @@ -1331,7 +1333,7 @@ final class HistoricalRegistry { private @Nullable HistoricalOps readHistoricalAttributionOpsDLocked( @Nullable HistoricalOps ops, int uid, String packageName, - @NonNull XmlPullParser parser, @Nullable String filterAttributionTag, + @NonNull TypedXmlPullParser parser, @Nullable String filterAttributionTag, @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter, @OpFlags int filterFlags, double filterScale) throws IOException, XmlPullParserException { @@ -1357,11 +1359,11 @@ final class HistoricalRegistry { private @Nullable HistoricalOps readHistoricalOpDLocked(@Nullable HistoricalOps ops, int uid, @NonNull String packageName, @Nullable String attributionTag, - @NonNull XmlPullParser parser, @Nullable String[] filterOpNames, + @NonNull TypedXmlPullParser parser, @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter, @OpFlags int filterFlags, double filterScale) throws IOException, XmlPullParserException { - final int op = XmlUtils.readIntAttribute(parser, ATTR_NAME); + final int op = parser.getAttributeInt(null, ATTR_NAME); if ((filter & FILTER_BY_OP_NAMES) != 0 && !ArrayUtils.contains(filterOpNames, AppOpsManager.opToPublicName(op))) { XmlUtils.skipCurrentTag(parser); @@ -1382,15 +1384,15 @@ final class HistoricalRegistry { private @Nullable HistoricalOps readStateDLocked(@Nullable HistoricalOps ops, int uid, @NonNull String packageName, @Nullable String attributionTag, int op, - @NonNull XmlPullParser parser, @OpFlags int filterFlags, double filterScale) - throws IOException { - final long key = XmlUtils.readLongAttribute(parser, ATTR_NAME); + @NonNull TypedXmlPullParser parser, @OpFlags int filterFlags, double filterScale) + throws IOException, XmlPullParserException { + final long key = parser.getAttributeLong(null, ATTR_NAME); final int flags = AppOpsManager.extractFlagsFromKey(key) & filterFlags; if (flags == 0) { return null; } final int uidState = AppOpsManager.extractUidStateFromKey(key); - long accessCount = XmlUtils.readLongAttribute(parser, ATTR_ACCESS_COUNT, 0); + long accessCount = parser.getAttributeLong(null, ATTR_ACCESS_COUNT, 0); if (accessCount > 0) { if (!Double.isNaN(filterScale)) { accessCount = (long) HistoricalOps.round( @@ -1402,7 +1404,7 @@ final class HistoricalRegistry { ops.increaseAccessCount(op, uid, packageName, attributionTag, uidState, flags, accessCount); } - long rejectCount = XmlUtils.readLongAttribute(parser, ATTR_REJECT_COUNT, 0); + long rejectCount = parser.getAttributeLong(null, ATTR_REJECT_COUNT, 0); if (rejectCount > 0) { if (!Double.isNaN(filterScale)) { rejectCount = (long) HistoricalOps.round( @@ -1414,7 +1416,7 @@ final class HistoricalRegistry { ops.increaseRejectCount(op, uid, packageName, attributionTag, uidState, flags, rejectCount); } - long accessDuration = XmlUtils.readLongAttribute(parser, ATTR_ACCESS_DURATION, 0); + long accessDuration = parser.getAttributeLong(null, ATTR_ACCESS_DURATION, 0); if (accessDuration > 0) { if (!Double.isNaN(filterScale)) { accessDuration = (long) HistoricalOps.round( @@ -1433,16 +1435,14 @@ final class HistoricalRegistry { long intervalOverflowMillis, @NonNull File file) throws IOException { final FileOutputStream output = sHistoricalAppOpsDir.openWrite(file); try { - final XmlSerializer serializer = Xml.newSerializer(); - serializer.setOutput(output, StandardCharsets.UTF_8.name()); + final TypedXmlSerializer serializer = Xml.resolveSerializer(output); serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); serializer.startDocument(null, true); serializer.startTag(null, TAG_HISTORY); - serializer.attribute(null, ATTR_VERSION, String.valueOf(CURRENT_VERSION)); + serializer.attributeInt(null, ATTR_VERSION, CURRENT_VERSION); if (intervalOverflowMillis != 0) { - serializer.attribute(null, ATTR_OVERFLOW, - Long.toString(intervalOverflowMillis)); + serializer.attributeLong(null, ATTR_OVERFLOW, intervalOverflowMillis); } if (allOps != null) { final int opsCount = allOps.size(); @@ -1461,10 +1461,10 @@ final class HistoricalRegistry { } private void writeHistoricalOpDLocked(@NonNull HistoricalOps ops, - @NonNull XmlSerializer serializer) throws IOException { + @NonNull TypedXmlSerializer serializer) throws IOException { serializer.startTag(null, TAG_OPS); - serializer.attribute(null, ATTR_BEGIN_TIME, Long.toString(ops.getBeginTimeMillis())); - serializer.attribute(null, ATTR_END_TIME, Long.toString(ops.getEndTimeMillis())); + serializer.attributeLong(null, ATTR_BEGIN_TIME, ops.getBeginTimeMillis()); + serializer.attributeLong(null, ATTR_END_TIME, ops.getEndTimeMillis()); final int uidCount = ops.getUidCount(); for (int i = 0; i < uidCount; i++) { final HistoricalUidOps uidOp = ops.getUidOpsAt(i); @@ -1474,9 +1474,9 @@ final class HistoricalRegistry { } private void writeHistoricalUidOpsDLocked(@NonNull HistoricalUidOps uidOps, - @NonNull XmlSerializer serializer) throws IOException { + @NonNull TypedXmlSerializer serializer) throws IOException { serializer.startTag(null, TAG_UID); - serializer.attribute(null, ATTR_NAME, Integer.toString(uidOps.getUid())); + serializer.attributeInt(null, ATTR_NAME, uidOps.getUid()); final int packageCount = uidOps.getPackageCount(); for (int i = 0; i < packageCount; i++) { final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(i); @@ -1486,7 +1486,7 @@ final class HistoricalRegistry { } private void writeHistoricalPackageOpsDLocked(@NonNull HistoricalPackageOps packageOps, - @NonNull XmlSerializer serializer) throws IOException { + @NonNull TypedXmlSerializer serializer) throws IOException { serializer.startTag(null, TAG_PACKAGE); serializer.attribute(null, ATTR_NAME, packageOps.getPackageName()); final int numAttributions = packageOps.getAttributedOpsCount(); @@ -1499,7 +1499,7 @@ final class HistoricalRegistry { private void writeHistoricalAttributionOpsDLocked( @NonNull AppOpsManager.AttributedHistoricalOps attributionOps, - @NonNull XmlSerializer serializer) throws IOException { + @NonNull TypedXmlSerializer serializer) throws IOException { serializer.startTag(null, TAG_ATTRIBUTION); XmlUtils.writeStringAttribute(serializer, ATTR_NAME, attributionOps.getTag()); final int opCount = attributionOps.getOpCount(); @@ -1511,13 +1511,13 @@ final class HistoricalRegistry { } private void writeHistoricalOpDLocked(@NonNull HistoricalOp op, - @NonNull XmlSerializer serializer) throws IOException { + @NonNull TypedXmlSerializer serializer) throws IOException { final LongSparseArray keys = op.collectKeys(); if (keys == null || keys.size() <= 0) { return; } serializer.startTag(null, TAG_OP); - serializer.attribute(null, ATTR_NAME, Integer.toString(op.getOpCode())); + serializer.attributeInt(null, ATTR_NAME, op.getOpCode()); final int keyCount = keys.size(); for (int i = 0; i < keyCount; i++) { writeStateOnLocked(op, keys.keyAt(i), serializer); @@ -1526,7 +1526,7 @@ final class HistoricalRegistry { } private void writeStateOnLocked(@NonNull HistoricalOp op, long key, - @NonNull XmlSerializer serializer) throws IOException { + @NonNull TypedXmlSerializer serializer) throws IOException { final int uidState = AppOpsManager.extractUidStateFromKey(key); final int flags = AppOpsManager.extractFlagsFromKey(key); @@ -1539,15 +1539,15 @@ final class HistoricalRegistry { } serializer.startTag(null, TAG_STATE); - serializer.attribute(null, ATTR_NAME, Long.toString(key)); + serializer.attributeLong(null, ATTR_NAME, key); if (accessCount > 0) { - serializer.attribute(null, ATTR_ACCESS_COUNT, Long.toString(accessCount)); + serializer.attributeLong(null, ATTR_ACCESS_COUNT, accessCount); } if (rejectCount > 0) { - serializer.attribute(null, ATTR_REJECT_COUNT, Long.toString(rejectCount)); + serializer.attributeLong(null, ATTR_REJECT_COUNT, rejectCount); } if (accessDuration > 0) { - serializer.attribute(null, ATTR_ACCESS_DURATION, Long.toString(accessDuration)); + serializer.attributeLong(null, ATTR_ACCESS_DURATION, accessDuration); } serializer.endTag(null, TAG_STATE); } diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java index ff410fcd2f66..0943c9ca17fe 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -49,7 +49,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> private final boolean mIsStrongBiometric; private final boolean mRequireConfirmation; - private final IActivityTaskManager mActivityTaskManager; + private final ActivityTaskManager mActivityTaskManager; @Nullable private final TaskStackListener mTaskStackListener; private final LockoutTracker mLockoutTracker; private final boolean mIsRestricted; @@ -71,7 +71,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> mIsStrongBiometric = isStrongBiometric; mOperationId = operationId; mRequireConfirmation = requireConfirmation; - mActivityTaskManager = ActivityTaskManager.getService(); + mActivityTaskManager = ActivityTaskManager.getInstance(); mTaskStackListener = taskStackListener; mLockoutTracker = lockoutTracker; mIsRestricted = restricted; @@ -146,29 +146,24 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> // Ensure authentication only succeeds if the client activity is on top or is keyguard. boolean isBackgroundAuth = false; if (authenticated && !Utils.isKeyguard(getContext(), getOwnerString())) { - try { - final List<ActivityManager.RunningTaskInfo> tasks = - mActivityTaskManager.getTasks(1); - if (tasks == null || tasks.isEmpty()) { - Slog.e(TAG, "No running tasks reported"); + final List<ActivityManager.RunningTaskInfo> tasks = + mActivityTaskManager.getTasks(1); + if (tasks == null || tasks.isEmpty()) { + Slog.e(TAG, "No running tasks reported"); + isBackgroundAuth = true; + } else { + final ComponentName topActivity = tasks.get(0).topActivity; + if (topActivity == null) { + Slog.e(TAG, "Unable to get top activity"); isBackgroundAuth = true; } else { - final ComponentName topActivity = tasks.get(0).topActivity; - if (topActivity == null) { - Slog.e(TAG, "Unable to get top activity"); + final String topPackage = topActivity.getPackageName(); + if (!topPackage.contentEquals(getOwnerString())) { + Slog.e(TAG, "Background authentication detected, top: " + topPackage + + ", client: " + this); isBackgroundAuth = true; - } else { - final String topPackage = topActivity.getPackageName(); - if (!topPackage.contentEquals(getOwnerString())) { - Slog.e(TAG, "Background authentication detected, top: " + topPackage - + ", client: " + this); - isBackgroundAuth = true; - } } } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to get running tasks", e); - isBackgroundAuth = true; } } @@ -198,11 +193,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> } if (mTaskStackListener != null) { - try { - mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); - } catch (RemoteException e) { - Slog.e(TAG, "Could not unregister task stack listener", e); - } + mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); } final byte[] byteToken = new byte[hardwareAuthToken.size()]; @@ -290,11 +281,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> } if (mTaskStackListener != null) { - try { - mActivityTaskManager.registerTaskStackListener(mTaskStackListener); - } catch (RemoteException e) { - Slog.e(TAG, "Could not register task stack listener", e); - } + mActivityTaskManager.registerTaskStackListener(mTaskStackListener); } if (DEBUG) Slog.w(TAG, "Requesting auth for " + getOwnerString()); @@ -309,11 +296,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> super.cancel(); if (mTaskStackListener != null) { - try { - mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); - } catch (RemoteException e) { - Slog.e(TAG, "Could not unregister task stack listener", e); - } + mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java b/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java index a8250ac9e72d..d588b8d4aa13 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java @@ -22,6 +22,7 @@ import android.hardware.biometrics.BiometricAuthenticator; import android.os.AsyncTask; import android.os.Environment; import android.util.Slog; +import android.util.TypedXmlPullParser; import android.util.Xml; import com.android.internal.annotations.GuardedBy; @@ -80,7 +81,7 @@ public abstract class BiometricUserState<T extends BiometricAuthenticator.Identi /** * @return */ - protected abstract void parseBiometricsLocked(XmlPullParser parser) + protected abstract void parseBiometricsLocked(TypedXmlPullParser parser) throws IOException, XmlPullParserException; @@ -176,8 +177,7 @@ public abstract class BiometricUserState<T extends BiometricAuthenticator.Identi return; } try { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(in, null); + TypedXmlPullParser parser = Xml.resolvePullParser(in); parseStateLocked(parser); } catch (XmlPullParserException | IOException e) { @@ -189,7 +189,7 @@ public abstract class BiometricUserState<T extends BiometricAuthenticator.Identi } @GuardedBy("this") - private void parseStateLocked(XmlPullParser parser) + private void parseStateLocked(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); int type; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java index d30c3c804362..78e875b864f4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java @@ -20,6 +20,8 @@ import android.content.Context; import android.hardware.face.Face; import android.util.AtomicFile; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.GuardedBy; @@ -29,7 +31,6 @@ import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.FileOutputStream; import java.io.IOException; @@ -87,8 +88,7 @@ public class FaceUserState extends BiometricUserState<Face> { try { out = destination.startWrite(); - XmlSerializer serializer = Xml.newSerializer(); - serializer.setOutput(out, "utf-8"); + TypedXmlSerializer serializer = Xml.resolveSerializer(out); serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); serializer.startDocument(null, true); serializer.startTag(null, TAG_FACES); @@ -97,9 +97,9 @@ public class FaceUserState extends BiometricUserState<Face> { for (int i = 0; i < count; i++) { Face f = faces.get(i); serializer.startTag(null, TAG_FACE); - serializer.attribute(null, ATTR_FACE_ID, Integer.toString(f.getBiometricId())); + serializer.attributeInt(null, ATTR_FACE_ID, f.getBiometricId()); serializer.attribute(null, ATTR_NAME, f.getName().toString()); - serializer.attribute(null, ATTR_DEVICE_ID, Long.toString(f.getDeviceId())); + serializer.attributeLong(null, ATTR_DEVICE_ID, f.getDeviceId()); serializer.endTag(null, TAG_FACE); } @@ -119,7 +119,7 @@ public class FaceUserState extends BiometricUserState<Face> { @GuardedBy("this") @Override - protected void parseBiometricsLocked(XmlPullParser parser) + protected void parseBiometricsLocked(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); int type; @@ -132,9 +132,9 @@ public class FaceUserState extends BiometricUserState<Face> { String tagName = parser.getName(); if (tagName.equals(TAG_FACE)) { String name = parser.getAttributeValue(null, ATTR_NAME); - String faceId = parser.getAttributeValue(null, ATTR_FACE_ID); - String deviceId = parser.getAttributeValue(null, ATTR_DEVICE_ID); - mBiometrics.add(new Face(name, Integer.parseInt(faceId), Integer.parseInt(deviceId))); + int faceId = parser.getAttributeInt(null, ATTR_FACE_ID); + int deviceId = parser.getAttributeInt(null, ATTR_DEVICE_ID); + mBiometrics.add(new Face(name, faceId, deviceId)); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index cc9298603a3e..cec1cb8654fc 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -77,7 +77,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @NonNull private final Handler mHandler; @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher; @NonNull private final UsageStats mUsageStats; - @NonNull private final IActivityTaskManager mActivityTaskManager; + @NonNull private final ActivityTaskManager mActivityTaskManager; @NonNull private final BiometricTaskStackListener mTaskStackListener; @Nullable private IFace mDaemon; @@ -97,22 +97,18 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { continue; // Keyguard is always allowed } - try { - final List<ActivityManager.RunningTaskInfo> runningTasks = - mActivityTaskManager.getTasks(1); - if (!runningTasks.isEmpty()) { - final String topPackage = - runningTasks.get(0).topActivity.getPackageName(); - if (!topPackage.contentEquals(client.getOwnerString()) - && !client.isAlreadyDone()) { - Slog.e(getTag(), "Stopping background authentication, top: " - + topPackage + " currentClient: " + client); - mSensors.valueAt(i).getScheduler() - .cancelAuthentication(client.getToken()); - } + final List<ActivityManager.RunningTaskInfo> runningTasks = + mActivityTaskManager.getTasks(1); + if (!runningTasks.isEmpty()) { + final String topPackage = + runningTasks.get(0).topActivity.getPackageName(); + if (!topPackage.contentEquals(client.getOwnerString()) + && !client.isAlreadyDone()) { + Slog.e(getTag(), "Stopping background authentication, top: " + + topPackage + " currentClient: " + client); + mSensors.valueAt(i).getScheduler() + .cancelAuthentication(client.getToken()); } - } catch (RemoteException e) { - Slog.e(getTag(), "Unable to get running tasks", e); } } }); @@ -129,7 +125,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mHandler = new Handler(Looper.getMainLooper()); mUsageStats = new UsageStats(context); mLockoutResetDispatcher = lockoutResetDispatcher; - mActivityTaskManager = ActivityTaskManager.getService(); + mActivityTaskManager = ActivityTaskManager.getInstance(); mTaskStackListener = new BiometricTaskStackListener(); for (SensorProps prop : props) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java index e56c8d5b5ceb..671e08bd8c56 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java @@ -20,6 +20,8 @@ import android.content.Context; import android.hardware.fingerprint.Fingerprint; import android.util.AtomicFile; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.GuardedBy; @@ -29,7 +31,6 @@ import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.FileOutputStream; import java.io.IOException; @@ -88,8 +89,7 @@ public class FingerprintUserState extends BiometricUserState<Fingerprint> { try { out = destination.startWrite(); - XmlSerializer serializer = Xml.newSerializer(); - serializer.setOutput(out, "utf-8"); + TypedXmlSerializer serializer = Xml.resolveSerializer(out); serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); serializer.startDocument(null, true); serializer.startTag(null, TAG_FINGERPRINTS); @@ -98,10 +98,10 @@ public class FingerprintUserState extends BiometricUserState<Fingerprint> { for (int i = 0; i < count; i++) { Fingerprint fp = fingerprints.get(i); serializer.startTag(null, TAG_FINGERPRINT); - serializer.attribute(null, ATTR_FINGER_ID, Integer.toString(fp.getBiometricId())); + serializer.attributeInt(null, ATTR_FINGER_ID, fp.getBiometricId()); serializer.attribute(null, ATTR_NAME, fp.getName().toString()); - serializer.attribute(null, ATTR_GROUP_ID, Integer.toString(fp.getGroupId())); - serializer.attribute(null, ATTR_DEVICE_ID, Long.toString(fp.getDeviceId())); + serializer.attributeInt(null, ATTR_GROUP_ID, fp.getGroupId()); + serializer.attributeLong(null, ATTR_DEVICE_ID, fp.getDeviceId()); serializer.endTag(null, TAG_FINGERPRINT); } @@ -121,7 +121,7 @@ public class FingerprintUserState extends BiometricUserState<Fingerprint> { @GuardedBy("this") @Override - protected void parseBiometricsLocked(XmlPullParser parser) + protected void parseBiometricsLocked(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); @@ -135,11 +135,10 @@ public class FingerprintUserState extends BiometricUserState<Fingerprint> { String tagName = parser.getName(); if (tagName.equals(TAG_FINGERPRINT)) { String name = parser.getAttributeValue(null, ATTR_NAME); - String groupId = parser.getAttributeValue(null, ATTR_GROUP_ID); - String fingerId = parser.getAttributeValue(null, ATTR_FINGER_ID); - String deviceId = parser.getAttributeValue(null, ATTR_DEVICE_ID); - mBiometrics.add(new Fingerprint(name, Integer.parseInt(groupId), - Integer.parseInt(fingerId), Long.parseLong(deviceId))); + int groupId = parser.getAttributeInt(null, ATTR_GROUP_ID); + int fingerId = parser.getAttributeInt(null, ATTR_FINGER_ID); + long deviceId = parser.getAttributeLong(null, ATTR_DEVICE_ID); + mBiometrics.add(new Fingerprint(name, groupId, fingerId, deviceId)); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index 98c32cbcfc4a..99c662a57c3b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -77,7 +77,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @NonNull private final ClientMonitor.LazyDaemon<IFingerprint> mLazyDaemon; @NonNull private final Handler mHandler; @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher; - @NonNull private final IActivityTaskManager mActivityTaskManager; + @NonNull private final ActivityTaskManager mActivityTaskManager; @NonNull private final BiometricTaskStackListener mTaskStackListener; @Nullable private IFingerprint mDaemon; @@ -98,22 +98,18 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi continue; // Keyguard is always allowed } - try { - final List<ActivityManager.RunningTaskInfo> runningTasks = - mActivityTaskManager.getTasks(1); - if (!runningTasks.isEmpty()) { - final String topPackage = - runningTasks.get(0).topActivity.getPackageName(); - if (!topPackage.contentEquals(client.getOwnerString()) - && !client.isAlreadyDone()) { - Slog.e(getTag(), "Stopping background authentication, top: " - + topPackage + " currentClient: " + client); - mSensors.valueAt(i).getScheduler() - .cancelAuthentication(client.getToken()); - } + final List<ActivityManager.RunningTaskInfo> runningTasks = + mActivityTaskManager.getTasks(1); + if (!runningTasks.isEmpty()) { + final String topPackage = + runningTasks.get(0).topActivity.getPackageName(); + if (!topPackage.contentEquals(client.getOwnerString()) + && !client.isAlreadyDone()) { + Slog.e(getTag(), "Stopping background authentication, top: " + + topPackage + " currentClient: " + client); + mSensors.valueAt(i).getScheduler() + .cancelAuthentication(client.getToken()); } - } catch (RemoteException e) { - Slog.e(getTag(), "Unable to get running tasks", e); } } }); @@ -129,7 +125,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mLazyDaemon = this::getHalInstance; mHandler = new Handler(Looper.getMainLooper()); mLockoutResetDispatcher = lockoutResetDispatcher; - mActivityTaskManager = ActivityTaskManager.getService(); + mActivityTaskManager = ActivityTaskManager.getInstance(); mTaskStackListener = new BiometricTaskStackListener(); for (SensorProps prop : props) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index f38dd092007a..7c5b7c92c1c6 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -97,7 +97,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider private boolean mTestHalEnabled; final Context mContext; - private final IActivityTaskManager mActivityTaskManager; + private final ActivityTaskManager mActivityTaskManager; @NonNull private final FingerprintSensorPropertiesInternal mSensorProperties; private final BiometricScheduler mScheduler; private final Handler mHandler; @@ -125,20 +125,16 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider return; // Keyguard is always allowed } - try { - final List<ActivityManager.RunningTaskInfo> runningTasks = - mActivityTaskManager.getTasks(1); - if (!runningTasks.isEmpty()) { - final String topPackage = runningTasks.get(0).topActivity.getPackageName(); - if (!topPackage.contentEquals(client.getOwnerString()) - && !client.isAlreadyDone()) { - Slog.e(TAG, "Stopping background authentication, top: " - + topPackage + " currentClient: " + client); - mScheduler.cancelAuthentication(client.getToken()); - } + final List<ActivityManager.RunningTaskInfo> runningTasks = + mActivityTaskManager.getTasks(1); + if (!runningTasks.isEmpty()) { + final String topPackage = runningTasks.get(0).topActivity.getPackageName(); + if (!topPackage.contentEquals(client.getOwnerString()) + && !client.isAlreadyDone()) { + Slog.e(TAG, "Stopping background authentication, top: " + + topPackage + " currentClient: " + client); + mScheduler.cancelAuthentication(client.getToken()); } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to get running tasks", e); } }); } @@ -313,7 +309,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mContext = context; mScheduler = scheduler; mHandler = handler; - mActivityTaskManager = ActivityTaskManager.getService(); + mActivityTaskManager = ActivityTaskManager.getInstance(); mTaskStackListener = new BiometricTaskStackListener(); mAuthenticatorIds = Collections.synchronizedMap(new HashMap<>()); diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index eec286a99a8f..234dcc9d74a5 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -122,7 +122,6 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.math.BigInteger; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; @@ -152,36 +151,13 @@ import java.util.concurrent.atomic.AtomicInteger; public class Vpn { private static final String NETWORKTYPE = "VPN"; private static final String TAG = "Vpn"; + private static final String VPN_PROVIDER_NAME_BASE = "VpnNetworkProvider:"; private static final boolean LOGD = true; // Length of time (in milliseconds) that an app hosting an always-on VPN is placed on // the device idle allowlist during service launch and VPN bootstrap. private static final long VPN_LAUNCH_IDLE_ALLOWLIST_DURATION_MS = 60 * 1000; - // Settings for how much of the address space should be routed so that Vpn considers - // "most" of the address space is routed. This is used to determine whether this Vpn - // should be marked with the INTERNET capability. - private static final long MOST_IPV4_ADDRESSES_COUNT; - private static final BigInteger MOST_IPV6_ADDRESSES_COUNT; - static { - // 85% of the address space must be routed for Vpn to consider this VPN to provide - // INTERNET access. - final int howManyPercentIsMost = 85; - - final long twoPower32 = 1L << 32; - MOST_IPV4_ADDRESSES_COUNT = twoPower32 * howManyPercentIsMost / 100; - final BigInteger twoPower128 = BigInteger.ONE.shiftLeft(128); - MOST_IPV6_ADDRESSES_COUNT = twoPower128 - .multiply(BigInteger.valueOf(howManyPercentIsMost)) - .divide(BigInteger.valueOf(100)); - } - // How many routes to evaluate before bailing and declaring this Vpn should provide - // the INTERNET capability. This is necessary because computing the address space is - // O(n²) and this is running in the system service, so a limit is needed to alleviate - // the risk of attack. - // This is taken as a total of IPv4 + IPV6 routes for simplicity, but the algorithm - // is actually O(n²)+O(n²). - private static final int MAX_ROUTES_TO_EVALUATE = 150; private static final String LOCKDOWN_ALLOWLIST_SETTING_NAME = Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST; /** @@ -198,6 +174,7 @@ public class Vpn { // automated reconnection private final Context mContext; + private final ConnectivityManager mConnectivityManager; // The context is for specific user which is created from mUserId private final Context mUserIdContext; @VisibleForTesting final Dependencies mDeps; @@ -218,6 +195,7 @@ public class Vpn { private final INetworkManagementService mNetd; @VisibleForTesting protected VpnConfig mConfig; + private final NetworkProvider mNetworkProvider; @VisibleForTesting protected NetworkAgent mNetworkAgent; private final Looper mLooper; @@ -401,6 +379,7 @@ public class Vpn { int userId, @NonNull KeyStore keyStore, SystemServices systemServices, Ikev2SessionCreator ikev2SessionCreator) { mContext = context; + mConnectivityManager = mContext.getSystemService(ConnectivityManager.class); mUserIdContext = context.createContextAsUser(UserHandle.of(userId), 0 /* flags */); mDeps = deps; mNetd = netService; @@ -419,13 +398,16 @@ public class Vpn { Log.wtf(TAG, "Problem registering observer", e); } + mNetworkProvider = new NetworkProvider(context, looper, VPN_PROVIDER_NAME_BASE + mUserId); + // This constructor is called in onUserStart and registers the provider. The provider + // will be unregistered in onUserStop. + mConnectivityManager.registerNetworkProvider(mNetworkProvider); mLegacyState = LegacyVpnInfo.STATE_DISCONNECTED; mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0 /* subtype */, NETWORKTYPE, "" /* subtypeName */); mNetworkCapabilities = new NetworkCapabilities(); mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN); mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN); - updateCapabilities(null /* defaultNetwork */); loadAlwaysOnPackage(keyStore); } @@ -443,12 +425,39 @@ public class Vpn { * Update current state, dispatching event to listeners. */ @VisibleForTesting + @GuardedBy("this") protected void updateState(DetailedState detailedState, String reason) { if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason); mLegacyState = LegacyVpnInfo.stateFromNetworkInfo(detailedState); mNetworkInfo.setDetailedState(detailedState, reason, null); - if (mNetworkAgent != null) { - mNetworkAgent.sendNetworkInfo(mNetworkInfo); + // TODO : only accept transitions when the agent is in the correct state (non-null for + // CONNECTED, DISCONNECTED and FAILED, null for CONNECTED). + // This will require a way for tests to pretend the VPN is connected that's not + // calling this method with CONNECTED. + // It will also require audit of where the code calls this method with DISCONNECTED + // with a null agent, which it was doing historically to make sure the agent is + // disconnected as this was a no-op if the agent was null. + switch (detailedState) { + case CONNECTED: + if (null != mNetworkAgent) { + mNetworkAgent.markConnected(); + } + break; + case DISCONNECTED: + case FAILED: + if (null != mNetworkAgent) { + mNetworkAgent.unregister(); + mNetworkAgent = null; + } + break; + case CONNECTING: + if (null != mNetworkAgent) { + throw new IllegalStateException("VPN can only go to CONNECTING state when" + + " the agent is null."); + } + break; + default: + throw new IllegalArgumentException("Illegal state argument " + detailedState); } updateAlwaysOnNotification(detailedState); } @@ -476,7 +485,7 @@ public class Vpn { final boolean isAlwaysMetered = mIsPackageTargetingAtLeastQ && mConfig.isMetered; applyUnderlyingCapabilities( - mContext.getSystemService(ConnectivityManager.class), + mConnectivityManager, underlyingNetworks, mNetworkCapabilities, isAlwaysMetered); @@ -486,10 +495,10 @@ public class Vpn { @VisibleForTesting public static void applyUnderlyingCapabilities( - ConnectivityManager cm, - Network[] underlyingNetworks, - NetworkCapabilities caps, - boolean isAlwaysMetered) { + @NonNull final ConnectivityManager cm, + @Nullable final Network[] underlyingNetworks, + @NonNull final NetworkCapabilities caps, + final boolean isAlwaysMetered) { int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN }; int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; @@ -1016,7 +1025,7 @@ public class Vpn { } mConfig = null; - updateState(DetailedState.IDLE, "prepare"); + updateState(DetailedState.DISCONNECTED, "prepare"); setVpnForcedLocked(mLockdown); } finally { Binder.restoreCallingIdentity(token); @@ -1252,7 +1261,7 @@ public class Vpn { mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); mLegacyState = LegacyVpnInfo.STATE_CONNECTING; - mNetworkInfo.setDetailedState(DetailedState.CONNECTING, null, null); + updateState(DetailedState.CONNECTING, "agentConnect"); NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig(); networkAgentConfig.allowBypass = mConfig.allowBypass && !mLockdown; @@ -1270,20 +1279,23 @@ public class Vpn { mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED); } - final long token = Binder.clearCallingIdentity(); - try { - mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE /* logtag */, - mNetworkInfo, mNetworkCapabilities, lp, - ConnectivityConstants.VPN_DEFAULT_SCORE, networkAgentConfig, - NetworkProvider.ID_VPN) { - @Override - public void unwanted() { - // We are user controlled, not driven by NetworkRequest. - } - }; - } finally { - Binder.restoreCallingIdentity(token); - } + mNetworkAgent = new NetworkAgent(mContext, mLooper, NETWORKTYPE /* logtag */, + mNetworkCapabilities, lp, + ConnectivityConstants.VPN_DEFAULT_SCORE, networkAgentConfig, mNetworkProvider) { + @Override + public void unwanted() { + // We are user controlled, not driven by NetworkRequest. + } + }; + Binder.withCleanCallingIdentity(() -> { + try { + mNetworkAgent.register(); + } catch (final Exception e) { + // If register() throws, don't keep an unregistered agent. + mNetworkAgent = null; + throw e; + } + }); mNetworkAgent.setUnderlyingNetworks((mConfig.underlyingNetworks != null) ? Arrays.asList(mConfig.underlyingNetworks) : null); mNetworkInfo.setIsAvailable(true); @@ -1301,19 +1313,12 @@ public class Vpn { private void agentDisconnect(NetworkAgent networkAgent) { if (networkAgent != null) { - NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo); - networkInfo.setIsAvailable(false); - networkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null); - networkAgent.sendNetworkInfo(networkInfo); + networkAgent.unregister(); } } private void agentDisconnect() { - if (mNetworkInfo.isConnected()) { - mNetworkInfo.setIsAvailable(false); - updateState(DetailedState.DISCONNECTED, "agentDisconnect"); - mNetworkAgent = null; - } + updateState(DetailedState.DISCONNECTED, "agentDisconnect"); } /** @@ -1402,6 +1407,8 @@ public class Vpn { && updateLinkPropertiesInPlaceIfPossible(mNetworkAgent, oldConfig)) { // Keep mNetworkAgent unchanged } else { + // Initialize the state for a new agent, while keeping the old one connected + // in case this new connection fails. mNetworkAgent = null; updateState(DetailedState.CONNECTING, "establish"); // Set up forwarding and DNS rules. @@ -1585,12 +1592,13 @@ public class Vpn { try { addUserToRanges(existingRanges, userId, mConfig.allowedApplications, mConfig.disallowedApplications); - // ConnectivityService will call {@link #updateCapabilities} and apply - // those for VPN network. mNetworkCapabilities.setUids(existingRanges); } catch (Exception e) { Log.wtf(TAG, "Failed to add restricted user to owner", e); } + if (mNetworkAgent != null) { + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } } setVpnForcedLocked(mLockdown); } @@ -1613,12 +1621,13 @@ public class Vpn { final List<UidRange> removedRanges = uidRangesForUser(userId, existingRanges); existingRanges.removeAll(removedRanges); - // ConnectivityService will call {@link #updateCapabilities} and - // apply those for VPN network. mNetworkCapabilities.setUids(existingRanges); } catch (Exception e) { Log.wtf(TAG, "Failed to remove restricted user to owner", e); } + if (mNetworkAgent != null) { + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } } setVpnForcedLocked(mLockdown); } @@ -1635,6 +1644,9 @@ public class Vpn { // Quit any active connections agentDisconnect(); + + // The provider has been registered in the constructor, which is called in onUserStart. + mConnectivityManager.unregisterNetworkProvider(mNetworkProvider); } /** @@ -2411,7 +2423,6 @@ public class Vpn { // When restricted to test networks, select any network with TRANSPORT_TEST. Since the // creator of the profile and the test network creator both have MANAGE_TEST_NETWORKS, // this is considered safe. - final ConnectivityManager cm = ConnectivityManager.from(mContext); final NetworkRequest req; if (mProfile.isRestrictedToTestNetworks()) { @@ -2430,7 +2441,7 @@ public class Vpn { .build(); } - cm.requestNetwork(req, mNetworkCallback); + mConnectivityManager.requestNetwork(req, mNetworkCallback); } private boolean isActiveNetwork(@Nullable Network network) { @@ -2717,8 +2728,7 @@ public class Vpn { resetIkeState(); - final ConnectivityManager cm = ConnectivityManager.from(mContext); - cm.unregisterNetworkCallback(mNetworkCallback); + mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); mExecutor.shutdown(); } @@ -2799,13 +2809,12 @@ public class Vpn { mProfile = profile; if (!TextUtils.isEmpty(mOuterInterface)) { - final ConnectivityManager cm = ConnectivityManager.from(mContext); - for (Network network : cm.getAllNetworks()) { - final LinkProperties lp = cm.getLinkProperties(network); + for (Network network : mConnectivityManager.getAllNetworks()) { + final LinkProperties lp = mConnectivityManager.getLinkProperties(network); if (lp != null && lp.getAllInterfaceNames().contains(mOuterInterface)) { - final NetworkInfo networkInfo = cm.getNetworkInfo(network); - if (networkInfo != null) { - mOuterConnection.set(networkInfo.getType()); + final NetworkInfo netInfo = mConnectivityManager.getNetworkInfo(network); + if (netInfo != null) { + mOuterConnection.set(netInfo.getType()); break; } } diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java index 9a1f1e522f97..d27cb16ecc51 100644 --- a/services/core/java/com/android/server/content/SyncStorageEngine.java +++ b/services/core/java/com/android/server/content/SyncStorageEngine.java @@ -58,12 +58,10 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IntPair; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileInputStream; @@ -71,7 +69,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; @@ -1650,37 +1647,23 @@ public class SyncStorageEngine { String tagName = parser.getName(); if ("accounts".equals(tagName)) { - String listen = parser.getAttributeValue(null, XML_ATTR_LISTEN_FOR_TICKLES); - String versionString = parser.getAttributeValue(null, "version"); - int version; - try { - version = (versionString == null) ? 0 : Integer.parseInt(versionString); - } catch (NumberFormatException e) { - version = 0; - } + boolean listen = parser.getAttributeBoolean( + null, XML_ATTR_LISTEN_FOR_TICKLES, true); + int version = parser.getAttributeInt(null, "version", 0); if (version < 3) { mGrantSyncAdaptersAccountAccess = true; } - String nextIdString = parser.getAttributeValue(null, XML_ATTR_NEXT_AUTHORITY_ID); - try { - int id = (nextIdString == null) ? 0 : Integer.parseInt(nextIdString); - mNextAuthorityId = Math.max(mNextAuthorityId, id); - } catch (NumberFormatException e) { - // don't care - } - String offsetString = parser.getAttributeValue(null, XML_ATTR_SYNC_RANDOM_OFFSET); - try { - mSyncRandomOffset = (offsetString == null) ? 0 : Integer.parseInt(offsetString); - } catch (NumberFormatException e) { - mSyncRandomOffset = 0; - } + int nextId = parser.getAttributeInt(null, XML_ATTR_NEXT_AUTHORITY_ID, 0); + mNextAuthorityId = Math.max(mNextAuthorityId, nextId); + + mSyncRandomOffset = parser.getAttributeInt(null, XML_ATTR_SYNC_RANDOM_OFFSET, 0); if (mSyncRandomOffset == 0) { Random random = new Random(System.currentTimeMillis()); mSyncRandomOffset = random.nextInt(86400); } - mMasterSyncAutomatically.put(0, listen == null || Boolean.parseBoolean(listen)); + mMasterSyncAutomatically.put(0, listen); eventType = parser.next(); AuthorityInfo authority = null; PeriodicSync periodicSync = null; @@ -1804,27 +1787,23 @@ public class SyncStorageEngine { return writeNeeded; } - private void parseListenForTickles(XmlPullParser parser) { - String user = parser.getAttributeValue(null, XML_ATTR_USER); + private void parseListenForTickles(TypedXmlPullParser parser) { int userId = 0; try { - userId = Integer.parseInt(user); - } catch (NumberFormatException e) { + parser.getAttributeInt(null, XML_ATTR_USER); + } catch (XmlPullParserException e) { Slog.e(TAG, "error parsing the user for listen-for-tickles", e); - } catch (NullPointerException e) { - Slog.e(TAG, "the user in listen-for-tickles is null", e); } - String enabled = parser.getAttributeValue(null, XML_ATTR_ENABLED); - boolean listen = enabled == null || Boolean.parseBoolean(enabled); + boolean listen = parser.getAttributeBoolean(null, XML_ATTR_ENABLED, true); mMasterSyncAutomatically.put(userId, listen); } - private AuthorityInfo parseAuthority(XmlPullParser parser, int version, - AccountAuthorityValidator validator) { + private AuthorityInfo parseAuthority(TypedXmlPullParser parser, int version, + AccountAuthorityValidator validator) throws XmlPullParserException { AuthorityInfo authority = null; int id = -1; try { - id = Integer.parseInt(parser.getAttributeValue(null, "id")); + id = parser.getAttributeInt(null, "id"); } catch (NumberFormatException e) { Slog.e(TAG, "error parsing the id of the authority", e); } catch (NullPointerException e) { @@ -1832,14 +1811,13 @@ public class SyncStorageEngine { } if (id >= 0) { String authorityName = parser.getAttributeValue(null, "authority"); - String enabled = parser.getAttributeValue(null, XML_ATTR_ENABLED); + boolean enabled = parser.getAttributeBoolean(null, XML_ATTR_ENABLED, true); String syncable = parser.getAttributeValue(null, "syncable"); String accountName = parser.getAttributeValue(null, "account"); String accountType = parser.getAttributeValue(null, "type"); - String user = parser.getAttributeValue(null, XML_ATTR_USER); + int userId = parser.getAttributeInt(null, XML_ATTR_USER, 0); String packageName = parser.getAttributeValue(null, "package"); String className = parser.getAttributeValue(null, "class"); - int userId = user == null ? 0 : Integer.parseInt(user); if (accountType == null && packageName == null) { accountType = "com.google"; syncable = String.valueOf(AuthorityInfo.NOT_INITIALIZED); @@ -1884,7 +1862,7 @@ public class SyncStorageEngine { } } if (authority != null) { - authority.enabled = enabled == null || Boolean.parseBoolean(enabled); + authority.enabled = enabled; try { authority.syncable = (syncable == null) ? AuthorityInfo.NOT_INITIALIZED : Integer.parseInt(syncable); @@ -1912,32 +1890,22 @@ public class SyncStorageEngine { /** * Parse a periodic sync from accounts.xml. Sets the bundle to be empty. */ - private PeriodicSync parsePeriodicSync(XmlPullParser parser, AuthorityInfo authorityInfo) { + private PeriodicSync parsePeriodicSync(TypedXmlPullParser parser, AuthorityInfo authorityInfo) { Bundle extras = new Bundle(); // Gets filled in later. - String periodValue = parser.getAttributeValue(null, "period"); - String flexValue = parser.getAttributeValue(null, "flex"); - final long period; + long period; long flextime; try { - period = Long.parseLong(periodValue); - } catch (NumberFormatException e) { + period = parser.getAttributeLong(null, "period"); + } catch (XmlPullParserException e) { Slog.e(TAG, "error parsing the period of a periodic sync", e); return null; - } catch (NullPointerException e) { - Slog.e(TAG, "the period of a periodic sync is null", e); - return null; } try { - flextime = Long.parseLong(flexValue); - } catch (NumberFormatException e) { - flextime = calculateDefaultFlexTime(period); - Slog.e(TAG, "Error formatting value parsed for periodic sync flex: " + flexValue - + ", using default: " - + flextime); - } catch (NullPointerException expected) { + flextime = parser.getAttributeLong(null, "flex"); + } catch (XmlPullParserException e) { flextime = calculateDefaultFlexTime(period); - Slog.d(TAG, "No flex time specified for this sync, using a default. period: " - + period + " flex: " + flextime); + Slog.e(TAG, "Error formatting value parsed for periodic sync flex, using default: " + + flextime, e); } PeriodicSync periodicSync; periodicSync = @@ -1949,7 +1917,7 @@ public class SyncStorageEngine { return periodicSync; } - private void parseExtra(XmlPullParser parser, Bundle extras) { + private void parseExtra(TypedXmlPullParser parser, Bundle extras) { String name = parser.getAttributeValue(null, "name"); String type = parser.getAttributeValue(null, "type"); String value1 = parser.getAttributeValue(null, "value1"); @@ -1994,9 +1962,9 @@ public class SyncStorageEngine { out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); out.startTag(null, "accounts"); - out.attribute(null, "version", Integer.toString(ACCOUNTS_VERSION)); - out.attribute(null, XML_ATTR_NEXT_AUTHORITY_ID, Integer.toString(mNextAuthorityId)); - out.attribute(null, XML_ATTR_SYNC_RANDOM_OFFSET, Integer.toString(mSyncRandomOffset)); + out.attributeInt(null, "version", ACCOUNTS_VERSION); + out.attributeInt(null, XML_ATTR_NEXT_AUTHORITY_ID, mNextAuthorityId); + out.attributeInt(null, XML_ATTR_SYNC_RANDOM_OFFSET, mSyncRandomOffset); // Write the Sync Automatically flags for each user final int M = mMasterSyncAutomatically.size(); @@ -2004,8 +1972,8 @@ public class SyncStorageEngine { int userId = mMasterSyncAutomatically.keyAt(m); Boolean listen = mMasterSyncAutomatically.valueAt(m); out.startTag(null, XML_TAG_LISTEN_FOR_TICKLES); - out.attribute(null, XML_ATTR_USER, Integer.toString(userId)); - out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(listen)); + out.attributeInt(null, XML_ATTR_USER, userId); + out.attributeBoolean(null, XML_ATTR_ENABLED, listen); out.endTag(null, XML_TAG_LISTEN_FOR_TICKLES); } @@ -2014,13 +1982,13 @@ public class SyncStorageEngine { AuthorityInfo authority = mAuthorities.valueAt(i); EndPoint info = authority.target; out.startTag(null, "authority"); - out.attribute(null, "id", Integer.toString(authority.ident)); - out.attribute(null, XML_ATTR_USER, Integer.toString(info.userId)); - out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(authority.enabled)); + out.attributeInt(null, "id", authority.ident); + out.attributeInt(null, XML_ATTR_USER, info.userId); + out.attributeBoolean(null, XML_ATTR_ENABLED, authority.enabled); out.attribute(null, "account", info.account.name); out.attribute(null, "type", info.account.type); out.attribute(null, "authority", info.provider); - out.attribute(null, "syncable", Integer.toString(authority.syncable)); + out.attributeInt(null, "syncable", authority.syncable); out.endTag(null, "authority"); } out.endTag(null, "accounts"); diff --git a/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java b/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java index 928799b31df8..0f1e6668ceec 100644 --- a/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java +++ b/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java @@ -27,17 +27,14 @@ import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.FastXmlSerializer; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; import java.time.LocalDate; import java.time.format.DateTimeParseException; import java.util.ArrayDeque; @@ -179,7 +176,7 @@ public class AmbientBrightnessStatsTracker { entry.getKey()); if (userSerialNumber != -1 && userDayStats.getLocalDate().isAfter(cutOffDate)) { out.startTag(null, TAG_AMBIENT_BRIGHTNESS_DAY_STATS); - out.attribute(null, ATTR_USER, Integer.toString(userSerialNumber)); + out.attributeInt(null, ATTR_USER, userSerialNumber); out.attribute(null, ATTR_LOCAL_DATE, userDayStats.getLocalDate().toString()); StringBuilder bucketBoundariesValues = new StringBuilder(); @@ -229,7 +226,7 @@ public class AmbientBrightnessStatsTracker { } tag = parser.getName(); if (TAG_AMBIENT_BRIGHTNESS_DAY_STATS.equals(tag)) { - String userSerialNumber = parser.getAttributeValue(null, ATTR_USER); + int userSerialNumber = parser.getAttributeInt(null, ATTR_USER); LocalDate localDate = LocalDate.parse( parser.getAttributeValue(null, ATTR_LOCAL_DATE)); String[] bucketBoundaries = parser.getAttributeValue(null, @@ -246,8 +243,7 @@ public class AmbientBrightnessStatsTracker { parsedBucketBoundaries[i] = Float.parseFloat(bucketBoundaries[i]); parsedBucketStats[i] = Float.parseFloat(bucketStats[i]); } - int userId = mInjector.getUserId(mUserManager, - Integer.parseInt(userSerialNumber)); + int userId = mInjector.getUserId(mUserManager, userSerialNumber); if (userId != -1 && localDate.isAfter(cutOffDate)) { Deque<AmbientBrightnessDayStats> userStats = getOrCreateUserStats( parsedStats, userId); diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java index 3ae99ef3ed5e..2a0e21919704 100644 --- a/services/core/java/com/android/server/display/BrightnessTracker.java +++ b/services/core/java/com/android/server/display/BrightnessTracker.java @@ -555,22 +555,22 @@ public class BrightnessTracker { if (userSerialNo != -1 && toWrite[i].timeStamp > timeCutOff) { mEvents.append(toWrite[i]); out.startTag(null, TAG_EVENT); - out.attribute(null, ATTR_NITS, Float.toString(toWrite[i].brightness)); - out.attribute(null, ATTR_TIMESTAMP, Long.toString(toWrite[i].timeStamp)); + out.attributeFloat(null, ATTR_NITS, toWrite[i].brightness); + out.attributeLong(null, ATTR_TIMESTAMP, toWrite[i].timeStamp); out.attribute(null, ATTR_PACKAGE_NAME, toWrite[i].packageName); - out.attribute(null, ATTR_USER, Integer.toString(userSerialNo)); - out.attribute(null, ATTR_BATTERY_LEVEL, Float.toString(toWrite[i].batteryLevel)); - out.attribute(null, ATTR_NIGHT_MODE, Boolean.toString(toWrite[i].nightMode)); - out.attribute(null, ATTR_COLOR_TEMPERATURE, Integer.toString( - toWrite[i].colorTemperature)); - out.attribute(null, ATTR_LAST_NITS, - Float.toString(toWrite[i].lastBrightness)); - out.attribute(null, ATTR_DEFAULT_CONFIG, - Boolean.toString(toWrite[i].isDefaultBrightnessConfig)); - out.attribute(null, ATTR_POWER_SAVE, - Float.toString(toWrite[i].powerBrightnessFactor)); - out.attribute(null, ATTR_USER_POINT, - Boolean.toString(toWrite[i].isUserSetBrightness)); + out.attributeInt(null, ATTR_USER, userSerialNo); + out.attributeFloat(null, ATTR_BATTERY_LEVEL, toWrite[i].batteryLevel); + out.attributeBoolean(null, ATTR_NIGHT_MODE, toWrite[i].nightMode); + out.attributeInt(null, ATTR_COLOR_TEMPERATURE, + toWrite[i].colorTemperature); + out.attributeFloat(null, ATTR_LAST_NITS, + toWrite[i].lastBrightness); + out.attributeBoolean(null, ATTR_DEFAULT_CONFIG, + toWrite[i].isDefaultBrightnessConfig); + out.attributeFloat(null, ATTR_POWER_SAVE, + toWrite[i].powerBrightnessFactor); + out.attributeBoolean(null, ATTR_USER_POINT, + toWrite[i].isUserSetBrightness); StringBuilder luxValues = new StringBuilder(); StringBuilder luxTimestamps = new StringBuilder(); for (int j = 0; j < toWrite[i].luxValues.length; ++j) { @@ -585,8 +585,8 @@ public class BrightnessTracker { out.attribute(null, ATTR_LUX_TIMESTAMPS, luxTimestamps.toString()); if (toWrite[i].colorValueBuckets != null && toWrite[i].colorValueBuckets.length > 0) { - out.attribute(null, ATTR_COLOR_SAMPLE_DURATION, - Long.toString(toWrite[i].colorSampleDuration)); + out.attributeLong(null, ATTR_COLOR_SAMPLE_DURATION, + toWrite[i].colorSampleDuration); StringBuilder buckets = new StringBuilder(); for (int j = 0; j < toWrite[i].colorValueBuckets.length; ++j) { if (j > 0) { @@ -633,22 +633,16 @@ public class BrightnessTracker { if (TAG_EVENT.equals(tag)) { BrightnessChangeEvent.Builder builder = new BrightnessChangeEvent.Builder(); - String brightness = parser.getAttributeValue(null, ATTR_NITS); - builder.setBrightness(Float.parseFloat(brightness)); - String timestamp = parser.getAttributeValue(null, ATTR_TIMESTAMP); - builder.setTimeStamp(Long.parseLong(timestamp)); + builder.setBrightness(parser.getAttributeFloat(null, ATTR_NITS)); + builder.setTimeStamp(parser.getAttributeLong(null, ATTR_TIMESTAMP)); builder.setPackageName(parser.getAttributeValue(null, ATTR_PACKAGE_NAME)); - String user = parser.getAttributeValue(null, ATTR_USER); - builder.setUserId(mInjector.getUserId(mUserManager, Integer.parseInt(user))); - String batteryLevel = parser.getAttributeValue(null, ATTR_BATTERY_LEVEL); - builder.setBatteryLevel(Float.parseFloat(batteryLevel)); - String nightMode = parser.getAttributeValue(null, ATTR_NIGHT_MODE); - builder.setNightMode(Boolean.parseBoolean(nightMode)); - String colorTemperature = - parser.getAttributeValue(null, ATTR_COLOR_TEMPERATURE); - builder.setColorTemperature(Integer.parseInt(colorTemperature)); - String lastBrightness = parser.getAttributeValue(null, ATTR_LAST_NITS); - builder.setLastBrightness(Float.parseFloat(lastBrightness)); + builder.setUserId(mInjector.getUserId(mUserManager, + parser.getAttributeInt(null, ATTR_USER))); + builder.setBatteryLevel(parser.getAttributeFloat(null, ATTR_BATTERY_LEVEL)); + builder.setNightMode(parser.getAttributeBoolean(null, ATTR_NIGHT_MODE)); + builder.setColorTemperature( + parser.getAttributeInt(null, ATTR_COLOR_TEMPERATURE)); + builder.setLastBrightness(parser.getAttributeFloat(null, ATTR_LAST_NITS)); String luxValue = parser.getAttributeValue(null, ATTR_LUX); String luxTimestamp = parser.getAttributeValue(null, ATTR_LUX_TIMESTAMPS); @@ -667,20 +661,12 @@ public class BrightnessTracker { builder.setLuxValues(luxValues); builder.setLuxTimestamps(luxTimestamps); - String defaultConfig = parser.getAttributeValue(null, ATTR_DEFAULT_CONFIG); - if (defaultConfig != null) { - builder.setIsDefaultBrightnessConfig(Boolean.parseBoolean(defaultConfig)); - } - String powerSave = parser.getAttributeValue(null, ATTR_POWER_SAVE); - if (powerSave != null) { - builder.setPowerBrightnessFactor(Float.parseFloat(powerSave)); - } else { - builder.setPowerBrightnessFactor(1.0f); - } - String userPoint = parser.getAttributeValue(null, ATTR_USER_POINT); - if (userPoint != null) { - builder.setUserBrightnessPoint(Boolean.parseBoolean(userPoint)); - } + builder.setIsDefaultBrightnessConfig( + parser.getAttributeBoolean(null, ATTR_DEFAULT_CONFIG, false)); + builder.setPowerBrightnessFactor( + parser.getAttributeFloat(null, ATTR_POWER_SAVE, 1.0f)); + builder.setUserBrightnessPoint( + parser.getAttributeBoolean(null, ATTR_USER_POINT, false)); String colorSampleDurationString = parser.getAttributeValue(null, ATTR_COLOR_SAMPLE_DURATION); diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 1aced07e0997..329081a8391f 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -38,6 +38,7 @@ import android.os.UserHandle; import android.provider.DeviceConfig; import android.provider.Settings; import android.text.TextUtils; +import android.util.IndentingPrintWriter; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; @@ -49,6 +50,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import com.android.server.display.utils.AmbientFilter; import com.android.server.display.utils.AmbientFilterFactory; +import com.android.server.utils.DeviceConfigInterface; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -68,9 +70,11 @@ public class DisplayModeDirector { private static final boolean DEBUG = false; private static final int MSG_REFRESH_RATE_RANGE_CHANGED = 1; - private static final int MSG_BRIGHTNESS_THRESHOLDS_CHANGED = 2; + private static final int MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED = 2; private static final int MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED = 3; - private static final int MSG_REFRESH_RATE_IN_ZONE_CHANGED = 4; + private static final int MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED = 4; + private static final int MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED = 5; + private static final int MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED = 6; // Special ID used to indicate that given vote is to be applied globally, rather than to a // specific display. @@ -83,6 +87,13 @@ public class DisplayModeDirector { private final Context mContext; private final DisplayModeDirectorHandler mHandler; + private final Injector mInjector; + + private final AppRequestObserver mAppRequestObserver; + private final SettingsObserver mSettingsObserver; + private final DisplayObserver mDisplayObserver; + private final DeviceConfigInterface mDeviceConfig; + private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings; // A map from the display ID to the collection of votes and their priority. The latter takes // the form of another map from the priority to the vote itself so that each priority is @@ -93,12 +104,8 @@ public class DisplayModeDirector { // A map from the display ID to the default mode of that display. private SparseArray<Display.Mode> mDefaultModeByDisplay; - private final AppRequestObserver mAppRequestObserver; - private final SettingsObserver mSettingsObserver; - private final DisplayObserver mDisplayObserver; private BrightnessObserver mBrightnessObserver; - private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings; private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener; private boolean mAlwaysRespectAppRequest; @@ -127,8 +134,14 @@ public class DisplayModeDirector { private int mModeSwitchingType = SWITCHING_TYPE_WITHIN_GROUPS; public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) { + this(context, handler, new RealInjector()); + } + + public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler, + @NonNull Injector injector) { mContext = context; mHandler = new DisplayModeDirectorHandler(handler.getLooper()); + mInjector = injector; mVotesByDisplay = new SparseArray<>(); mSupportedModesByDisplay = new SparseArray<>(); mDefaultModeByDisplay = new SparseArray<>(); @@ -137,6 +150,7 @@ public class DisplayModeDirector { mDisplayObserver = new DisplayObserver(context, handler); mBrightnessObserver = new BrightnessObserver(context, handler); mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings(); + mDeviceConfig = injector.getDeviceConfig(); mAlwaysRespectAppRequest = false; } @@ -455,6 +469,23 @@ public class DisplayModeDirector { } /** + * Retrieve the Vote for the given display and priority. Intended only for testing purposes. + * + * @param displayId the display to query for + * @param priority the priority of the vote to return + * @return the vote corresponding to the given {@code displayId} and {@code priority}, + * or {@code null} if there isn't one + */ + @VisibleForTesting + @Nullable + Vote getVote(int displayId, int priority) { + synchronized (mLock) { + SparseArray<Vote> votes = getVotesLocked(displayId); + return votes.get(priority); + } + } + + /** * Print the object's state and debug information into the given stream. * * @param pw The stream to dump information to. @@ -586,6 +617,17 @@ public class DisplayModeDirector { } @VisibleForTesting + BrightnessObserver getBrightnessObserver() { + return mBrightnessObserver; + } + + @VisibleForTesting + SettingsObserver getSettingsObserver() { + return mSettingsObserver; + } + + + @VisibleForTesting DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings( float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) { synchronized (mLock) { @@ -613,16 +655,35 @@ public class DisplayModeDirector { @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_BRIGHTNESS_THRESHOLDS_CHANGED: + case MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED: { + Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj; + mBrightnessObserver.onDeviceConfigLowBrightnessThresholdsChanged( + thresholds.first, thresholds.second); + break; + } + + case MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED: { + int refreshRateInZone = msg.arg1; + mBrightnessObserver.onDeviceConfigRefreshRateInLowZoneChanged( + refreshRateInZone); + break; + } + + case MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED: { Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj; - if (thresholds != null) { - mBrightnessObserver.onDeviceConfigThresholdsChanged( - thresholds.first, thresholds.second); - } else { - mBrightnessObserver.onDeviceConfigThresholdsChanged(null, null); - } + mBrightnessObserver.onDeviceConfigHighBrightnessThresholdsChanged( + thresholds.first, thresholds.second); + + break; + } + + case MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED: { + int refreshRateInZone = msg.arg1; + mBrightnessObserver.onDeviceConfigRefreshRateInHighZoneChanged( + refreshRateInZone); break; + } case MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED: Float defaultPeakRefreshRate = (Float) msg.obj; @@ -630,12 +691,6 @@ public class DisplayModeDirector { defaultPeakRefreshRate); break; - case MSG_REFRESH_RATE_IN_ZONE_CHANGED: - int refreshRateInZone = msg.arg1; - mBrightnessObserver.onDeviceConfigRefreshRateInZoneChanged( - refreshRateInZone); - break; - case MSG_REFRESH_RATE_RANGE_CHANGED: DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener = (DesiredDisplayModeSpecsListener) msg.obj; @@ -822,10 +877,11 @@ public class DisplayModeDirector { // by all other considerations. It acts to set a default frame rate for a device. public static final int PRIORITY_DEFAULT_REFRESH_RATE = 0; - // LOW_BRIGHTNESS votes for a single refresh rate like [60,60], [90,90] or null. + // FLICKER votes for a single refresh rate like [60,60], [90,90] or null. // If the higher voters result is a range, it will fix the rate to a single choice. - // It's used to avoid rate switch in certain conditions. - public static final int PRIORITY_LOW_BRIGHTNESS = 1; + // It's used to avoid refresh rate switches in certain conditions which may result in the + // user seeing the display flickering when the switches occur. + public static final int PRIORITY_FLICKER = 1; // SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate. // It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY] @@ -898,8 +954,8 @@ public class DisplayModeDirector { switch (priority) { case PRIORITY_DEFAULT_REFRESH_RATE: return "PRIORITY_DEFAULT_REFRESH_RATE"; - case PRIORITY_LOW_BRIGHTNESS: - return "PRIORITY_LOW_BRIGHTNESS"; + case PRIORITY_FLICKER: + return "PRIORITY_FLICKER"; case PRIORITY_USER_SETTING_MIN_REFRESH_RATE: return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE"; case PRIORITY_APP_REQUEST_REFRESH_RATE: @@ -924,7 +980,8 @@ public class DisplayModeDirector { } } - private final class SettingsObserver extends ContentObserver { + @VisibleForTesting + final class SettingsObserver extends ContentObserver { private final Uri mPeakRefreshRateSetting = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE); private final Uri mMinRefreshRateSetting = @@ -949,8 +1006,7 @@ public class DisplayModeDirector { public void observe() { final ContentResolver cr = mContext.getContentResolver(); - cr.registerContentObserver(mPeakRefreshRateSetting, false /*notifyDescendants*/, this, - UserHandle.USER_SYSTEM); + mInjector.registerPeakRefreshRateObserver(cr, this); cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM); cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this, @@ -971,6 +1027,13 @@ public class DisplayModeDirector { } } + public void setDefaultRefreshRate(float refreshRate) { + synchronized (mLock) { + mDefaultRefreshRate = refreshRate; + updateRefreshRateSettingLocked(); + } + } + public void onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate) { if (defaultPeakRefreshRate == null) { defaultPeakRefreshRate = (float) mContext.getResources().getInteger( @@ -1189,6 +1252,7 @@ public class DisplayModeDirector { @Override public void onDisplayChanged(int displayId) { updateDisplayModes(displayId); + // TODO: Break the coupling between DisplayObserver and BrightnessObserver. mBrightnessObserver.onDisplayChanged(displayId); } @@ -1227,15 +1291,16 @@ public class DisplayModeDirector { */ @VisibleForTesting public class BrightnessObserver extends ContentObserver { - // TODO: brightnessfloat: change this to the float setting - private final Uri mDisplayBrightnessSetting = - Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS); private final static int LIGHT_SENSOR_RATE_MS = 250; - private int[] mDisplayBrightnessThresholds; - private int[] mAmbientBrightnessThresholds; + private int[] mLowDisplayBrightnessThresholds; + private int[] mLowAmbientBrightnessThresholds; + private int[] mHighDisplayBrightnessThresholds; + private int[] mHighAmbientBrightnessThresholds; // valid threshold if any item from the array >= 0 - private boolean mShouldObserveDisplayChange; - private boolean mShouldObserveAmbientChange; + private boolean mShouldObserveDisplayLowChange; + private boolean mShouldObserveAmbientLowChange; + private boolean mShouldObserveDisplayHighChange; + private boolean mShouldObserveAmbientHighChange; private SensorManager mSensorManager; private Sensor mLightSensor; @@ -1243,46 +1308,122 @@ public class DisplayModeDirector { // Take it as low brightness before valid sensor data comes private float mAmbientLux = -1.0f; private AmbientFilter mAmbientFilter; + private int mBrightness = -1; private final Context mContext; - // Enable light sensor only when mShouldObserveAmbientChange is true, screen is on, peak - // refresh rate changeable and low power mode off. After initialization, these states will + // Enable light sensor only when mShouldObserveAmbientLowChange is true or + // mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate + // changeable and low power mode off. After initialization, these states will // be updated from the same handler thread. - private boolean mScreenOn = false; + private boolean mDefaultDisplayOn = false; private boolean mRefreshRateChangeable = false; private boolean mLowPowerModeEnabled = false; - private int mRefreshRateInZone; + private int mRefreshRateInLowZone; + private int mRefreshRateInHighZone; BrightnessObserver(Context context, Handler handler) { super(handler); mContext = context; - mDisplayBrightnessThresholds = context.getResources().getIntArray( + mLowDisplayBrightnessThresholds = context.getResources().getIntArray( R.array.config_brightnessThresholdsOfPeakRefreshRate); - mAmbientBrightnessThresholds = context.getResources().getIntArray( + mLowAmbientBrightnessThresholds = context.getResources().getIntArray( R.array.config_ambientThresholdsOfPeakRefreshRate); - if (mDisplayBrightnessThresholds.length != mAmbientBrightnessThresholds.length) { - throw new RuntimeException("display brightness threshold array and ambient " - + "brightness threshold array have different length"); + if (mLowDisplayBrightnessThresholds.length != mLowAmbientBrightnessThresholds.length) { + throw new RuntimeException("display low brightness threshold array and ambient " + + "brightness threshold array have different length: " + + "displayBrightnessThresholds=" + + Arrays.toString(mLowDisplayBrightnessThresholds) + + ", ambientBrightnessThresholds=" + + Arrays.toString(mLowAmbientBrightnessThresholds)); } + + mHighDisplayBrightnessThresholds = context.getResources().getIntArray( + R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate); + mHighAmbientBrightnessThresholds = context.getResources().getIntArray( + R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate); + if (mHighDisplayBrightnessThresholds.length + != mHighAmbientBrightnessThresholds.length) { + throw new RuntimeException("display high brightness threshold array and ambient " + + "brightness threshold array have different length: " + + "displayBrightnessThresholds=" + + Arrays.toString(mHighDisplayBrightnessThresholds) + + ", ambientBrightnessThresholds=" + + Arrays.toString(mHighAmbientBrightnessThresholds)); + } + mRefreshRateInHighZone = context.getResources().getInteger( + R.integer.config_fixedRefreshRateInHighZone); + } + + /** + * @return the refresh to lock to when in a low brightness zone + */ + @VisibleForTesting + int getRefreshRateInLowZone() { + return mRefreshRateInLowZone; + } + + /** + * @return the display brightness thresholds for the low brightness zones + */ + @VisibleForTesting + int[] getLowDisplayBrightnessThresholds() { + return mLowDisplayBrightnessThresholds; + } + + /** + * @return the ambient brightness thresholds for the low brightness zones + */ + @VisibleForTesting + int[] getLowAmbientBrightnessThresholds() { + return mLowAmbientBrightnessThresholds; + } + + public void registerLightSensor(SensorManager sensorManager, Sensor lightSensor) { + mSensorManager = sensorManager; + mLightSensor = lightSensor; + + mSensorManager.registerListener(mLightSensorListener, + mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler); } public void observe(SensorManager sensorManager) { mSensorManager = sensorManager; + final ContentResolver cr = mContext.getContentResolver(); + mBrightness = Settings.System.getIntForUser(cr, + Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId()); // DeviceConfig is accessible after system ready. - int[] brightnessThresholds = mDeviceConfigDisplaySettings.getBrightnessThresholds(); - int[] ambientThresholds = mDeviceConfigDisplaySettings.getAmbientThresholds(); + int[] lowDisplayBrightnessThresholds = + mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds(); + int[] lowAmbientBrightnessThresholds = + mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds(); + + if (lowDisplayBrightnessThresholds != null && lowAmbientBrightnessThresholds != null + && lowDisplayBrightnessThresholds.length + == lowAmbientBrightnessThresholds.length) { + mLowDisplayBrightnessThresholds = lowDisplayBrightnessThresholds; + mLowAmbientBrightnessThresholds = lowAmbientBrightnessThresholds; + } - if (brightnessThresholds != null && ambientThresholds != null - && brightnessThresholds.length == ambientThresholds.length) { - mDisplayBrightnessThresholds = brightnessThresholds; - mAmbientBrightnessThresholds = ambientThresholds; + + int[] highDisplayBrightnessThresholds = + mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds(); + int[] highAmbientBrightnessThresholds = + mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds(); + + if (highDisplayBrightnessThresholds != null && highAmbientBrightnessThresholds != null + && highDisplayBrightnessThresholds.length + == highAmbientBrightnessThresholds.length) { + mHighDisplayBrightnessThresholds = highDisplayBrightnessThresholds; + mHighAmbientBrightnessThresholds = highAmbientBrightnessThresholds; } - mRefreshRateInZone = mDeviceConfigDisplaySettings.getRefreshRateInZone(); + mRefreshRateInLowZone = mDeviceConfigDisplaySettings.getRefreshRateInLowZone(); + mRefreshRateInHighZone = mDeviceConfigDisplaySettings.getRefreshRateInHighZone(); + restartObserver(); mDeviceConfigDisplaySettings.startListening(); } @@ -1294,7 +1435,7 @@ public class DisplayModeDirector { updateSensorStatus(); if (!changeable) { // Revoke previous vote from BrightnessObserver - updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, null); + updateVoteLocked(Vote.PRIORITY_FLICKER, null); } } } @@ -1306,25 +1447,48 @@ public class DisplayModeDirector { } } - public void onDeviceConfigThresholdsChanged(int[] brightnessThresholds, + public void onDeviceConfigLowBrightnessThresholdsChanged(int[] displayThresholds, int[] ambientThresholds) { - if (brightnessThresholds != null && ambientThresholds != null - && brightnessThresholds.length == ambientThresholds.length) { - mDisplayBrightnessThresholds = brightnessThresholds; - mAmbientBrightnessThresholds = ambientThresholds; + if (displayThresholds != null && ambientThresholds != null + && displayThresholds.length == ambientThresholds.length) { + mLowDisplayBrightnessThresholds = displayThresholds; + mLowAmbientBrightnessThresholds = ambientThresholds; } else { // Invalid or empty. Use device default. - mDisplayBrightnessThresholds = mContext.getResources().getIntArray( + mLowDisplayBrightnessThresholds = mContext.getResources().getIntArray( R.array.config_brightnessThresholdsOfPeakRefreshRate); - mAmbientBrightnessThresholds = mContext.getResources().getIntArray( + mLowAmbientBrightnessThresholds = mContext.getResources().getIntArray( R.array.config_ambientThresholdsOfPeakRefreshRate); } restartObserver(); } - public void onDeviceConfigRefreshRateInZoneChanged(int refreshRate) { - if (refreshRate != mRefreshRateInZone) { - mRefreshRateInZone = refreshRate; + public void onDeviceConfigRefreshRateInLowZoneChanged(int refreshRate) { + if (refreshRate != mRefreshRateInLowZone) { + mRefreshRateInLowZone = refreshRate; + restartObserver(); + } + } + + public void onDeviceConfigHighBrightnessThresholdsChanged(int[] displayThresholds, + int[] ambientThresholds) { + if (displayThresholds != null && ambientThresholds != null + && displayThresholds.length == ambientThresholds.length) { + mHighDisplayBrightnessThresholds = displayThresholds; + mHighAmbientBrightnessThresholds = ambientThresholds; + } else { + // Invalid or empty. Use device default. + mHighDisplayBrightnessThresholds = mContext.getResources().getIntArray( + R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate); + mHighAmbientBrightnessThresholds = mContext.getResources().getIntArray( + R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate); + } + restartObserver(); + } + + public void onDeviceConfigRefreshRateInHighZoneChanged(int refreshRate) { + if (refreshRate != mRefreshRateInHighZone) { + mRefreshRateInHighZone = refreshRate; restartObserver(); } } @@ -1332,48 +1496,95 @@ public class DisplayModeDirector { public void dumpLocked(PrintWriter pw) { pw.println(" BrightnessObserver"); pw.println(" mAmbientLux: " + mAmbientLux); - pw.println(" mRefreshRateInZone: " + mRefreshRateInZone); + pw.println(" mBrightness: " + mBrightness); + pw.println(" mDefaultDisplayOn: " + mDefaultDisplayOn); + pw.println(" mLowPowerModeEnabled: " + mLowPowerModeEnabled); + pw.println(" mRefreshRateChangeable: " + mRefreshRateChangeable); + pw.println(" mShouldObserveDisplayLowChange: " + mShouldObserveDisplayLowChange); + pw.println(" mShouldObserveAmbientLowChange: " + mShouldObserveAmbientLowChange); + pw.println(" mRefreshRateInLowZone: " + mRefreshRateInLowZone); - for (int d: mDisplayBrightnessThresholds) { - pw.println(" mDisplayBrightnessThreshold: " + d); + for (int d : mLowDisplayBrightnessThresholds) { + pw.println(" mDisplayLowBrightnessThreshold: " + d); } - for (int d: mAmbientBrightnessThresholds) { - pw.println(" mAmbientBrightnessThreshold: " + d); + for (int d : mLowAmbientBrightnessThresholds) { + pw.println(" mAmbientLowBrightnessThreshold: " + d); + } + + pw.println(" mShouldObserveDisplayHighChange: " + mShouldObserveDisplayHighChange); + pw.println(" mShouldObserveAmbientHighChange: " + mShouldObserveAmbientHighChange); + pw.println(" mRefreshRateInHighZone: " + mRefreshRateInHighZone); + + for (int d : mHighDisplayBrightnessThresholds) { + pw.println(" mDisplayHighBrightnessThresholds: " + d); + } + + for (int d : mHighAmbientBrightnessThresholds) { + pw.println(" mAmbientHighBrightnessThresholds: " + d); } mLightSensorListener.dumpLocked(pw); + + if (mAmbientFilter != null) { + IndentingPrintWriter ipw = new IndentingPrintWriter(pw); + ipw.setIndent(" "); + mAmbientFilter.dump(ipw); + } } public void onDisplayChanged(int displayId) { if (displayId == Display.DEFAULT_DISPLAY) { - onScreenOn(isDefaultDisplayOn()); + updateDefaultDisplayState(); } } @Override public void onChange(boolean selfChange, Uri uri, int userId) { synchronized (mLock) { - onBrightnessChangedLocked(); + final ContentResolver cr = mContext.getContentResolver(); + int brightness = Settings.System.getIntForUser(cr, + Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId()); + if (brightness != mBrightness) { + mBrightness = brightness; + onBrightnessChangedLocked(); + } } } private void restartObserver() { - mShouldObserveDisplayChange = checkShouldObserve(mDisplayBrightnessThresholds); - mShouldObserveAmbientChange = checkShouldObserve(mAmbientBrightnessThresholds); - final ContentResolver cr = mContext.getContentResolver(); - if (mShouldObserveDisplayChange) { + + if (mRefreshRateInLowZone > 0) { + mShouldObserveDisplayLowChange = hasValidThreshold( + mLowDisplayBrightnessThresholds); + mShouldObserveAmbientLowChange = hasValidThreshold( + mLowAmbientBrightnessThresholds); + } else { + mShouldObserveDisplayLowChange = false; + mShouldObserveAmbientLowChange = false; + } + + if (mRefreshRateInHighZone > 0) { + mShouldObserveDisplayHighChange = hasValidThreshold( + mHighDisplayBrightnessThresholds); + mShouldObserveAmbientHighChange = hasValidThreshold( + mHighAmbientBrightnessThresholds); + } else { + mShouldObserveDisplayHighChange = false; + mShouldObserveAmbientHighChange = false; + } + + if (mShouldObserveDisplayLowChange || mShouldObserveDisplayHighChange) { // Content Service does not check if an listener has already been registered. // To ensure only one listener is registered, force an unregistration first. - cr.unregisterContentObserver(this); - cr.registerContentObserver(mDisplayBrightnessSetting, - false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM); + mInjector.unregisterBrightnessObserver(cr, this); + mInjector.registerBrightnessObserver(cr, this); } else { - cr.unregisterContentObserver(this); + mInjector.unregisterBrightnessObserver(cr, this); } - if (mShouldObserveAmbientChange) { + if (mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) { Resources resources = mContext.getResources(); String lightSensorType = resources.getString( com.android.internal.R.string.config_displayLightSensorType); @@ -1399,8 +1610,6 @@ public class DisplayModeDirector { mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, res); mLightSensor = lightSensor; - - onScreenOn(isDefaultDisplayOn()); } } else { mAmbientFilter = null; @@ -1419,11 +1628,7 @@ public class DisplayModeDirector { * Checks to see if at least one value is positive, in which case it is necessary to listen * to value changes. */ - private boolean checkShouldObserve(int[] a) { - if (mRefreshRateInZone <= 0) { - return false; - } - + private boolean hasValidThreshold(int[] a) { for (int d: a) { if (d >= 0) { return true; @@ -1433,13 +1638,13 @@ public class DisplayModeDirector { return false; } - private boolean isInsideZone(int brightness, float lux) { - for (int i = 0; i < mDisplayBrightnessThresholds.length; i++) { - int disp = mDisplayBrightnessThresholds[i]; - int ambi = mAmbientBrightnessThresholds[i]; + private boolean isInsideLowZone(int brightness, float lux) { + for (int i = 0; i < mLowDisplayBrightnessThresholds.length; i++) { + int disp = mLowDisplayBrightnessThresholds[i]; + int ambi = mLowAmbientBrightnessThresholds[i]; if (disp >= 0 && ambi >= 0) { - if (brightness <= disp && mAmbientLux <= ambi) { + if (brightness <= disp && lux <= ambi) { return true; } } else if (disp >= 0) { @@ -1447,7 +1652,7 @@ public class DisplayModeDirector { return true; } } else if (ambi >= 0) { - if (mAmbientLux <= ambi) { + if (lux <= ambi) { return true; } } @@ -1455,28 +1660,77 @@ public class DisplayModeDirector { return false; } - // TODO: brightnessfloat: make it use float not int - private void onBrightnessChangedLocked() { - final ContentResolver cr = mContext.getContentResolver(); - int brightness = Settings.System.getIntForUser(cr, - Settings.System.SCREEN_BRIGHTNESS, -1, cr.getUserId()); + private boolean isInsideHighZone(int brightness, float lux) { + for (int i = 0; i < mHighDisplayBrightnessThresholds.length; i++) { + int disp = mHighDisplayBrightnessThresholds[i]; + int ambi = mHighAmbientBrightnessThresholds[i]; + + if (disp >= 0 && ambi >= 0) { + if (brightness >= disp && lux >= ambi) { + return true; + } + } else if (disp >= 0) { + if (brightness >= disp) { + return true; + } + } else if (ambi >= 0) { + if (lux >= ambi) { + return true; + } + } + } + + return false; + } + private void onBrightnessChangedLocked() { Vote vote = null; - boolean insideZone = isInsideZone(brightness, mAmbientLux); - if (insideZone) { - vote = Vote.forRefreshRates(mRefreshRateInZone, mRefreshRateInZone); + + if (mBrightness < 0) { + // Either the setting isn't available or we shouldn't be observing yet anyways. + // Either way, just bail out since there's nothing we can do here. + return; + } + + boolean insideLowZone = hasValidLowZone() && isInsideLowZone(mBrightness, mAmbientLux); + if (insideLowZone) { + vote = Vote.forRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone); + } + + boolean insideHighZone = hasValidHighZone() + && isInsideHighZone(mBrightness, mAmbientLux); + if (insideHighZone) { + vote = Vote.forRefreshRates(mRefreshRateInHighZone, mRefreshRateInHighZone); } if (DEBUG) { - Slog.d(TAG, "Display brightness " + brightness + ", ambient lux " + mAmbientLux + - ", Vote " + vote); + Slog.d(TAG, "Display brightness " + mBrightness + ", ambient lux " + mAmbientLux + + ", Vote " + vote); } - updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, vote); + updateVoteLocked(Vote.PRIORITY_FLICKER, vote); + } + + private boolean hasValidLowZone() { + return mRefreshRateInLowZone > 0 + && (mShouldObserveDisplayLowChange || mShouldObserveAmbientLowChange); + } + + private boolean hasValidHighZone() { + return mRefreshRateInHighZone > 0 + && (mShouldObserveDisplayHighChange || mShouldObserveAmbientHighChange); + } + + private void updateDefaultDisplayState() { + Display display = mContext.getSystemService(DisplayManager.class) + .getDisplay(Display.DEFAULT_DISPLAY); + boolean defaultDisplayOn = display != null && display.getState() != Display.STATE_OFF; + setDefaultDisplayState(defaultDisplayOn); } - private void onScreenOn(boolean on) { - if (mScreenOn != on) { - mScreenOn = on; + @VisibleForTesting + public void setDefaultDisplayState(boolean on) { + if (mDefaultDisplayOn != on) { + mDefaultDisplayOn = on; updateSensorStatus(); } } @@ -1486,8 +1740,8 @@ public class DisplayModeDirector { return; } - if (mShouldObserveAmbientChange && mScreenOn && !mLowPowerModeEnabled - && mRefreshRateChangeable) { + if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) + && isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) { mSensorManager.registerListener(mLightSensorListener, mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler); } else { @@ -1496,11 +1750,8 @@ public class DisplayModeDirector { } } - private boolean isDefaultDisplayOn() { - final Display display = mContext.getSystemService(DisplayManager.class) - .getDisplay(Display.DEFAULT_DISPLAY); - return display.getState() != Display.STATE_OFF - && mContext.getSystemService(PowerManager.class).isInteractive(); + private boolean isDeviceActive() { + return mDefaultDisplayOn && mInjector.isDeviceInteractive(mContext); } private final class LightSensorEventListener implements SensorEventListener { @@ -1518,23 +1769,33 @@ public class DisplayModeDirector { Slog.d(TAG, "On sensor changed: " + mLastSensorData); } - boolean zoneChanged = isDifferentZone(mLastSensorData, mAmbientLux); - if (zoneChanged && mLastSensorData < mAmbientLux) { - // Easier to see flicker at lower brightness environment. Forget the history to - // get immediate response. - mAmbientFilter.clear(); + boolean lowZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux, + mLowAmbientBrightnessThresholds); + boolean highZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux, + mHighAmbientBrightnessThresholds); + if ((lowZoneChanged && mLastSensorData < mAmbientLux) + || (highZoneChanged && mLastSensorData > mAmbientLux)) { + // Easier to see flicker at lower brightness environment or high brightness + // environment. Forget the history to get immediate response. + if (mAmbientFilter != null) { + mAmbientFilter.clear(); + } } long now = SystemClock.uptimeMillis(); - mAmbientFilter.addValue(now, mLastSensorData); + if (mAmbientFilter != null) { + mAmbientFilter.addValue(now, mLastSensorData); + } mHandler.removeCallbacks(mInjectSensorEventRunnable); processSensorData(now); - if (zoneChanged && mLastSensorData > mAmbientLux) { + if ((lowZoneChanged && mLastSensorData > mAmbientLux) + || (highZoneChanged && mLastSensorData < mAmbientLux)) { // Sensor may not report new event if there is no brightness change. // Need to keep querying the temporal filter for the latest estimation, - // until enter in higher lux zone or is interrupted by a new sensor event. + // until sensor readout and filter estimation are in the same zone or + // is interrupted by a new sensor event. mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS); } } @@ -1549,17 +1810,19 @@ public class DisplayModeDirector { } private void processSensorData(long now) { - mAmbientLux = mAmbientFilter.getEstimate(now); + if (mAmbientFilter != null) { + mAmbientLux = mAmbientFilter.getEstimate(now); + } else { + mAmbientLux = mLastSensorData; + } synchronized (mLock) { onBrightnessChangedLocked(); } } - private boolean isDifferentZone(float lux1, float lux2) { - for (int z = 0; z < mAmbientBrightnessThresholds.length; z++) { - final float boundary = mAmbientBrightnessThresholds[z]; - + private boolean isDifferentZone(float lux1, float lux2, int[] luxThresholds) { + for (final float boundary : luxThresholds) { // Test each boundary. See if the current value and the new value are at // different sides. if ((lux1 <= boundary && lux2 > boundary) @@ -1579,7 +1842,10 @@ public class DisplayModeDirector { processSensorData(now); // Inject next event if there is a possible zone change. - if (isDifferentZone(mLastSensorData, mAmbientLux)) { + if (isDifferentZone(mLastSensorData, mAmbientLux, + mLowAmbientBrightnessThresholds) + || isDifferentZone(mLastSensorData, mAmbientLux, + mHighAmbientBrightnessThresholds)) { mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS); } } @@ -1592,72 +1858,113 @@ public class DisplayModeDirector { } public void startListening() { - DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, + mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, BackgroundThread.getExecutor(), this); } /* * Return null if no such property or wrong format (not comma separated integers). */ - public int[] getBrightnessThresholds() { + public int[] getLowDisplayBrightnessThresholds() { return getIntArrayProperty( DisplayManager.DeviceConfig. - KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS); + KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS); } /* * Return null if no such property or wrong format (not comma separated integers). */ - public int[] getAmbientThresholds() { + public int[] getLowAmbientBrightnessThresholds() { return getIntArrayProperty( DisplayManager.DeviceConfig. - KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS); + KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS); + } + + public int getRefreshRateInLowZone() { + int defaultRefreshRateInZone = mContext.getResources().getInteger( + R.integer.config_defaultRefreshRateInZone); + + int refreshRate = mDeviceConfig.getInt( + DeviceConfig.NAMESPACE_DISPLAY_MANAGER, + DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE, + defaultRefreshRateInZone); + + return refreshRate; } /* - * Return null if no such property + * Return null if no such property or wrong format (not comma separated integers). */ - public Float getDefaultPeakRefreshRate() { - float defaultPeakRefreshRate = DeviceConfig.getFloat( - DeviceConfig.NAMESPACE_DISPLAY_MANAGER, - DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1); + public int[] getHighDisplayBrightnessThresholds() { + return getIntArrayProperty( + DisplayManager.DeviceConfig + .KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS); + } - if (defaultPeakRefreshRate == -1) { - return null; - } - return defaultPeakRefreshRate; + /* + * Return null if no such property or wrong format (not comma separated integers). + */ + public int[] getHighAmbientBrightnessThresholds() { + return getIntArrayProperty( + DisplayManager.DeviceConfig + .KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS); } - public int getRefreshRateInZone() { + public int getRefreshRateInHighZone() { int defaultRefreshRateInZone = mContext.getResources().getInteger( - R.integer.config_defaultRefreshRateInZone); + R.integer.config_fixedRefreshRateInHighZone); - int refreshRate = DeviceConfig.getInt( + int refreshRate = mDeviceConfig.getInt( DeviceConfig.NAMESPACE_DISPLAY_MANAGER, - DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_ZONE, + DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE, defaultRefreshRateInZone); return refreshRate; } + /* + * Return null if no such property + */ + public Float getDefaultPeakRefreshRate() { + float defaultPeakRefreshRate = mDeviceConfig.getFloat( + DeviceConfig.NAMESPACE_DISPLAY_MANAGER, + DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1); + + if (defaultPeakRefreshRate == -1) { + return null; + } + return defaultPeakRefreshRate; + } + @Override public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) { - int[] brightnessThresholds = getBrightnessThresholds(); - int[] ambientThresholds = getAmbientThresholds(); Float defaultPeakRefreshRate = getDefaultPeakRefreshRate(); - int refreshRateInZone = getRefreshRateInZone(); - - mHandler.obtainMessage(MSG_BRIGHTNESS_THRESHOLDS_CHANGED, - new Pair<int[], int[]>(brightnessThresholds, ambientThresholds)) - .sendToTarget(); mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED, defaultPeakRefreshRate).sendToTarget(); - mHandler.obtainMessage(MSG_REFRESH_RATE_IN_ZONE_CHANGED, refreshRateInZone, - 0).sendToTarget(); + + int[] lowDisplayBrightnessThresholds = getLowDisplayBrightnessThresholds(); + int[] lowAmbientBrightnessThresholds = getLowAmbientBrightnessThresholds(); + int refreshRateInLowZone = getRefreshRateInLowZone(); + + mHandler.obtainMessage(MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED, + new Pair<>(lowDisplayBrightnessThresholds, lowAmbientBrightnessThresholds)) + .sendToTarget(); + mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone, 0) + .sendToTarget(); + + int[] highDisplayBrightnessThresholds = getHighDisplayBrightnessThresholds(); + int[] highAmbientBrightnessThresholds = getHighAmbientBrightnessThresholds(); + int refreshRateInHighZone = getRefreshRateInHighZone(); + + mHandler.obtainMessage(MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED, + new Pair<>(highDisplayBrightnessThresholds, highAmbientBrightnessThresholds)) + .sendToTarget(); + mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED, refreshRateInHighZone, 0) + .sendToTarget(); } private int[] getIntArrayProperty(String prop) { - String strArray = DeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop, + String strArray = mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop, null); if (strArray != null) { @@ -1684,4 +1991,59 @@ public class DisplayModeDirector { } } + interface Injector { + // TODO: brightnessfloat: change this to the float setting + Uri DISPLAY_BRIGHTNESS_URI = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS); + Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE); + + @NonNull + DeviceConfigInterface getDeviceConfig(); + + void registerBrightnessObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer); + + void unregisterBrightnessObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer); + + void registerPeakRefreshRateObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer); + + boolean isDeviceInteractive(@NonNull Context context); + } + + @VisibleForTesting + static class RealInjector implements Injector { + + @Override + @NonNull + public DeviceConfigInterface getDeviceConfig() { + return DeviceConfigInterface.REAL; + } + + @Override + public void registerBrightnessObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer) { + cr.registerContentObserver(DISPLAY_BRIGHTNESS_URI, false /*notifyDescendants*/, + observer, UserHandle.USER_SYSTEM); + } + + @Override + public void unregisterBrightnessObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer) { + cr.unregisterContentObserver(observer); + } + + @Override + public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer) { + cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/, + observer, UserHandle.USER_SYSTEM); + } + + @Override + public boolean isDeviceInteractive(@NonNull Context ctx) { + return ctx.getSystemService(PowerManager.class).isInteractive(); + } + } + } diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java index 494dd397b1e9..b0820e81ec09 100644 --- a/services/core/java/com/android/server/display/PersistentDataStore.java +++ b/services/core/java/com/android/server/display/PersistentDataStore.java @@ -25,6 +25,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseLongArray; import android.util.TimeUtils; +import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import android.util.Xml; import android.view.Display; @@ -322,7 +323,7 @@ final class PersistentDataStore { return; } - XmlPullParser parser; + TypedXmlPullParser parser; try { parser = Xml.resolvePullParser(is); loadFromXml(parser); @@ -355,7 +356,7 @@ final class PersistentDataStore { } } - private void loadFromXml(XmlPullParser parser) + private void loadFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { XmlUtils.beginDocument(parser, TAG_DISPLAY_MANAGER_STATE); final int outerDepth = parser.getDepth(); @@ -375,7 +376,7 @@ final class PersistentDataStore { } } - private void loadRememberedWifiDisplaysFromXml(XmlPullParser parser) + private void loadRememberedWifiDisplaysFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, outerDepth)) { @@ -399,7 +400,7 @@ final class PersistentDataStore { } } - private void loadDisplaysFromXml(XmlPullParser parser) + private void loadDisplaysFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, outerDepth)) { @@ -420,7 +421,7 @@ final class PersistentDataStore { } } - private void saveToXml(XmlSerializer serializer) throws IOException { + private void saveToXml(TypedXmlSerializer serializer) throws IOException { serializer.startDocument(null, true); serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); serializer.startTag(null, TAG_DISPLAY_MANAGER_STATE); @@ -491,7 +492,7 @@ final class PersistentDataStore { return mColorMode; } - public void loadFromXml(XmlPullParser parser) + public void loadFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); @@ -503,7 +504,7 @@ final class PersistentDataStore { } } - public void saveToXml(XmlSerializer serializer) throws IOException { + public void saveToXml(TypedXmlSerializer serializer) throws IOException { serializer.startTag(null, TAG_COLOR_MODE); serializer.text(Integer.toString(mColorMode)); serializer.endTag(null, TAG_COLOR_MODE); @@ -531,7 +532,8 @@ final class PersistentDataStore { return false; } - public void loadFromXml(XmlPullParser parser) throws IOException, XmlPullParserException { + public void loadFromXml(TypedXmlPullParser parser) + throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, outerDepth)) { switch (parser.getName()) { @@ -545,7 +547,7 @@ final class PersistentDataStore { } } - private static int loadIntValue(XmlPullParser parser) + private static int loadIntValue(TypedXmlPullParser parser) throws IOException, XmlPullParserException { try { String value = parser.nextText(); @@ -555,7 +557,7 @@ final class PersistentDataStore { } } - public void saveToXml(XmlSerializer serializer) throws IOException { + public void saveToXml(TypedXmlSerializer serializer) throws IOException { if (mWidth > 0 && mHeight > 0) { serializer.startTag(null, TAG_STABLE_DISPLAY_WIDTH); serializer.text(Integer.toString(mWidth)); @@ -612,14 +614,14 @@ final class PersistentDataStore { return mConfigurations.get(userSerial); } - public void loadFromXml(XmlPullParser parser) throws IOException, XmlPullParserException { + public void loadFromXml(TypedXmlPullParser parser) + throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, outerDepth)) { if (TAG_BRIGHTNESS_CONFIGURATION.equals(parser.getName())) { int userSerial; try { - userSerial = Integer.parseInt( - parser.getAttributeValue(null, ATTR_USER_SERIAL)); + userSerial = parser.getAttributeInt(null, ATTR_USER_SERIAL); } catch (NumberFormatException nfe) { userSerial = -1; Slog.e(TAG, "Failed to read in brightness configuration", nfe); @@ -655,20 +657,20 @@ final class PersistentDataStore { } } - public void saveToXml(XmlSerializer serializer) throws IOException { + public void saveToXml(TypedXmlSerializer serializer) throws IOException { for (int i = 0; i < mConfigurations.size(); i++) { final int userSerial = mConfigurations.keyAt(i); final BrightnessConfiguration config = mConfigurations.valueAt(i); serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATION); - serializer.attribute(null, ATTR_USER_SERIAL, Integer.toString(userSerial)); + serializer.attributeInt(null, ATTR_USER_SERIAL, userSerial); String packageName = mPackageNames.get(userSerial); if (packageName != null) { serializer.attribute(null, ATTR_PACKAGE_NAME, packageName); } long timestamp = mTimeStamps.get(userSerial, -1); if (timestamp != -1) { - serializer.attribute(null, ATTR_TIME_STAMP, Long.toString(timestamp)); + serializer.attributeLong(null, ATTR_TIME_STAMP, timestamp); } config.saveToXml(serializer); serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATION); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java index 8d42a909978e..2374ece1dd65 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java @@ -92,7 +92,7 @@ public class HdmiCecConfig { @NonNull private final Context mContext; @NonNull private final StorageAdapter mStorageAdapter; - @Nullable private final CecSettings mSystemConfig; + @Nullable private final CecSettings mProductConfig; @Nullable private final CecSettings mVendorOverride; private final ArrayMap<Setting, Set<SettingChangeListener>> @@ -198,14 +198,14 @@ public class HdmiCecConfig { @VisibleForTesting HdmiCecConfig(@NonNull Context context, @NonNull StorageAdapter storageAdapter, - @Nullable CecSettings systemConfig, + @Nullable CecSettings productConfig, @Nullable CecSettings vendorOverride) { mContext = context; mStorageAdapter = storageAdapter; - mSystemConfig = systemConfig; + mProductConfig = productConfig; mVendorOverride = vendorOverride; - if (mSystemConfig == null) { - Slog.i(TAG, "CEC system configuration XML missing."); + if (mProductConfig == null) { + Slog.i(TAG, "CEC master configuration XML missing."); } if (mVendorOverride == null) { Slog.i(TAG, "CEC OEM configuration override XML missing."); @@ -214,7 +214,7 @@ public class HdmiCecConfig { HdmiCecConfig(@NonNull Context context) { this(context, new StorageAdapter(context), - readSettingsFromFile(Environment.buildPath(Environment.getRootDirectory(), + readSettingsFromFile(Environment.buildPath(Environment.getProductDirectory(), ETC_DIR, CONFIG_FILE)), readSettingsFromFile(Environment.buildPath(Environment.getVendorDirectory(), ETC_DIR, CONFIG_FILE))); @@ -241,14 +241,14 @@ public class HdmiCecConfig { @VisibleForTesting static HdmiCecConfig createFromStrings(@NonNull Context context, @NonNull StorageAdapter storageAdapter, - @Nullable String systemConfigXml, + @Nullable String productConfigXml, @Nullable String vendorOverrideXml) { - CecSettings systemConfig = null; + CecSettings productConfig = null; CecSettings vendorOverride = null; try { - if (systemConfigXml != null) { - systemConfig = XmlParser.read( - new ByteArrayInputStream(systemConfigXml.getBytes())); + if (productConfigXml != null) { + productConfig = XmlParser.read( + new ByteArrayInputStream(productConfigXml.getBytes())); } if (vendorOverrideXml != null) { vendorOverride = XmlParser.read( @@ -257,12 +257,12 @@ public class HdmiCecConfig { } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) { Slog.e(TAG, "Encountered an error while reading/parsing CEC config strings", e); } - return new HdmiCecConfig(context, storageAdapter, systemConfig, vendorOverride); + return new HdmiCecConfig(context, storageAdapter, productConfig, vendorOverride); } @Nullable private Setting getSetting(@NonNull String name) { - if (mSystemConfig == null) { + if (mProductConfig == null) { return null; } if (mVendorOverride != null) { @@ -273,8 +273,8 @@ public class HdmiCecConfig { } } } - // If not found, try the system config. - for (Setting setting : mSystemConfig.getSetting()) { + // If not found, try the product config. + for (Setting setting : mProductConfig.getSetting()) { if (setting.getName().equals(name)) { return setting; } @@ -456,11 +456,11 @@ public class HdmiCecConfig { * Returns a list of all settings based on the XML metadata. */ public @CecSettingName List<String> getAllSettings() { - if (mSystemConfig == null) { + if (mProductConfig == null) { return new ArrayList<String>(); } List<String> allSettings = new ArrayList<String>(); - for (Setting setting : mSystemConfig.getSetting()) { + for (Setting setting : mProductConfig.getSetting()) { allSettings.add(setting.getName()); } return allSettings; @@ -470,12 +470,12 @@ public class HdmiCecConfig { * Returns a list of user-modifiable settings based on the XML metadata. */ public @CecSettingName List<String> getUserSettings() { - if (mSystemConfig == null) { + if (mProductConfig == null) { return new ArrayList<String>(); } Set<String> userSettings = new HashSet<String>(); - // First read from the system config. - for (Setting setting : mSystemConfig.getSetting()) { + // First read from the product config. + for (Setting setting : mProductConfig.getSetting()) { if (setting.getUserConfigurable()) { userSettings.add(setting.getName()); } diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java index e47b54472192..52a804a36750 100644 --- a/services/core/java/com/android/server/hdmi/HdmiUtils.java +++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java @@ -25,6 +25,7 @@ import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.util.Slog; import android.util.SparseArray; +import android.util.TypedXmlPullParser; import android.util.Xml; import com.android.internal.util.HexDump; @@ -571,14 +572,13 @@ final class HdmiUtils { // return a list of devices config public static List<DeviceConfig> parse(InputStream in) throws XmlPullParserException, IOException { - XmlPullParser parser = Xml.newPullParser(); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); - parser.setInput(in, null); + TypedXmlPullParser parser = Xml.resolvePullParser(in); parser.nextTag(); return readDevices(parser); } - private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException { + private static void skip(TypedXmlPullParser parser) + throws XmlPullParserException, IOException { if (parser.getEventType() != XmlPullParser.START_TAG) { throw new IllegalStateException(); } @@ -595,7 +595,7 @@ final class HdmiUtils { } } - private static List<DeviceConfig> readDevices(XmlPullParser parser) + private static List<DeviceConfig> readDevices(TypedXmlPullParser parser) throws XmlPullParserException, IOException { List<DeviceConfig> devices = new ArrayList<>(); @@ -624,7 +624,7 @@ final class HdmiUtils { // Processes device tags in the config. @Nullable - private static DeviceConfig readDeviceConfig(XmlPullParser parser, String deviceType) + private static DeviceConfig readDeviceConfig(TypedXmlPullParser parser, String deviceType) throws XmlPullParserException, IOException { List<CodecSad> codecSads = new ArrayList<>(); int format; diff --git a/services/core/java/com/android/server/input/ConfigurationProcessor.java b/services/core/java/com/android/server/input/ConfigurationProcessor.java index 3888b1b71096..0563806895a0 100644 --- a/services/core/java/com/android/server/input/ConfigurationProcessor.java +++ b/services/core/java/com/android/server/input/ConfigurationProcessor.java @@ -18,15 +18,13 @@ package com.android.server.input; import android.text.TextUtils; import android.util.Slog; +import android.util.TypedXmlPullParser; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.XmlUtils; -import org.xmlpull.v1.XmlPullParser; - import java.io.InputStream; -import java.io.InputStreamReader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -38,9 +36,8 @@ class ConfigurationProcessor { static List<String> processExcludedDeviceNames(InputStream xml) throws Exception { List<String> names = new ArrayList<>(); - try (InputStreamReader confReader = new InputStreamReader(xml)) { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(confReader); + { + TypedXmlPullParser parser = Xml.resolvePullParser(xml); XmlUtils.beginDocument(parser, "devices"); while (true) { XmlUtils.nextElement(parser); @@ -90,9 +87,8 @@ class ConfigurationProcessor { static Map<String, Integer> processInputPortAssociations(InputStream xml) throws Exception { Map<String, Integer> associations = new HashMap<String, Integer>(); - try (InputStreamReader confReader = new InputStreamReader(xml)) { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(confReader); + { + TypedXmlPullParser parser = Xml.resolvePullParser(xml); XmlUtils.beginDocument(parser, "ports"); while (true) { diff --git a/services/core/java/com/android/server/input/PersistentDataStore.java b/services/core/java/com/android/server/input/PersistentDataStore.java index f61662d34e75..a735a8f9d305 100644 --- a/services/core/java/com/android/server/input/PersistentDataStore.java +++ b/services/core/java/com/android/server/input/PersistentDataStore.java @@ -253,7 +253,7 @@ final class PersistentDataStore { } } - private void loadFromXml(XmlPullParser parser) + private void loadFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { XmlUtils.beginDocument(parser, "input-manager-state"); final int outerDepth = parser.getDepth(); @@ -264,7 +264,7 @@ final class PersistentDataStore { } } - private void loadInputDevicesFromXml(XmlPullParser parser) + private void loadInputDevicesFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, outerDepth)) { @@ -285,7 +285,7 @@ final class PersistentDataStore { } } - private void saveToXml(XmlSerializer serializer) throws IOException { + private void saveToXml(TypedXmlSerializer serializer) throws IOException { serializer.startDocument(null, true); serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); serializer.startTag(null, "input-manager-state"); @@ -422,7 +422,7 @@ final class PersistentDataStore { return changed; } - public void loadFromXml(XmlPullParser parser) + public void loadFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, outerDepth)) { @@ -505,7 +505,7 @@ final class PersistentDataStore { } } - public void saveToXml(XmlSerializer serializer) throws IOException { + public void saveToXml(TypedXmlSerializer serializer) throws IOException { for (String layout : mKeyboardLayouts) { serializer.startTag(null, "keyboard-layout"); serializer.attribute(null, "descriptor", layout); diff --git a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java index a077b049b014..24b8e340dee9 100644 --- a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java +++ b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java @@ -31,17 +31,13 @@ import android.util.Xml; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; -import com.android.internal.util.FastXmlSerializer; - import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -158,20 +154,17 @@ final class AdditionalSubtypeUtils { final InputMethodSubtype subtype = subtypesList.get(i); out.startTag(null, NODE_SUBTYPE); if (subtype.hasSubtypeId()) { - out.attribute(null, ATTR_IME_SUBTYPE_ID, - String.valueOf(subtype.getSubtypeId())); + out.attributeInt(null, ATTR_IME_SUBTYPE_ID, subtype.getSubtypeId()); } - out.attribute(null, ATTR_ICON, String.valueOf(subtype.getIconResId())); - out.attribute(null, ATTR_LABEL, String.valueOf(subtype.getNameResId())); + out.attributeInt(null, ATTR_ICON, subtype.getIconResId()); + out.attributeInt(null, ATTR_LABEL, subtype.getNameResId()); out.attribute(null, ATTR_IME_SUBTYPE_LOCALE, subtype.getLocale()); out.attribute(null, ATTR_IME_SUBTYPE_LANGUAGE_TAG, subtype.getLanguageTag()); out.attribute(null, ATTR_IME_SUBTYPE_MODE, subtype.getMode()); out.attribute(null, ATTR_IME_SUBTYPE_EXTRA_VALUE, subtype.getExtraValue()); - out.attribute(null, ATTR_IS_AUXILIARY, - String.valueOf(subtype.isAuxiliary() ? 1 : 0)); - out.attribute(null, ATTR_IS_ASCII_CAPABLE, - String.valueOf(subtype.isAsciiCapable() ? 1 : 0)); + out.attributeInt(null, ATTR_IS_AUXILIARY, subtype.isAuxiliary() ? 1 : 0); + out.attributeInt(null, ATTR_IS_ASCII_CAPABLE, subtype.isAsciiCapable() ? 1 : 0); out.endTag(null, NODE_SUBTYPE); } out.endTag(null, NODE_IMI); @@ -243,10 +236,8 @@ final class AdditionalSubtypeUtils { Slog.w(TAG, "IME uninstalled or not valid.: " + currentImiId); continue; } - final int icon = Integer.parseInt( - parser.getAttributeValue(null, ATTR_ICON)); - final int label = Integer.parseInt( - parser.getAttributeValue(null, ATTR_LABEL)); + final int icon = parser.getAttributeInt(null, ATTR_ICON); + final int label = parser.getAttributeInt(null, ATTR_LABEL); final String imeSubtypeLocale = parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LOCALE); final String languageTag = @@ -269,10 +260,10 @@ final class AdditionalSubtypeUtils { .setSubtypeExtraValue(imeSubtypeExtraValue) .setIsAuxiliary(isAuxiliary) .setIsAsciiCapable(isAsciiCapable); - final String subtypeIdString = - parser.getAttributeValue(null, ATTR_IME_SUBTYPE_ID); - if (subtypeIdString != null) { - builder.setSubtypeId(Integer.parseInt(subtypeIdString)); + final int subtypeId = parser.getAttributeInt(null, ATTR_IME_SUBTYPE_ID, + InputMethodSubtype.SUBTYPE_ID_NONE); + if (subtypeId != InputMethodSubtype.SUBTYPE_ID_NONE) { + builder.setSubtypeId(subtypeId); } tempSubtypesArray.add(builder.build()); } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 0ceaf7748eba..a257cde185f3 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -158,6 +158,8 @@ import android.view.inputmethod.InputMethodSubtype; import com.android.internal.annotations.GuardedBy; import com.android.internal.compat.IPlatformCompat; import com.android.internal.content.PackageMonitor; +import com.android.internal.inputmethod.CallbackUtils; +import com.android.internal.inputmethod.IInputBindResultResultCallback; import com.android.internal.inputmethod.IInputContentUriToken; import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; import com.android.internal.inputmethod.InputMethodDebug; @@ -208,6 +210,7 @@ import java.util.Objects; import java.util.WeakHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; /** * This class provides a system service that manages input methods. @@ -2284,6 +2287,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } if (mCurClient == cs) { + hideCurrentInputLocked( + mCurFocusedWindow, 0, null, SoftInputShowHideReason.HIDE_REMOVE_CLIENT); if (mBoundToMethod) { mBoundToMethod = false; if (mCurMethod != null) { @@ -3345,63 +3350,68 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @NonNull @Override - public InputBindResult startInputOrWindowGainedFocus( + public void startInputOrWindowGainedFocus( @StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken, @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode, int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext, - @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion) { - if (windowToken == null) { - Slog.e(TAG, "windowToken cannot be null."); - return InputBindResult.NULL; - } - try { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, - "IMMS.startInputOrWindowGainedFocus"); - ImeTracing.getInstance().triggerManagerServiceDump( - "InputMethodManagerService#startInputOrWindowGainedFocus"); - final int callingUserId = UserHandle.getCallingUserId(); - final int userId; - if (attribute != null && attribute.targetInputMethodUser != null - && attribute.targetInputMethodUser.getIdentifier() != callingUserId) { - mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, - "Using EditorInfo.targetInputMethodUser requires" - + " INTERACT_ACROSS_USERS_FULL."); - userId = attribute.targetInputMethodUser.getIdentifier(); - if (!mUserManagerInternal.isUserRunning(userId)) { - // There is a chance that we hit here because of race condition. Let's just - // return an error code instead of crashing the caller process, which at least - // has INTERACT_ACROSS_USERS_FULL permission thus is likely to be an important - // process. - Slog.e(TAG, "User #" + userId + " is not running."); - return InputBindResult.INVALID_USER; - } - } else { - userId = callingUserId; - } - final InputBindResult result; - synchronized (mMethodMap) { - final long ident = Binder.clearCallingIdentity(); - try { - result = startInputOrWindowGainedFocusInternalLocked(startInputReason, client, - windowToken, startInputFlags, softInputMode, windowFlags, attribute, - inputContext, missingMethods, unverifiedTargetSdkVersion, userId); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - if (result == null) { - // This must never happen, but just in case. - Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason=" - + InputMethodDebug.startInputReasonToString(startInputReason) - + " windowFlags=#" + Integer.toHexString(windowFlags) - + " editorInfo=" + attribute); + @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion, + IInputBindResultResultCallback resultCallback) { + CallbackUtils.onResult(resultCallback, (Supplier<InputBindResult>) () -> { + if (windowToken == null) { + Slog.e(TAG, "windowToken cannot be null."); return InputBindResult.NULL; } + try { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, + "IMMS.startInputOrWindowGainedFocus"); + ImeTracing.getInstance().triggerManagerServiceDump( + "InputMethodManagerService#startInputOrWindowGainedFocus"); + final int callingUserId = UserHandle.getCallingUserId(); + final int userId; + if (attribute != null && attribute.targetInputMethodUser != null + && attribute.targetInputMethodUser.getIdentifier() != callingUserId) { + mContext.enforceCallingPermission( + Manifest.permission.INTERACT_ACROSS_USERS_FULL, + "Using EditorInfo.targetInputMethodUser requires" + + " INTERACT_ACROSS_USERS_FULL."); + userId = attribute.targetInputMethodUser.getIdentifier(); + if (!mUserManagerInternal.isUserRunning(userId)) { + // There is a chance that we hit here because of race condition. Let's just + // return an error code instead of crashing the caller process, which at + // least has INTERACT_ACROSS_USERS_FULL permission thus is likely to be an + // important process. + Slog.e(TAG, "User #" + userId + " is not running."); + return InputBindResult.INVALID_USER; + } + } else { + userId = callingUserId; + } + final InputBindResult result; + synchronized (mMethodMap) { + final long ident = Binder.clearCallingIdentity(); + try { + result = startInputOrWindowGainedFocusInternalLocked(startInputReason, + client, windowToken, startInputFlags, softInputMode, windowFlags, + attribute, inputContext, missingMethods, unverifiedTargetSdkVersion, + userId); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + if (result == null) { + // This must never happen, but just in case. + Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason=" + + InputMethodDebug.startInputReasonToString(startInputReason) + + " windowFlags=#" + Integer.toHexString(windowFlags) + + " editorInfo=" + attribute); + return InputBindResult.NULL; + } - return result; - } finally { - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - } + return result; + } finally { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + }); } @NonNull diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index 62d817c22ae6..6bdae63461b2 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -72,6 +72,8 @@ import android.view.inputmethod.InputMethodSubtype; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.inputmethod.CallbackUtils; +import com.android.internal.inputmethod.IInputBindResultResultCallback; import com.android.internal.inputmethod.IMultiClientInputMethod; import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations; import com.android.internal.inputmethod.IMultiClientInputMethodSession; @@ -104,6 +106,7 @@ import java.lang.annotation.Retention; import java.util.Collections; import java.util.List; import java.util.WeakHashMap; +import java.util.function.Supplier; /** * Actual implementation of multi-client InputMethodManagerService. @@ -1588,7 +1591,26 @@ public final class MultiClientInputMethodManagerService { @BinderThread @Override - public InputBindResult startInputOrWindowGainedFocus( + public void startInputOrWindowGainedFocus( + @StartInputReason int startInputReason, + @Nullable IInputMethodClient client, + @Nullable IBinder windowToken, + @StartInputFlags int startInputFlags, + @SoftInputModeFlags int softInputMode, + int windowFlags, + @Nullable EditorInfo editorInfo, + @Nullable IInputContext inputContext, + @MissingMethodFlags int missingMethods, + int unverifiedTargetSdkVersion, + IInputBindResultResultCallback resultCallback) { + CallbackUtils.onResult(resultCallback, (Supplier<InputBindResult>) () -> + startInputOrWindowGainedFocusInternal(startInputReason, client, windowToken, + startInputFlags, softInputMode, windowFlags, editorInfo, inputContext, + missingMethods, unverifiedTargetSdkVersion)); + } + + @BinderThread + private InputBindResult startInputOrWindowGainedFocusInternal( @StartInputReason int startInputReason, @Nullable IInputMethodClient client, @Nullable IBinder windowToken, @@ -1676,8 +1698,7 @@ public final class MultiClientInputMethodManagerService { clientInfo.mMSInputMethodSession.startInputOrWindowGainedFocus( inputContext, missingMethods, editorInfo, startInputFlags, softInputMode, windowHandle); - } catch (RemoteException e) { - } + } catch (RemoteException ignored) { } break; } return InputBindResult.NULL_EDITOR_INFO; @@ -1708,8 +1729,7 @@ public final class MultiClientInputMethodManagerService { clientInfo.mMSInputMethodSession.startInputOrWindowGainedFocus( inputContext, missingMethods, editorInfo, startInputFlags, softInputMode, windowHandle); - } catch (RemoteException e) { - } + } catch (RemoteException ignored) { } clientInfo.mState = InputMethodClientState.ALREADY_SENT_BIND_RESULT; return new InputBindResult( InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION, diff --git a/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java b/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java index 28d2e6914103..ab912906617b 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java +++ b/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java @@ -17,6 +17,7 @@ package com.android.server.integrity.parser; import android.annotation.Nullable; +import android.util.TypedXmlPullParser; import android.util.Xml; import com.android.server.integrity.model.RuleMetadata; @@ -26,7 +27,6 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.StandardCharsets; /** Helper class for parsing rule metadata. */ public class RuleMetadataParser { @@ -42,8 +42,7 @@ public class RuleMetadataParser { String ruleProvider = ""; String version = ""; - XmlPullParser xmlPullParser = Xml.newPullParser(); - xmlPullParser.setInput(inputStream, StandardCharsets.UTF_8.name()); + TypedXmlPullParser xmlPullParser = Xml.resolvePullParser(inputStream); int eventType; while ((eventType = xmlPullParser.next()) != XmlPullParser.END_DOCUMENT) { diff --git a/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java index 5c51f31ba8cc..7aed35252816 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java @@ -19,6 +19,7 @@ package com.android.server.integrity.serializer; import static com.android.server.integrity.parser.RuleMetadataParser.RULE_PROVIDER_TAG; import static com.android.server.integrity.parser.RuleMetadataParser.VERSION_TAG; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.server.integrity.model.RuleMetadata; @@ -34,8 +35,7 @@ public class RuleMetadataSerializer { /** Serialize the rule metadata to an output stream. */ public static void serialize(RuleMetadata ruleMetadata, OutputStream outputStream) throws IOException { - XmlSerializer xmlSerializer = Xml.newSerializer(); - xmlSerializer.setOutput(outputStream, StandardCharsets.UTF_8.name()); + TypedXmlSerializer xmlSerializer = Xml.resolveSerializer(outputStream); serializeTaggedValue(xmlSerializer, RULE_PROVIDER_TAG, ruleMetadata.getRuleProvider()); serializeTaggedValue(xmlSerializer, VERSION_TAG, ruleMetadata.getVersion()); @@ -43,8 +43,8 @@ public class RuleMetadataSerializer { xmlSerializer.endDocument(); } - private static void serializeTaggedValue(XmlSerializer xmlSerializer, String tag, String value) - throws IOException { + private static void serializeTaggedValue(TypedXmlSerializer xmlSerializer, String tag, + String value) throws IOException { xmlSerializer.startTag(/* namespace= */ null, tag); xmlSerializer.text(value); xmlSerializer.endTag(/* namespace= */ null, tag); diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java index a8889fd6b454..9e126673637f 100644 --- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java @@ -987,21 +987,17 @@ public class GnssLocationProvider extends AbstractLocationProvider implements // update client uids updateClientUids(mProviderRequest.getWorkSource()); - mFixInterval = (int) mProviderRequest.getIntervalMillis(); - // check for overflow - if (mFixInterval != mProviderRequest.getIntervalMillis()) { + if (mProviderRequest.getIntervalMillis() <= Integer.MAX_VALUE) { + mFixInterval = (int) mProviderRequest.getIntervalMillis(); + } else { Log.w(TAG, "interval overflow: " + mProviderRequest.getIntervalMillis()); mFixInterval = Integer.MAX_VALUE; } - // requested batch size, or zero to disable batching - int batchSize; - try { - batchSize = mBatchingEnabled ? Math.toIntExact( - mProviderRequest.getMaxUpdateDelayMillis() / mFixInterval) : 0; - } catch (ArithmeticException e) { - batchSize = Integer.MAX_VALUE; - } + // requested batch size, or zero to disable batching + long batchSize = + mBatchingEnabled ? mProviderRequest.getMaxUpdateDelayMillis() / Math.max( + mFixInterval, 1) : 0; if (batchSize < getBatchSize()) { batchSize = 0; } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java index 8a19d62de0b9..0c209c5b48dc 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java @@ -18,7 +18,6 @@ package com.android.server.locksettings.recoverablekeystore.serialization; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.CERTIFICATE_FACTORY_TYPE; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.NAMESPACE; -import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.OUTPUT_ENCODING; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALGORITHM; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY; @@ -46,6 +45,7 @@ import android.security.keystore.recovery.KeyChainSnapshot; import android.security.keystore.recovery.KeyDerivationParams; import android.security.keystore.recovery.WrappedApplicationKey; import android.util.Base64; +import android.util.TypedXmlPullParser; import android.util.Xml; import org.xmlpull.v1.XmlPullParser; @@ -84,8 +84,7 @@ public class KeyChainSnapshotDeserializer { private static KeyChainSnapshot deserializeInternal(InputStream inputStream) throws IOException, XmlPullParserException, KeyChainSnapshotParserException { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(inputStream, OUTPUT_ENCODING); + TypedXmlPullParser parser = Xml.resolvePullParser(inputStream); parser.nextTag(); parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_CHAIN_SNAPSHOT); @@ -156,7 +155,7 @@ public class KeyChainSnapshotDeserializer { } } - private static List<WrappedApplicationKey> readWrappedApplicationKeys(XmlPullParser parser) + private static List<WrappedApplicationKey> readWrappedApplicationKeys(TypedXmlPullParser parser) throws IOException, XmlPullParserException, KeyChainSnapshotParserException { parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_APPLICATION_KEYS); ArrayList<WrappedApplicationKey> keys = new ArrayList<>(); @@ -170,7 +169,7 @@ public class KeyChainSnapshotDeserializer { return keys; } - private static WrappedApplicationKey readWrappedApplicationKey(XmlPullParser parser) + private static WrappedApplicationKey readWrappedApplicationKey(TypedXmlPullParser parser) throws IOException, XmlPullParserException, KeyChainSnapshotParserException { parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_APPLICATION_KEY); WrappedApplicationKey.Builder builder = new WrappedApplicationKey.Builder(); @@ -209,7 +208,7 @@ public class KeyChainSnapshotDeserializer { } private static List<KeyChainProtectionParams> readKeyChainProtectionParamsList( - XmlPullParser parser) throws IOException, XmlPullParserException, + TypedXmlPullParser parser) throws IOException, XmlPullParserException, KeyChainSnapshotParserException { parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST); @@ -225,7 +224,7 @@ public class KeyChainSnapshotDeserializer { return keyChainProtectionParamsList; } - private static KeyChainProtectionParams readKeyChainProtectionParams(XmlPullParser parser) + private static KeyChainProtectionParams readKeyChainProtectionParams(TypedXmlPullParser parser) throws IOException, XmlPullParserException, KeyChainSnapshotParserException { parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS); @@ -269,7 +268,7 @@ public class KeyChainSnapshotDeserializer { } } - private static KeyDerivationParams readKeyDerivationParams(XmlPullParser parser) + private static KeyDerivationParams readKeyDerivationParams(TypedXmlPullParser parser) throws XmlPullParserException, IOException, KeyChainSnapshotParserException { parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_DERIVATION_PARAMS); @@ -331,7 +330,7 @@ public class KeyChainSnapshotDeserializer { return keyDerivationParams; } - private static int readIntTag(XmlPullParser parser, String tagName) + private static int readIntTag(TypedXmlPullParser parser, String tagName) throws IOException, XmlPullParserException, KeyChainSnapshotParserException { parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName); String text = readText(parser); @@ -345,7 +344,7 @@ public class KeyChainSnapshotDeserializer { } } - private static long readLongTag(XmlPullParser parser, String tagName) + private static long readLongTag(TypedXmlPullParser parser, String tagName) throws IOException, XmlPullParserException, KeyChainSnapshotParserException { parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName); String text = readText(parser); @@ -359,7 +358,7 @@ public class KeyChainSnapshotDeserializer { } } - private static String readStringTag(XmlPullParser parser, String tagName) + private static String readStringTag(TypedXmlPullParser parser, String tagName) throws IOException, XmlPullParserException { parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName); String text = readText(parser); @@ -367,7 +366,7 @@ public class KeyChainSnapshotDeserializer { return text; } - private static byte[] readBlobTag(XmlPullParser parser, String tagName) + private static byte[] readBlobTag(TypedXmlPullParser parser, String tagName) throws IOException, XmlPullParserException, KeyChainSnapshotParserException { parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName); String text = readText(parser); @@ -384,7 +383,7 @@ public class KeyChainSnapshotDeserializer { } } - private static CertPath readCertPathTag(XmlPullParser parser, String tagName) + private static CertPath readCertPathTag(TypedXmlPullParser parser, String tagName) throws IOException, XmlPullParserException, KeyChainSnapshotParserException { byte[] bytes = readBlobTag(parser, tagName); try { @@ -396,7 +395,7 @@ public class KeyChainSnapshotDeserializer { } } - private static String readText(XmlPullParser parser) + private static String readText(TypedXmlPullParser parser) throws IOException, XmlPullParserException { String result = ""; if (parser.next() == XmlPullParser.TEXT) { diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java index 8f85a27d4690..6475d9e530f8 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java @@ -22,8 +22,6 @@ package com.android.server.locksettings.recoverablekeystore.serialization; class KeyChainSnapshotSchema { static final String NAMESPACE = null; - static final String OUTPUT_ENCODING = "UTF-8"; - static final String CERTIFICATE_FACTORY_TYPE = "X.509"; static final String CERT_PATH_ENCODING = "PkiPath"; diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java index 527e879a198b..eb34e981723d 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java @@ -19,7 +19,6 @@ package com.android.server.locksettings.recoverablekeystore.serialization; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.CERT_PATH_ENCODING; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.NAMESPACE; -import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.OUTPUT_ENCODING; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALGORITHM; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY; @@ -47,10 +46,9 @@ import android.security.keystore.recovery.KeyChainSnapshot; import android.security.keystore.recovery.KeyDerivationParams; import android.security.keystore.recovery.WrappedApplicationKey; import android.util.Base64; +import android.util.TypedXmlSerializer; import android.util.Xml; -import org.xmlpull.v1.XmlSerializer; - import java.io.IOException; import java.io.OutputStream; import java.security.cert.CertPath; @@ -71,8 +69,7 @@ public class KeyChainSnapshotSerializer { */ public static void serialize(KeyChainSnapshot keyChainSnapshot, OutputStream outputStream) throws IOException, CertificateEncodingException { - XmlSerializer xmlSerializer = Xml.newSerializer(); - xmlSerializer.setOutput(outputStream, OUTPUT_ENCODING); + TypedXmlSerializer xmlSerializer = Xml.resolveSerializer(outputStream); xmlSerializer.startDocument( /*encoding=*/ null, /*standalone=*/ null); @@ -87,7 +84,7 @@ public class KeyChainSnapshotSerializer { } private static void writeApplicationKeys( - XmlSerializer xmlSerializer, List<WrappedApplicationKey> wrappedApplicationKeys) + TypedXmlSerializer xmlSerializer, List<WrappedApplicationKey> wrappedApplicationKeys) throws IOException { xmlSerializer.startTag(NAMESPACE, TAG_APPLICATION_KEYS); for (WrappedApplicationKey key : wrappedApplicationKeys) { @@ -98,15 +95,15 @@ public class KeyChainSnapshotSerializer { xmlSerializer.endTag(NAMESPACE, TAG_APPLICATION_KEYS); } - private static void writeApplicationKeyProperties( - XmlSerializer xmlSerializer, WrappedApplicationKey applicationKey) throws IOException { + private static void writeApplicationKeyProperties(TypedXmlSerializer xmlSerializer, + WrappedApplicationKey applicationKey) throws IOException { writePropertyTag(xmlSerializer, TAG_ALIAS, applicationKey.getAlias()); writePropertyTag(xmlSerializer, TAG_KEY_MATERIAL, applicationKey.getEncryptedKeyMaterial()); writePropertyTag(xmlSerializer, TAG_KEY_METADATA, applicationKey.getMetadata()); } private static void writeKeyChainProtectionParams( - XmlSerializer xmlSerializer, + TypedXmlSerializer xmlSerializer, List<KeyChainProtectionParams> keyChainProtectionParamsList) throws IOException { xmlSerializer.startTag(NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST); for (KeyChainProtectionParams keyChainProtectionParams : keyChainProtectionParamsList) { @@ -118,7 +115,7 @@ public class KeyChainSnapshotSerializer { } private static void writeKeyChainProtectionParamsProperties( - XmlSerializer xmlSerializer, KeyChainProtectionParams keyChainProtectionParams) + TypedXmlSerializer xmlSerializer, KeyChainProtectionParams keyChainProtectionParams) throws IOException { writePropertyTag(xmlSerializer, TAG_USER_SECRET_TYPE, keyChainProtectionParams.getUserSecretType()); @@ -132,7 +129,7 @@ public class KeyChainSnapshotSerializer { } private static void writeKeyDerivationParams( - XmlSerializer xmlSerializer, KeyDerivationParams keyDerivationParams) + TypedXmlSerializer xmlSerializer, KeyDerivationParams keyDerivationParams) throws IOException { xmlSerializer.startTag(NAMESPACE, TAG_KEY_DERIVATION_PARAMS); writeKeyDerivationParamsProperties( @@ -141,7 +138,7 @@ public class KeyChainSnapshotSerializer { } private static void writeKeyDerivationParamsProperties( - XmlSerializer xmlSerializer, KeyDerivationParams keyDerivationParams) + TypedXmlSerializer xmlSerializer, KeyDerivationParams keyDerivationParams) throws IOException { writePropertyTag(xmlSerializer, TAG_ALGORITHM, keyDerivationParams.getAlgorithm()); writePropertyTag(xmlSerializer, TAG_SALT, keyDerivationParams.getSalt()); @@ -150,7 +147,7 @@ public class KeyChainSnapshotSerializer { } private static void writeKeyChainSnapshotProperties( - XmlSerializer xmlSerializer, KeyChainSnapshot keyChainSnapshot) + TypedXmlSerializer xmlSerializer, KeyChainSnapshot keyChainSnapshot) throws IOException, CertificateEncodingException { writePropertyTag(xmlSerializer, TAG_SNAPSHOT_VERSION, @@ -165,7 +162,7 @@ public class KeyChainSnapshotSerializer { } private static void writePropertyTag( - XmlSerializer xmlSerializer, String propertyName, long propertyValue) + TypedXmlSerializer xmlSerializer, String propertyName, long propertyValue) throws IOException { xmlSerializer.startTag(NAMESPACE, propertyName); xmlSerializer.text(Long.toString(propertyValue)); @@ -173,7 +170,7 @@ public class KeyChainSnapshotSerializer { } private static void writePropertyTag( - XmlSerializer xmlSerializer, String propertyName, String propertyValue) + TypedXmlSerializer xmlSerializer, String propertyName, String propertyValue) throws IOException { xmlSerializer.startTag(NAMESPACE, propertyName); xmlSerializer.text(propertyValue); @@ -181,7 +178,7 @@ public class KeyChainSnapshotSerializer { } private static void writePropertyTag( - XmlSerializer xmlSerializer, String propertyName, @Nullable byte[] propertyValue) + TypedXmlSerializer xmlSerializer, String propertyName, @Nullable byte[] propertyValue) throws IOException { if (propertyValue == null) { return; @@ -192,7 +189,7 @@ public class KeyChainSnapshotSerializer { } private static void writePropertyTag( - XmlSerializer xmlSerializer, String propertyName, CertPath certPath) + TypedXmlSerializer xmlSerializer, String propertyName, CertPath certPath) throws IOException, CertificateEncodingException { writePropertyTag(xmlSerializer, propertyName, certPath.getEncoded(CERT_PATH_ENCODING)); } diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java index bdf0fb9cee59..8200ca0523be 100644 --- a/services/core/java/com/android/server/notification/ConditionProviders.java +++ b/services/core/java/com/android/server/notification/ConditionProviders.java @@ -34,6 +34,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; +import android.util.TypedXmlSerializer; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; @@ -110,7 +111,7 @@ public class ConditionProviders extends ManagedServices { } @Override - void writeDefaults(XmlSerializer out) throws IOException { + void writeDefaults(TypedXmlSerializer out) throws IOException { synchronized (mDefaultsLock) { String defaults = String.join(ENABLED_SERVICES_SEPARATOR, mDefaultPackages); out.attribute(null, ATT_DEFAULTS, defaults); diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index f2e3708051c4..a7ee27286a24 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -60,6 +60,8 @@ import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; @@ -443,7 +445,7 @@ abstract public class ManagedServices { } } - void writeDefaults(XmlSerializer out) throws IOException { + void writeDefaults(TypedXmlSerializer out) throws IOException { synchronized (mDefaultsLock) { List<String> componentStrings = new ArrayList<>(mDefaultComponents.size()); for (int i = 0; i < mDefaultComponents.size(); i++) { @@ -454,10 +456,10 @@ abstract public class ManagedServices { } } - public void writeXml(XmlSerializer out, boolean forBackup, int userId) throws IOException { + public void writeXml(TypedXmlSerializer out, boolean forBackup, int userId) throws IOException { out.startTag(null, getConfig().xmlTag); - out.attribute(null, ATT_VERSION, String.valueOf(DB_VERSION)); + out.attributeInt(null, ATT_VERSION, DB_VERSION); writeDefaults(out); @@ -485,8 +487,8 @@ abstract public class ManagedServices { : String.join(ENABLED_SERVICES_SEPARATOR, approved); out.startTag(null, TAG_MANAGED_SERVICES); out.attribute(null, ATT_APPROVED_LIST, allowedItems); - out.attribute(null, ATT_USER_ID, Integer.toString(approvedUserId)); - out.attribute(null, ATT_IS_PRIMARY, Boolean.toString(isPrimary)); + out.attributeInt(null, ATT_USER_ID, approvedUserId); + out.attributeBoolean(null, ATT_IS_PRIMARY, isPrimary); if (userSet != null) { String userSetItems = String.join(ENABLED_SERVICES_SEPARATOR, userSet); @@ -516,23 +518,23 @@ abstract public class ManagedServices { /** * Writes extra xml attributes to {@link #TAG_MANAGED_SERVICES} tag. */ - protected void writeExtraAttributes(XmlSerializer out, int userId) throws IOException {} + protected void writeExtraAttributes(TypedXmlSerializer out, int userId) throws IOException {} /** * Writes extra xml tags within the parent tag specified in {@link Config#xmlTag}. */ - protected void writeExtraXmlTags(XmlSerializer out) throws IOException {} + protected void writeExtraXmlTags(TypedXmlSerializer out) throws IOException {} /** * This is called to process tags other than {@link #TAG_MANAGED_SERVICES}. */ - protected void readExtraTag(String tag, XmlPullParser parser) throws IOException {} + protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException {} protected void migrateToXml() { loadAllowedComponentsFromSettings(); } - void readDefaults(XmlPullParser parser) { + void readDefaults(TypedXmlPullParser parser) { String defaultComponents = XmlUtils.readStringAttribute(parser, ATT_DEFAULTS); if (!TextUtils.isEmpty(defaultComponents)) { @@ -554,7 +556,7 @@ abstract public class ManagedServices { } public void readXml( - XmlPullParser parser, + TypedXmlPullParser parser, TriPredicate<String, Integer, String> allowedManagedServicePackages, boolean forRestore, int userId) @@ -577,9 +579,9 @@ abstract public class ManagedServices { final String approved = XmlUtils.readStringAttribute(parser, ATT_APPROVED_LIST); // Ignore parser's user id for restore. final int resolvedUserId = forRestore - ? userId : XmlUtils.readIntAttribute(parser, ATT_USER_ID, 0); + ? userId : parser.getAttributeInt(null, ATT_USER_ID, 0); final boolean isPrimary = - XmlUtils.readBooleanAttribute(parser, ATT_IS_PRIMARY, true); + parser.getAttributeBoolean(null, ATT_IS_PRIMARY, true); final String userSet = XmlUtils.readStringAttribute(parser, ATT_USER_SET); readExtraAttributes(tag, parser, resolvedUserId); if (allowedManagedServicePackages == null || allowedManagedServicePackages.test( @@ -631,7 +633,7 @@ abstract public class ManagedServices { /** * Read extra attributes in the {@link #TAG_MANAGED_SERVICES} tag. */ - protected void readExtraAttributes(String tag, XmlPullParser parser, int userId) + protected void readExtraAttributes(String tag, TypedXmlPullParser parser, int userId) throws IOException {} protected abstract String getRequiredPermission(); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index b1289dd0e39f..76b9c8619cfc 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -231,6 +231,8 @@ import android.util.Slog; import android.util.SparseArray; import android.util.StatsEvent; import android.util.Xml; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; @@ -743,8 +745,13 @@ public class NotificationManagerService extends SystemService { void readPolicyXml(InputStream stream, boolean forRestore, int userId) throws XmlPullParserException, NumberFormatException, IOException { - final XmlPullParser parser = Xml.newPullParser(); - parser.setInput(stream, StandardCharsets.UTF_8.name()); + final TypedXmlPullParser parser; + if (forRestore) { + parser = Xml.newFastPullParser(); + parser.setInput(stream, StandardCharsets.UTF_8.name()); + } else { + parser = Xml.resolvePullParser(stream); + } XmlUtils.beginDocument(parser, TAG_NOTIFICATION_POLICY); boolean migratedManagedServices = false; boolean ineligibleForManagedServices = forRestore && mUm.isManagedProfile(userId); @@ -781,9 +788,8 @@ public class NotificationManagerService extends SystemService { if (forRestore && userId != UserHandle.USER_SYSTEM) { continue; } - mLockScreenAllowSecureNotifications = - safeBoolean(parser.getAttributeValue(null, - LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE), true); + mLockScreenAllowSecureNotifications = parser.getAttributeBoolean(null, + LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE, true); } } @@ -856,11 +862,16 @@ public class NotificationManagerService extends SystemService { private void writePolicyXml(OutputStream stream, boolean forBackup, int userId) throws IOException { - final XmlSerializer out = new FastXmlSerializer(); - out.setOutput(stream, StandardCharsets.UTF_8.name()); + final TypedXmlSerializer out; + if (forBackup) { + out = Xml.newFastSerializer(); + out.setOutput(stream, StandardCharsets.UTF_8.name()); + } else { + out = Xml.resolveSerializer(stream); + } out.startDocument(null, true); out.startTag(null, TAG_NOTIFICATION_POLICY); - out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION)); + out.attributeInt(null, ATTR_VERSION, DB_VERSION); mZenModeHelper.writeXml(out, forBackup, null, userId); mPreferencesHelper.writeXml(out, forBackup, userId); mListeners.writeXml(out, forBackup, userId); @@ -8975,7 +8986,7 @@ public class NotificationManagerService extends SystemService { } @Override - protected void writeExtraXmlTags(XmlSerializer out) throws IOException { + protected void writeExtraXmlTags(TypedXmlSerializer out) throws IOException { synchronized (mLock) { out.startTag(null, TAG_ALLOWED_ADJUSTMENT_TYPES); out.attribute(null, ATT_TYPES, TextUtils.join(",", mAllowedAdjustments)); @@ -8984,7 +8995,7 @@ public class NotificationManagerService extends SystemService { } @Override - protected void readExtraTag(String tag, XmlPullParser parser) throws IOException { + protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException { if (TAG_ALLOWED_ADJUSTMENT_TYPES.equals(tag)) { final String types = XmlUtils.readStringAttribute(parser, ATT_TYPES); synchronized (mLock) { @@ -9104,9 +9115,9 @@ public class NotificationManagerService extends SystemService { } @Override - protected void readExtraAttributes(String tag, XmlPullParser parser, int userId) + protected void readExtraAttributes(String tag, TypedXmlPullParser parser, int userId) throws IOException { - boolean userSet = XmlUtils.readBooleanAttribute(parser, ATT_USER_SET, false); + boolean userSet = parser.getAttributeBoolean(null, ATT_USER_SET, false); setUserSet(userId, userSet); } @@ -10106,18 +10117,13 @@ public class NotificationManagerService extends SystemService { } } - private void writeSecureNotificationsPolicy(XmlSerializer out) throws IOException { + private void writeSecureNotificationsPolicy(TypedXmlSerializer out) throws IOException { out.startTag(null, LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_TAG); - out.attribute(null, LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE, - Boolean.toString(mLockScreenAllowSecureNotifications)); + out.attributeBoolean(null, LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE, + mLockScreenAllowSecureNotifications); out.endTag(null, LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_TAG); } - private static boolean safeBoolean(String val, boolean defValue) { - if (TextUtils.isEmpty(val)) return defValue; - return Boolean.parseBoolean(val); - } - /** * Shows a warning on logcat. Shows the toast only once per package. This is to avoid being too * aggressive and annoying the user. diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 2f990c62305f..1c0349d1b51f 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -56,6 +56,8 @@ import android.util.Pair; import android.util.Slog; import android.util.SparseBooleanArray; import android.util.StatsEvent; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; import com.android.internal.R; @@ -195,21 +197,15 @@ public class PreferencesHelper implements RankingConfig { syncChannelsBypassingDnd(mContext.getUserId()); } - public void readXml(XmlPullParser parser, boolean forRestore, int userId) + public void readXml(TypedXmlPullParser parser, boolean forRestore, int userId) throws XmlPullParserException, IOException { int type = parser.getEventType(); if (type != XmlPullParser.START_TAG) return; String tag = parser.getName(); if (!TAG_RANKING.equals(tag)) return; - boolean upgradeForBubbles = false; - if (parser.getAttributeCount() > 0) { - String attribute = parser.getAttributeName(0); - if (ATT_VERSION.equals(attribute)) { - int xmlVersion = Integer.parseInt(parser.getAttributeValue(0)); - upgradeForBubbles = xmlVersion == XML_VERSION_BUBBLES_UPGRADE; - } - } + final int xmlVersion = parser.getAttributeInt(null, ATT_VERSION, -1); + boolean upgradeForBubbles = xmlVersion == XML_VERSION_BUBBLES_UPGRADE; synchronized (mPackagePreferences) { while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { tag = parser.getName(); @@ -221,10 +217,10 @@ public class PreferencesHelper implements RankingConfig { if (forRestore && userId != UserHandle.USER_SYSTEM) { continue; } - mHideSilentStatusBarIcons = XmlUtils.readBooleanAttribute( - parser, ATT_HIDE_SILENT, DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS); + mHideSilentStatusBarIcons = parser.getAttributeBoolean(null, + ATT_HIDE_SILENT, DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS); } else if (TAG_PACKAGE.equals(tag)) { - int uid = XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID); + int uid = parser.getAttributeInt(null, ATT_UID, UNKNOWN_UID); String name = parser.getAttributeValue(null, ATT_NAME); if (!TextUtils.isEmpty(name)) { if (forRestore) { @@ -243,36 +239,36 @@ public class PreferencesHelper implements RankingConfig { } int bubblePref = hasSAWPermission ? BUBBLE_PREFERENCE_ALL - : XmlUtils.readIntAttribute(parser, ATT_ALLOW_BUBBLE, - DEFAULT_BUBBLE_PREFERENCE); + : parser.getAttributeInt( + null, ATT_ALLOW_BUBBLE, DEFAULT_BUBBLE_PREFERENCE); PackagePreferences r = getOrCreatePackagePreferencesLocked( name, userId, uid, - XmlUtils.readIntAttribute( - parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE), - XmlUtils.readIntAttribute(parser, ATT_PRIORITY, - DEFAULT_PRIORITY), - XmlUtils.readIntAttribute( - parser, ATT_VISIBILITY, DEFAULT_VISIBILITY), - XmlUtils.readBooleanAttribute( - parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE), + parser.getAttributeInt( + null, ATT_IMPORTANCE, DEFAULT_IMPORTANCE), + parser.getAttributeInt( + null, ATT_PRIORITY, DEFAULT_PRIORITY), + parser.getAttributeInt( + null, ATT_VISIBILITY, DEFAULT_VISIBILITY), + parser.getAttributeBoolean( + null, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE), bubblePref); - r.importance = XmlUtils.readIntAttribute( - parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE); - r.priority = XmlUtils.readIntAttribute( - parser, ATT_PRIORITY, DEFAULT_PRIORITY); - r.visibility = XmlUtils.readIntAttribute( - parser, ATT_VISIBILITY, DEFAULT_VISIBILITY); - r.showBadge = XmlUtils.readBooleanAttribute( - parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE); - r.lockedAppFields = XmlUtils.readIntAttribute(parser, - ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS); - r.hasSentInvalidMessage = XmlUtils.readBooleanAttribute( - parser, ATT_SENT_INVALID_MESSAGE, false); - r.hasSentValidMessage = XmlUtils.readBooleanAttribute( - parser, ATT_SENT_VALID_MESSAGE, false); - r.userDemotedMsgApp = XmlUtils.readBooleanAttribute( - parser, ATT_USER_DEMOTED_INVALID_MSG_APP, false); + r.importance = parser.getAttributeInt( + null, ATT_IMPORTANCE, DEFAULT_IMPORTANCE); + r.priority = parser.getAttributeInt( + null, ATT_PRIORITY, DEFAULT_PRIORITY); + r.visibility = parser.getAttributeInt( + null, ATT_VISIBILITY, DEFAULT_VISIBILITY); + r.showBadge = parser.getAttributeBoolean( + null, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE); + r.lockedAppFields = parser.getAttributeInt( + null, ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS); + r.hasSentInvalidMessage = parser.getAttributeBoolean( + null, ATT_SENT_INVALID_MESSAGE, false); + r.hasSentValidMessage = parser.getAttributeBoolean( + null, ATT_SENT_VALID_MESSAGE, false); + r.userDemotedMsgApp = parser.getAttributeBoolean( + null, ATT_USER_DEMOTED_INVALID_MSG_APP, false); final int innerDepth = parser.getDepth(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT @@ -307,8 +303,8 @@ public class PreferencesHelper implements RankingConfig { } String id = parser.getAttributeValue(null, ATT_ID); String channelName = parser.getAttributeValue(null, ATT_NAME); - int channelImportance = XmlUtils.readIntAttribute( - parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE); + int channelImportance = parser.getAttributeInt( + null, ATT_IMPORTANCE, DEFAULT_IMPORTANCE); if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) { NotificationChannel channel = new NotificationChannel(id, channelName, channelImportance); @@ -338,14 +334,13 @@ public class PreferencesHelper implements RankingConfig { // Delegate if (TAG_DELEGATE.equals(tagName)) { int delegateId = - XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID); + parser.getAttributeInt(null, ATT_UID, UNKNOWN_UID); String delegateName = XmlUtils.readStringAttribute(parser, ATT_NAME); - boolean delegateEnabled = XmlUtils.readBooleanAttribute( - parser, ATT_ENABLED, Delegate.DEFAULT_ENABLED); - boolean userAllowed = XmlUtils.readBooleanAttribute( - parser, ATT_USER_ALLOWED, - Delegate.DEFAULT_USER_ALLOWED); + boolean delegateEnabled = parser.getAttributeBoolean( + null, ATT_ENABLED, Delegate.DEFAULT_ENABLED); + boolean userAllowed = parser.getAttributeBoolean( + null, ATT_USER_ALLOWED, Delegate.DEFAULT_USER_ALLOWED); Delegate d = null; if (delegateId != UNKNOWN_UID && !TextUtils.isEmpty( delegateName)) { @@ -502,13 +497,13 @@ public class PreferencesHelper implements RankingConfig { return true; } - public void writeXml(XmlSerializer out, boolean forBackup, int userId) throws IOException { + public void writeXml(TypedXmlSerializer out, boolean forBackup, int userId) throws IOException { out.startTag(null, TAG_RANKING); - out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION)); + out.attributeInt(null, ATT_VERSION, XML_VERSION); if (mHideSilentStatusBarIcons != DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS && (!forBackup || userId == UserHandle.USER_SYSTEM)) { out.startTag(null, TAG_STATUS_ICONS); - out.attribute(null, ATT_HIDE_SILENT, String.valueOf(mHideSilentStatusBarIcons)); + out.attributeBoolean(null, ATT_HIDE_SILENT, mHideSilentStatusBarIcons); out.endTag(null, TAG_STATUS_ICONS); } @@ -536,42 +531,41 @@ public class PreferencesHelper implements RankingConfig { out.startTag(null, TAG_PACKAGE); out.attribute(null, ATT_NAME, r.pkg); if (r.importance != DEFAULT_IMPORTANCE) { - out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance)); + out.attributeInt(null, ATT_IMPORTANCE, r.importance); } if (r.priority != DEFAULT_PRIORITY) { - out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority)); + out.attributeInt(null, ATT_PRIORITY, r.priority); } if (r.visibility != DEFAULT_VISIBILITY) { - out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility)); + out.attributeInt(null, ATT_VISIBILITY, r.visibility); } if (r.bubblePreference != DEFAULT_BUBBLE_PREFERENCE) { - out.attribute(null, ATT_ALLOW_BUBBLE, Integer.toString(r.bubblePreference)); + out.attributeInt(null, ATT_ALLOW_BUBBLE, r.bubblePreference); } - out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge)); - out.attribute(null, ATT_APP_USER_LOCKED_FIELDS, - Integer.toString(r.lockedAppFields)); - out.attribute(null, ATT_SENT_INVALID_MESSAGE, - Boolean.toString(r.hasSentInvalidMessage)); - out.attribute(null, ATT_SENT_VALID_MESSAGE, - Boolean.toString(r.hasSentValidMessage)); - out.attribute(null, ATT_USER_DEMOTED_INVALID_MSG_APP, - Boolean.toString(r.userDemotedMsgApp)); + out.attributeBoolean(null, ATT_SHOW_BADGE, r.showBadge); + out.attributeInt(null, ATT_APP_USER_LOCKED_FIELDS, + r.lockedAppFields); + out.attributeBoolean(null, ATT_SENT_INVALID_MESSAGE, + r.hasSentInvalidMessage); + out.attributeBoolean(null, ATT_SENT_VALID_MESSAGE, + r.hasSentValidMessage); + out.attributeBoolean(null, ATT_USER_DEMOTED_INVALID_MSG_APP, + r.userDemotedMsgApp); if (!forBackup) { - out.attribute(null, ATT_UID, Integer.toString(r.uid)); + out.attributeInt(null, ATT_UID, r.uid); } if (r.delegate != null) { out.startTag(null, TAG_DELEGATE); out.attribute(null, ATT_NAME, r.delegate.mPkg); - out.attribute(null, ATT_UID, Integer.toString(r.delegate.mUid)); + out.attributeInt(null, ATT_UID, r.delegate.mUid); if (r.delegate.mEnabled != Delegate.DEFAULT_ENABLED) { - out.attribute(null, ATT_ENABLED, Boolean.toString(r.delegate.mEnabled)); + out.attributeBoolean(null, ATT_ENABLED, r.delegate.mEnabled); } if (r.delegate.mUserAllowed != Delegate.DEFAULT_USER_ALLOWED) { - out.attribute(null, ATT_USER_ALLOWED, - Boolean.toString(r.delegate.mUserAllowed)); + out.attributeBoolean(null, ATT_USER_ALLOWED, r.delegate.mUserAllowed); } out.endTag(null, TAG_DELEGATE); } diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java index f7d69fdc09d2..b5ca2abf8afe 100644 --- a/services/core/java/com/android/server/notification/SnoozeHelper.java +++ b/services/core/java/com/android/server/notification/SnoozeHelper.java @@ -32,6 +32,8 @@ import android.util.ArrayMap; import android.util.IntArray; import android.util.Log; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; @@ -57,7 +59,7 @@ import java.util.Set; * NotificationManagerService helper for handling snoozed notifications. */ public class SnoozeHelper { - public static final String XML_SNOOZED_NOTIFICATION_VERSION = "1"; + public static final int XML_SNOOZED_NOTIFICATION_VERSION = 1; protected static final String XML_TAG_NAME = "snoozed-notifications"; @@ -547,7 +549,7 @@ public class SnoozeHelper { } } - protected void writeXml(XmlSerializer out) throws IOException { + protected void writeXml(TypedXmlSerializer out) throws IOException { synchronized (mLock) { final long currentTime = System.currentTimeMillis(); out.startTag(null, XML_TAG_NAME); @@ -573,7 +575,7 @@ public class SnoozeHelper { void insert(T t) throws IOException; } - private <T> void writeXml(XmlSerializer out, + private <T> void writeXml(TypedXmlSerializer out, ArrayMap<String, ArrayMap<String, T>> targets, String tag, Inserter<T> attributeInserter) throws IOException { @@ -596,21 +598,18 @@ public class SnoozeHelper { attributeInserter.insert(value); - out.attribute(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL, + out.attributeInt(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL, XML_SNOOZED_NOTIFICATION_VERSION); out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, key); - - out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, pkg); - out.attribute(null, XML_SNOOZED_NOTIFICATION_USER_ID, - String.valueOf(userId)); + out.attributeInt(null, XML_SNOOZED_NOTIFICATION_USER_ID, userId); out.endTag(null, tag); } } } - protected void readXml(XmlPullParser parser, long currentTime) + protected void readXml(TypedXmlPullParser parser, long currentTime) throws XmlPullParserException, IOException { int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { @@ -622,16 +621,16 @@ public class SnoozeHelper { if (type == XmlPullParser.START_TAG && (XML_SNOOZED_NOTIFICATION.equals(tag) || tag.equals(XML_SNOOZED_NOTIFICATION_CONTEXT)) - && parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL) - .equals(XML_SNOOZED_NOTIFICATION_VERSION)) { + && parser.getAttributeInt(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL, -1) + == XML_SNOOZED_NOTIFICATION_VERSION) { try { final String key = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_KEY); final String pkg = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_PKG); - final int userId = XmlUtils.readIntAttribute( - parser, XML_SNOOZED_NOTIFICATION_USER_ID, UserHandle.USER_ALL); + final int userId = parser.getAttributeInt( + null, XML_SNOOZED_NOTIFICATION_USER_ID, UserHandle.USER_ALL); if (tag.equals(XML_SNOOZED_NOTIFICATION)) { - final Long time = XmlUtils.readLongAttribute( - parser, XML_SNOOZED_NOTIFICATION_TIME, 0); + final Long time = parser.getAttributeLong( + null, XML_SNOOZED_NOTIFICATION_TIME, 0); if (time > currentTime) { //only read new stuff synchronized (mLock) { storeRecordLocked( diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 13cd6e547629..94f46ba6bc60 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -72,6 +72,8 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.StatsEvent; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; import com.android.internal.R; @@ -702,7 +704,7 @@ public class ZenModeHelper { } } - public void readXml(XmlPullParser parser, boolean forRestore, int userId) + public void readXml(TypedXmlPullParser parser, boolean forRestore, int userId) throws XmlPullParserException, IOException { ZenModeConfig config = ZenModeConfig.readXml(parser); String reason = "readXml"; @@ -761,7 +763,7 @@ public class ZenModeHelper { } } - public void writeXml(XmlSerializer out, boolean forBackup, Integer version, int userId) + public void writeXml(TypedXmlSerializer out, boolean forBackup, Integer version, int userId) throws IOException { synchronized (mConfigs) { final int n = mConfigs.size(); diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java index 8a2d823cf7e9..0613dff31da5 100644 --- a/services/core/java/com/android/server/om/OverlayManagerSettings.java +++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java @@ -411,7 +411,7 @@ final class OverlayManagerSettings { table.clear(); final TypedXmlPullParser parser = Xml.resolvePullParser(is); XmlUtils.beginDocument(parser, TAG_OVERLAYS); - int version = XmlUtils.readIntAttribute(parser, ATTR_VERSION); + int version = parser.getAttributeInt(null, ATTR_VERSION); if (version != CURRENT_VERSION) { upgrade(version); } @@ -445,19 +445,19 @@ final class OverlayManagerSettings { } } - private static SettingsItem restoreRow(@NonNull final XmlPullParser parser, final int depth) - throws IOException { + private static SettingsItem restoreRow(@NonNull final TypedXmlPullParser parser, + final int depth) throws IOException, XmlPullParserException { final String packageName = XmlUtils.readStringAttribute(parser, ATTR_PACKAGE_NAME); - final int userId = XmlUtils.readIntAttribute(parser, ATTR_USER_ID); + final int userId = parser.getAttributeInt(null, ATTR_USER_ID); final String targetPackageName = XmlUtils.readStringAttribute(parser, ATTR_TARGET_PACKAGE_NAME); final String targetOverlayableName = XmlUtils.readStringAttribute(parser, ATTR_TARGET_OVERLAYABLE_NAME); final String baseCodePath = XmlUtils.readStringAttribute(parser, ATTR_BASE_CODE_PATH); - final int state = XmlUtils.readIntAttribute(parser, ATTR_STATE); - final boolean isEnabled = XmlUtils.readBooleanAttribute(parser, ATTR_IS_ENABLED); - final boolean isStatic = XmlUtils.readBooleanAttribute(parser, ATTR_IS_STATIC); - final int priority = XmlUtils.readIntAttribute(parser, ATTR_PRIORITY); + final int state = parser.getAttributeInt(null, ATTR_STATE); + final boolean isEnabled = parser.getAttributeBoolean(null, ATTR_IS_ENABLED, false); + final boolean isStatic = parser.getAttributeBoolean(null, ATTR_IS_STATIC, false); + final int priority = parser.getAttributeInt(null, ATTR_PRIORITY); final String category = XmlUtils.readStringAttribute(parser, ATTR_CATEGORY); return new SettingsItem(packageName, userId, targetPackageName, targetOverlayableName, @@ -470,7 +470,7 @@ final class OverlayManagerSettings { xml.startDocument(null, true); xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); xml.startTag(null, TAG_OVERLAYS); - XmlUtils.writeIntAttribute(xml, ATTR_VERSION, CURRENT_VERSION); + xml.attributeInt(null, ATTR_VERSION, CURRENT_VERSION); final int n = table.size(); for (int i = 0; i < n; i++) { @@ -485,15 +485,15 @@ final class OverlayManagerSettings { @NonNull final SettingsItem item) throws IOException { xml.startTag(null, TAG_ITEM); XmlUtils.writeStringAttribute(xml, ATTR_PACKAGE_NAME, item.mPackageName); - XmlUtils.writeIntAttribute(xml, ATTR_USER_ID, item.mUserId); + xml.attributeInt(null, ATTR_USER_ID, item.mUserId); XmlUtils.writeStringAttribute(xml, ATTR_TARGET_PACKAGE_NAME, item.mTargetPackageName); XmlUtils.writeStringAttribute(xml, ATTR_TARGET_OVERLAYABLE_NAME, item.mTargetOverlayableName); XmlUtils.writeStringAttribute(xml, ATTR_BASE_CODE_PATH, item.mBaseCodePath); - XmlUtils.writeIntAttribute(xml, ATTR_STATE, item.mState); + xml.attributeInt(null, ATTR_STATE, item.mState); XmlUtils.writeBooleanAttribute(xml, ATTR_IS_ENABLED, item.mIsEnabled); XmlUtils.writeBooleanAttribute(xml, ATTR_IS_STATIC, !item.mIsMutable); - XmlUtils.writeIntAttribute(xml, ATTR_PRIORITY, item.mPriority); + xml.attributeInt(null, ATTR_PRIORITY, item.mPriority); XmlUtils.writeStringAttribute(xml, ATTR_CATEGORY, item.mCategory); xml.endTag(null, TAG_ITEM); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index db5eb844f1b4..e95b1a2b9d7a 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -21,7 +21,6 @@ import static android.Manifest.permission.MANAGE_DEVICE_ADMINS; import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS; import static android.Manifest.permission.REQUEST_DELETE_PACKAGES; import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS; -import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_DEFAULT; import static android.app.AppOpsManager.MODE_IGNORED; import static android.content.Intent.ACTION_MAIN; @@ -44,7 +43,6 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT; import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET; -import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER; import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS; import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_PERMISSION_GROUP; import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE; @@ -343,7 +341,6 @@ import com.android.internal.content.PackageHelper; import com.android.internal.content.om.OverlayConfig; import com.android.internal.logging.MetricsLogger; import com.android.internal.os.SomeArgs; -import com.android.internal.os.Zygote; import com.android.internal.telephony.CarrierAppUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.ConcurrentUtils; @@ -2172,7 +2169,7 @@ public class PackageManagerService extends IPackageManager.Stub private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions, boolean killApp, boolean virtualPreload, - String[] grantedPermissions, List<String> whitelistedRestrictedPermissions, + String[] grantedPermissions, List<String> allowlistedRestrictedPermissions, int autoRevokePermissionsMode, boolean launchedForRestore, String installerPackage, IPackageInstallObserver2 installObserver, int dataLoaderType) { @@ -2205,31 +2202,22 @@ public class PackageManagerService extends IPackageManager.Stub res.removedInfo.sendPackageRemovedBroadcasts(killApp, false /*removedBySystem*/); } - // Allowlist any restricted permissions first as some may be runtime - // that the installer requested to be granted at install time. - if (whitelistedRestrictedPermissions != null - && !whitelistedRestrictedPermissions.isEmpty()) { - mPermissionManager.setAllowlistedRestrictedPermissions(res.pkg, - whitelistedRestrictedPermissions, FLAG_PERMISSION_WHITELIST_INSTALLER, - res.newUsers); + final List<String> grantedPermissionsList; + if (grantPermissions) { + if (grantedPermissions != null) { + grantedPermissionsList = Arrays.asList(grantedPermissions); + } else { + grantedPermissionsList = res.pkg.getRequestedPermissions(); + } + } else { + grantedPermissionsList = Collections.emptyList(); } - - if (autoRevokePermissionsMode == MODE_ALLOWED - || autoRevokePermissionsMode == MODE_IGNORED) { - mPermissionManager.setAutoRevokeExempted(res.pkg, - autoRevokePermissionsMode == MODE_IGNORED, res.newUsers); + if (allowlistedRestrictedPermissions == null) { + allowlistedRestrictedPermissions = Collections.emptyList(); } - - // Now that we successfully installed the package, grant runtime - // permissions if requested before broadcasting the install. Also - // for legacy apps in permission review mode we clear the permission - // review flag which is used to emulate runtime permissions for - // legacy apps. - if (grantPermissions) { - final int callingUid = Binder.getCallingUid(); - mPermissionManager.grantRequestedRuntimePermissions(res.pkg, - grantedPermissions != null ? Arrays.asList(grantedPermissions) : null, - res.newUsers); + for (final int userId : res.newUsers) { + mPermissionManager.onPackageInstalled(res.pkg, grantedPermissionsList, + allowlistedRestrictedPermissions, autoRevokePermissionsMode, userId); } final String installerPackageName = @@ -4105,10 +4093,7 @@ public class PackageManagerService extends IPackageManager.Stub // feature flags should cause us to invalidate any caches. final String cacheName = FORCE_PACKAGE_PARSED_CACHE_ENABLED ? "debug" : injector.getSystemWrapper().digestOfProperties( - "ro.build.fingerprint", - StorageManager.PROP_ISOLATED_STORAGE, - StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT - ); + "ro.build.fingerprint"); // Reconcile cache directories, keeping only what we'd actually use. for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) { @@ -13611,11 +13596,12 @@ public class PackageManagerService extends IPackageManager.Stub int installExistingPackageAsUser(@Nullable String packageName, @UserIdInt int userId, @PackageManager.InstallFlags int installFlags, @PackageManager.InstallReason int installReason, - @Nullable List<String> whiteListedPermissions, @Nullable IntentSender intentSender) { + @Nullable List<String> allowlistedRestrictedPermissions, + @Nullable IntentSender intentSender) { if (DEBUG_INSTALL) { Log.v(TAG, "installExistingPackageAsUser package=" + packageName + " userId=" + userId + " installFlags=" + installFlags + " installReason=" + installReason - + " whiteListedPermissions=" + whiteListedPermissions); + + " allowlistedRestrictedPermissions=" + allowlistedRestrictedPermissions); } final int callingUid = Binder.getCallingUid(); @@ -13681,11 +13667,12 @@ public class PackageManagerService extends IPackageManager.Stub if (pkgSetting.pkg != null) { if ((installFlags & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0) { - whiteListedPermissions = pkgSetting.pkg.getRequestedPermissions(); + allowlistedRestrictedPermissions = pkgSetting.pkg.getRequestedPermissions(); + } else if (allowlistedRestrictedPermissions == null) { + allowlistedRestrictedPermissions = Collections.emptyList(); } - mPermissionManager.setAllowlistedRestrictedPermissions(pkgSetting.pkg, - whiteListedPermissions, FLAG_PERMISSION_WHITELIST_INSTALLER, - new int[] { userId }); + mPermissionManager.onPackageInstalled(pkgSetting.pkg, Collections.emptyList(), + allowlistedRestrictedPermissions, MODE_DEFAULT, userId); } if (pkgSetting.pkg != null) { @@ -22221,22 +22208,6 @@ public class PackageManagerService extends IPackageManager.Stub mInstallerService.systemReady(); mPackageDexOptimizer.systemReady(); - mInjector.getLocalService(StorageManagerInternal.class).addExternalStoragePolicy( - new StorageManagerInternal.ExternalStorageMountPolicy() { - @Override - public int getMountMode(int uid, String packageName) { - if (Process.isIsolated(uid)) { - return Zygote.MOUNT_EXTERNAL_NONE; - } - return Zygote.MOUNT_EXTERNAL_DEFAULT; - } - - @Override - public boolean hasExternalStorage(int uid, String packageName) { - return true; - } - }); - // Now that we're mostly running, clean up stale users and apps mUserManager.reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL); reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL); diff --git a/services/core/java/com/android/server/pm/PackageProperty.java b/services/core/java/com/android/server/pm/PackageProperty.java index d18a02d9f315..ee9ed3b90599 100644 --- a/services/core/java/com/android/server/pm/PackageProperty.java +++ b/services/core/java/com/android/server/pm/PackageProperty.java @@ -274,7 +274,7 @@ public class PackageProperty { private Property getApplicationProperty(String propertyName, String packageName) { final ArrayMap<String, ArrayList<Property>> packagePropertyMap = - mApplicationProperties.get(propertyName); + mApplicationProperties != null ? mApplicationProperties.get(propertyName) : null; if (packagePropertyMap == null) { return null; } diff --git a/services/core/java/com/android/server/pm/ShareTargetInfo.java b/services/core/java/com/android/server/pm/ShareTargetInfo.java index fdfee773ea74..660874e7b3c3 100644 --- a/services/core/java/com/android/server/pm/ShareTargetInfo.java +++ b/services/core/java/com/android/server/pm/ShareTargetInfo.java @@ -17,10 +17,11 @@ package com.android.server.pm; import android.annotation.NonNull; import android.text.TextUtils; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; @@ -123,7 +124,7 @@ class ShareTargetInfo { return strBuilder.toString(); } - void saveToXml(@NonNull XmlSerializer out) throws IOException { + void saveToXml(@NonNull TypedXmlSerializer out) throws IOException { out.startTag(null, TAG_SHARE_TARGET); ShortcutService.writeAttr(out, ATTR_TARGET_CLASS, mTargetClass); @@ -149,7 +150,7 @@ class ShareTargetInfo { out.endTag(null, TAG_SHARE_TARGET); } - static ShareTargetInfo loadFromXml(XmlPullParser parser) + static ShareTargetInfo loadFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final String targetClass = ShortcutService.parseStringAttribute(parser, ATTR_TARGET_CLASS); final ArrayList<ShareTargetInfo.TargetData> targetData = new ArrayList<>(); @@ -178,7 +179,7 @@ class ShareTargetInfo { targetClass, categories.toArray(new String[categories.size()])); } - private static ShareTargetInfo.TargetData parseTargetData(XmlPullParser parser) { + private static ShareTargetInfo.TargetData parseTargetData(TypedXmlPullParser parser) { final String scheme = ShortcutService.parseStringAttribute(parser, ATTR_SCHEME); final String host = ShortcutService.parseStringAttribute(parser, ATTR_HOST); final String port = ShortcutService.parseStringAttribute(parser, ATTR_PORT); diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java index 0ebe596111f2..2960bc9a3790 100644 --- a/services/core/java/com/android/server/pm/ShortcutLauncher.java +++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java @@ -24,6 +24,8 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; @@ -36,15 +38,12 @@ import org.json.JSONException; import org.json.JSONObject; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; -import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -225,7 +224,7 @@ class ShortcutLauncher extends ShortcutPackageItem { * Persist. */ @Override - public void saveToXml(XmlSerializer out, boolean forBackup) + public void saveToXml(TypedXmlSerializer out, boolean forBackup) throws IOException { if (forBackup && !getPackageInfo().isBackupAllowed()) { // If an launcher app doesn't support backup&restore, then nothing to do. @@ -278,11 +277,8 @@ class ShortcutLauncher extends ShortcutPackageItem { } try { - final BufferedInputStream bis = new BufferedInputStream(in); - ShortcutLauncher ret = null; - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(bis, StandardCharsets.UTF_8.name()); + TypedXmlPullParser parser = Xml.resolvePullParser(in); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { @@ -313,7 +309,7 @@ class ShortcutLauncher extends ShortcutPackageItem { /** * Load. */ - public static ShortcutLauncher loadFromXml(XmlPullParser parser, ShortcutUser shortcutUser, + public static ShortcutLauncher loadFromXml(TypedXmlPullParser parser, ShortcutUser shortcutUser, int ownerUserId, boolean fromBackup) throws IOException, XmlPullParserException { final String launcherPackageName = ShortcutService.parseStringAttribute(parser, ATTR_PACKAGE_NAME); diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index f6c60ad6e2cf..0ac0c8d95423 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -35,6 +35,8 @@ import android.util.ArraySet; import android.util.AtomicFile; import android.util.Log; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; @@ -52,15 +54,12 @@ import org.json.JSONException; import org.json.JSONObject; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; -import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -1571,7 +1570,7 @@ class ShortcutPackage extends ShortcutPackageItem { } @Override - public void saveToXml(@NonNull XmlSerializer out, boolean forBackup) + public void saveToXml(@NonNull TypedXmlSerializer out, boolean forBackup) throws IOException, XmlPullParserException { final int size = mShortcuts.size(); final int shareTargetSize = mShareTargets.size(); @@ -1601,7 +1600,7 @@ class ShortcutPackage extends ShortcutPackageItem { out.endTag(null, TAG_ROOT); } - private void saveShortcut(XmlSerializer out, ShortcutInfo si, boolean forBackup, + private void saveShortcut(TypedXmlSerializer out, ShortcutInfo si, boolean forBackup, boolean appSupportsBackup) throws IOException, XmlPullParserException { @@ -1734,11 +1733,8 @@ class ShortcutPackage extends ShortcutPackageItem { } try { - final BufferedInputStream bis = new BufferedInputStream(in); - ShortcutPackage ret = null; - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(bis, StandardCharsets.UTF_8.name()); + TypedXmlPullParser parser = Xml.resolvePullParser(in); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { @@ -1767,7 +1763,7 @@ class ShortcutPackage extends ShortcutPackageItem { } public static ShortcutPackage loadFromXml(ShortcutService s, ShortcutUser shortcutUser, - XmlPullParser parser, boolean fromBackup) + TypedXmlPullParser parser, boolean fromBackup) throws IOException, XmlPullParserException { final String packageName = ShortcutService.parseStringAttribute(parser, @@ -1814,7 +1810,7 @@ class ShortcutPackage extends ShortcutPackageItem { return ret; } - private static ShortcutInfo parseShortcut(XmlPullParser parser, String packageName, + private static ShortcutInfo parseShortcut(TypedXmlPullParser parser, String packageName, @UserIdInt int userId, boolean fromBackup) throws IOException, XmlPullParserException { String id; @@ -1948,7 +1944,7 @@ class ShortcutPackage extends ShortcutPackageItem { disabledReason, persons.toArray(new Person[persons.size()]), locusId); } - private static Intent parseIntent(XmlPullParser parser) + private static Intent parseIntent(TypedXmlPullParser parser) throws IOException, XmlPullParserException { Intent intent = ShortcutService.parseIntentAttribute(parser, @@ -1978,7 +1974,7 @@ class ShortcutPackage extends ShortcutPackageItem { return intent; } - private static Person parsePerson(XmlPullParser parser) + private static Person parsePerson(TypedXmlPullParser parser) throws IOException, XmlPullParserException { CharSequence name = ShortcutService.parseStringAttribute(parser, ATTR_PERSON_NAME); String uri = ShortcutService.parseStringAttribute(parser, ATTR_PERSON_URI); diff --git a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java index 8c7871ffaf96..fce66108bede 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java +++ b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java @@ -23,6 +23,8 @@ import android.content.pm.ShortcutInfo; import android.content.pm.Signature; import android.content.pm.SigningInfo; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; @@ -32,7 +34,6 @@ import libcore.util.HexEncoding; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.io.PrintWriter; @@ -205,7 +206,7 @@ class ShortcutPackageInfo { mSigHashes = BackupUtils.hashSignatureArray(signatures); } - public void saveToXml(ShortcutService s, XmlSerializer out, boolean forBackup) + public void saveToXml(ShortcutService s, TypedXmlSerializer out, boolean forBackup) throws IOException { if (forBackup && !mBackupAllowedInitialized) { s.wtf("Backup happened before mBackupAllowed is initialized."); @@ -236,7 +237,7 @@ class ShortcutPackageInfo { out.endTag(null, TAG_ROOT); } - public void loadFromXml(XmlPullParser parser, boolean fromBackup) + public void loadFromXml(TypedXmlPullParser parser, boolean fromBackup) throws IOException, XmlPullParserException { // Don't use the version code from the backup file. final long versionCode = ShortcutService.parseLongAttribute(parser, ATTR_VERSION, diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java index 801c6cbb8f46..829133c9854a 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java +++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java @@ -20,16 +20,15 @@ import android.content.pm.PackageInfo; import android.content.pm.ShortcutInfo; import android.util.AtomicFile; import android.util.Slog; +import android.util.TypedXmlSerializer; +import android.util.Xml; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; import org.json.JSONException; import org.json.JSONObject; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; -import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -146,7 +145,7 @@ abstract class ShortcutPackageItem { protected abstract void onRestored(int restoreBlockReason); - public abstract void saveToXml(@NonNull XmlSerializer out, boolean forBackup) + public abstract void saveToXml(@NonNull TypedXmlSerializer out, boolean forBackup) throws IOException, XmlPullParserException; public void saveToFile(File path, boolean forBackup) { @@ -154,18 +153,21 @@ abstract class ShortcutPackageItem { FileOutputStream os = null; try { os = file.startWrite(); - final BufferedOutputStream bos = new BufferedOutputStream(os); // Write to XML - XmlSerializer itemOut = new FastXmlSerializer(); - itemOut.setOutput(bos, StandardCharsets.UTF_8.name()); + final TypedXmlSerializer itemOut; + if (forBackup) { + itemOut = Xml.newFastSerializer(); + itemOut.setOutput(os, StandardCharsets.UTF_8.name()); + } else { + itemOut = Xml.resolveSerializer(os); + } itemOut.startDocument(null, true); saveToXml(itemOut, forBackup); itemOut.endDocument(); - bos.flush(); os.flush(); file.finishWrite(os); } catch (XmlPullParserException | IOException e) { diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 2d771820865d..c68fe81d0565 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -95,6 +95,8 @@ import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.util.SparseLongArray; import android.util.TypedValue; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import android.view.IWindowManager; @@ -104,7 +106,6 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.os.BackgroundThread; import com.android.internal.util.CollectionUtils; import com.android.internal.util.DumpUtils; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; import com.android.internal.util.StatLogger; import com.android.server.LocalServices; @@ -119,10 +120,7 @@ import org.json.JSONException; import org.json.JSONObject; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -799,31 +797,31 @@ public class ShortcutService extends IShortcutService.Stub { // === Persisting === @Nullable - static String parseStringAttribute(XmlPullParser parser, String attribute) { + static String parseStringAttribute(TypedXmlPullParser parser, String attribute) { return parser.getAttributeValue(null, attribute); } - static boolean parseBooleanAttribute(XmlPullParser parser, String attribute) { + static boolean parseBooleanAttribute(TypedXmlPullParser parser, String attribute) { return parseLongAttribute(parser, attribute) == 1; } - static boolean parseBooleanAttribute(XmlPullParser parser, String attribute, boolean def) { + static boolean parseBooleanAttribute(TypedXmlPullParser parser, String attribute, boolean def) { return parseLongAttribute(parser, attribute, (def ? 1 : 0)) == 1; } - static int parseIntAttribute(XmlPullParser parser, String attribute) { + static int parseIntAttribute(TypedXmlPullParser parser, String attribute) { return (int) parseLongAttribute(parser, attribute); } - static int parseIntAttribute(XmlPullParser parser, String attribute, int def) { + static int parseIntAttribute(TypedXmlPullParser parser, String attribute, int def) { return (int) parseLongAttribute(parser, attribute, def); } - static long parseLongAttribute(XmlPullParser parser, String attribute) { + static long parseLongAttribute(TypedXmlPullParser parser, String attribute) { return parseLongAttribute(parser, attribute, 0); } - static long parseLongAttribute(XmlPullParser parser, String attribute, long def) { + static long parseLongAttribute(TypedXmlPullParser parser, String attribute, long def) { final String value = parseStringAttribute(parser, attribute); if (TextUtils.isEmpty(value)) { return def; @@ -837,7 +835,7 @@ public class ShortcutService extends IShortcutService.Stub { } @Nullable - static ComponentName parseComponentNameAttribute(XmlPullParser parser, String attribute) { + static ComponentName parseComponentNameAttribute(TypedXmlPullParser parser, String attribute) { final String value = parseStringAttribute(parser, attribute); if (TextUtils.isEmpty(value)) { return null; @@ -846,7 +844,7 @@ public class ShortcutService extends IShortcutService.Stub { } @Nullable - static Intent parseIntentAttributeNoDefault(XmlPullParser parser, String attribute) { + static Intent parseIntentAttributeNoDefault(TypedXmlPullParser parser, String attribute) { final String value = parseStringAttribute(parser, attribute); Intent parsed = null; if (!TextUtils.isEmpty(value)) { @@ -860,7 +858,7 @@ public class ShortcutService extends IShortcutService.Stub { } @Nullable - static Intent parseIntentAttribute(XmlPullParser parser, String attribute) { + static Intent parseIntentAttribute(TypedXmlPullParser parser, String attribute) { Intent parsed = parseIntentAttributeNoDefault(parser, attribute); if (parsed == null) { // Default intent. @@ -869,7 +867,7 @@ public class ShortcutService extends IShortcutService.Stub { return parsed; } - static void writeTagValue(XmlSerializer out, String tag, String value) throws IOException { + static void writeTagValue(TypedXmlSerializer out, String tag, String value) throws IOException { if (TextUtils.isEmpty(value)) return; out.startTag(null, tag); @@ -877,16 +875,17 @@ public class ShortcutService extends IShortcutService.Stub { out.endTag(null, tag); } - static void writeTagValue(XmlSerializer out, String tag, long value) throws IOException { + static void writeTagValue(TypedXmlSerializer out, String tag, long value) throws IOException { writeTagValue(out, tag, Long.toString(value)); } - static void writeTagValue(XmlSerializer out, String tag, ComponentName name) throws IOException { + static void writeTagValue(TypedXmlSerializer out, String tag, ComponentName name) + throws IOException { if (name == null) return; writeTagValue(out, tag, name.flattenToString()); } - static void writeTagExtra(XmlSerializer out, String tag, PersistableBundle bundle) + static void writeTagExtra(TypedXmlSerializer out, String tag, PersistableBundle bundle) throws IOException, XmlPullParserException { if (bundle == null) return; @@ -895,17 +894,18 @@ public class ShortcutService extends IShortcutService.Stub { out.endTag(null, tag); } - static void writeAttr(XmlSerializer out, String name, CharSequence value) throws IOException { + static void writeAttr(TypedXmlSerializer out, String name, CharSequence value) + throws IOException { if (TextUtils.isEmpty(value)) return; out.attribute(null, name, value.toString()); } - static void writeAttr(XmlSerializer out, String name, long value) throws IOException { + static void writeAttr(TypedXmlSerializer out, String name, long value) throws IOException { writeAttr(out, name, String.valueOf(value)); } - static void writeAttr(XmlSerializer out, String name, boolean value) throws IOException { + static void writeAttr(TypedXmlSerializer out, String name, boolean value) throws IOException { if (value) { writeAttr(out, name, "1"); } else { @@ -913,12 +913,13 @@ public class ShortcutService extends IShortcutService.Stub { } } - static void writeAttr(XmlSerializer out, String name, ComponentName comp) throws IOException { + static void writeAttr(TypedXmlSerializer out, String name, ComponentName comp) + throws IOException { if (comp == null) return; writeAttr(out, name, comp.flattenToString()); } - static void writeAttr(XmlSerializer out, String name, Intent intent) throws IOException { + static void writeAttr(TypedXmlSerializer out, String name, Intent intent) throws IOException { if (intent == null) return; writeAttr(out, name, intent.toUri(/* flags =*/ 0)); @@ -937,8 +938,7 @@ public class ShortcutService extends IShortcutService.Stub { outs = file.startWrite(); // Write to XML - XmlSerializer out = new FastXmlSerializer(); - out.setOutput(outs, StandardCharsets.UTF_8.name()); + TypedXmlSerializer out = Xml.resolveSerializer(outs); out.startDocument(null, true); out.startTag(null, TAG_ROOT); @@ -966,8 +966,7 @@ public class ShortcutService extends IShortcutService.Stub { Slog.d(TAG, "Loading from " + file.getBaseFile()); } try (FileInputStream in = file.openRead()) { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(in, StandardCharsets.UTF_8.name()); + TypedXmlPullParser parser = Xml.resolvePullParser(in); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { @@ -1043,18 +1042,20 @@ public class ShortcutService extends IShortcutService.Stub { private void saveUserInternalLocked(@UserIdInt int userId, OutputStream os, boolean forBackup) throws IOException, XmlPullParserException { - final BufferedOutputStream bos = new BufferedOutputStream(os); - // Write to XML - XmlSerializer out = new FastXmlSerializer(); - out.setOutput(bos, StandardCharsets.UTF_8.name()); + final TypedXmlSerializer out; + if (forBackup) { + out = Xml.newFastSerializer(); + out.setOutput(os, StandardCharsets.UTF_8.name()); + } else { + out = Xml.resolveSerializer(os); + } out.startDocument(null, true); getUserShortcutsLocked(userId).saveToXml(out, forBackup); out.endDocument(); - bos.flush(); os.flush(); } @@ -1098,11 +1099,14 @@ public class ShortcutService extends IShortcutService.Stub { boolean fromBackup) throws XmlPullParserException, IOException, InvalidFileFormatException { - final BufferedInputStream bis = new BufferedInputStream(is); - ShortcutUser ret = null; - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(bis, StandardCharsets.UTF_8.name()); + TypedXmlPullParser parser; + if (fromBackup) { + parser = Xml.newFastPullParser(); + parser.setInput(is, StandardCharsets.UTF_8.name()); + } else { + parser = Xml.resolvePullParser(is); + } int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java index 5c1d8fb142e9..3e3aa677912b 100644 --- a/services/core/java/com/android/server/pm/ShortcutUser.java +++ b/services/core/java/com/android/server/pm/ShortcutUser.java @@ -26,6 +26,8 @@ import android.text.format.Formatter; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; @@ -38,7 +40,6 @@ import org.json.JSONException; import org.json.JSONObject; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.IOException; @@ -329,7 +330,7 @@ class ShortcutUser { }); } - public void saveToXml(XmlSerializer out, boolean forBackup) + public void saveToXml(TypedXmlSerializer out, boolean forBackup) throws IOException, XmlPullParserException { out.startTag(null, TAG_ROOT); @@ -371,7 +372,7 @@ class ShortcutUser { out.endTag(null, TAG_ROOT); } - private void saveShortcutPackageItem(XmlSerializer out, ShortcutPackageItem spi, + private void saveShortcutPackageItem(TypedXmlSerializer out, ShortcutPackageItem spi, boolean forBackup) throws IOException, XmlPullParserException { if (forBackup) { if (spi.getPackageUserId() != spi.getOwnerUserId()) { @@ -408,7 +409,7 @@ class ShortcutUser { return new File(path, fileName); } - public static ShortcutUser loadFromXml(ShortcutService s, XmlPullParser parser, int userId, + public static ShortcutUser loadFromXml(ShortcutService s, TypedXmlPullParser parser, int userId, boolean fromBackup) throws IOException, XmlPullParserException, InvalidFileFormatException { final ShortcutUser ret = new ShortcutUser(s, userId); boolean readShortcutItems = false; diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index c51e75c716cc..cc814bcc7760 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -119,7 +119,6 @@ import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileDescriptor; @@ -175,6 +174,7 @@ public class UserManagerService extends IUserManager.Stub { private static final String ATTR_CONVERTED_FROM_PRE_CREATED = "convertedFromPreCreated"; private static final String ATTR_GUEST_TO_REMOVE = "guestToRemove"; private static final String ATTR_USER_VERSION = "version"; + private static final String ATTR_USER_TYPE_VERSION = "userTypeConfigVersion"; private static final String ATTR_PROFILE_GROUP_ID = "profileGroupId"; private static final String ATTR_PROFILE_BADGE = "profileBadge"; private static final String ATTR_RESTRICTED_PROFILE_PARENT_ID = "restrictedProfileParentId"; @@ -422,6 +422,7 @@ public class UserManagerService extends IUserManager.Stub { @GuardedBy("mPackagesLock") private int mNextSerialNumber; private int mUserVersion = 0; + private int mUserTypeVersion = 0; private IAppOpsService mAppOpsService; @@ -2565,6 +2566,8 @@ public class UserManagerService extends IUserManager.Stub { parser.getAttributeInt(null, ATTR_NEXT_SERIAL_NO, mNextSerialNumber); mUserVersion = parser.getAttributeInt(null, ATTR_USER_VERSION, mUserVersion); + mUserTypeVersion = + parser.getAttributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion); } // Pre-O global user restriction were stored as a single bundle (as opposed to per-user @@ -2627,7 +2630,7 @@ public class UserManagerService extends IUserManager.Stub { */ @GuardedBy({"mRestrictionsLock", "mPackagesLock"}) private void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions) { - upgradeIfNecessaryLP(oldGlobalUserRestrictions, mUserVersion); + upgradeIfNecessaryLP(oldGlobalUserRestrictions, mUserVersion, mUserTypeVersion); } /** @@ -2636,9 +2639,11 @@ public class UserManagerService extends IUserManager.Stub { */ @GuardedBy({"mRestrictionsLock", "mPackagesLock"}) @VisibleForTesting - void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions, int userVersion) { + void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions, int userVersion, + int userTypeVersion) { Set<Integer> userIdsToWrite = new ArraySet<>(); final int originalVersion = mUserVersion; + final int originalUserTypeVersion = mUserTypeVersion; if (userVersion < 1) { // Assign a proper name for the owner, if not initialized correctly before UserData userData = getUserDataNoChecks(UserHandle.USER_SYSTEM); @@ -2771,13 +2776,24 @@ public class UserManagerService extends IUserManager.Stub { userVersion = 9; } + // Done with userVersion changes, moving on to deal with userTypeVersion upgrades + // Upgrade from previous user type to a new user type + final int newUserTypeVersion = UserTypeFactory.getUserTypeVersion(); + if (newUserTypeVersion > userTypeVersion) { + synchronized (mUsersLock) { + upgradeUserTypesLU(UserTypeFactory.getUserTypeUpgrades(), mUserTypes, + userTypeVersion, userIdsToWrite); + } + } + if (userVersion < USER_VERSION) { Slog.w(LOG_TAG, "User version " + mUserVersion + " didn't upgrade as expected to " + USER_VERSION); } else { mUserVersion = userVersion; + mUserTypeVersion = newUserTypeVersion; - if (originalVersion < mUserVersion) { + if (originalVersion < mUserVersion || originalUserTypeVersion < mUserTypeVersion) { for (int userId : userIdsToWrite) { UserData userData = getUserDataNoChecks(userId); if (userData != null) { @@ -2801,6 +2817,7 @@ public class UserManagerService extends IUserManager.Stub { UserData userData = putUserInfo(system); mNextSerialNumber = MIN_USER_ID; mUserVersion = USER_VERSION; + mUserTypeVersion = UserTypeFactory.getUserTypeVersion(); Bundle restrictions = new Bundle(); try { @@ -2991,6 +3008,7 @@ public class UserManagerService extends IUserManager.Stub { serializer.startTag(null, TAG_USERS); serializer.attributeInt(null, ATTR_NEXT_SERIAL_NO, mNextSerialNumber); serializer.attributeInt(null, ATTR_USER_VERSION, mUserVersion); + serializer.attributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion); serializer.startTag(null, TAG_GUEST_RESTRICTIONS); synchronized (mGuestRestrictions) { @@ -4957,6 +4975,7 @@ public class UserManagerService extends IUserManager.Stub { // Dump UserTypes pw.println(); + pw.println("User types version: " + mUserTypeVersion); pw.println("User types (" + mUserTypes.size() + " types):"); for (int i = 0; i < mUserTypes.size(); i++) { pw.println(" " + mUserTypes.keyAt(i) + ": "); @@ -5447,6 +5466,9 @@ public class UserManagerService extends IUserManager.Stub { * Returns the maximum number of users allowed for the given userTypeDetails per parent user. * This is applicable for user types that are {@link UserTypeDetails#isProfile()}. * If there is no maximum, {@link UserTypeDetails#UNLIMITED_NUMBER_OF_USERS} is returned. + * Under certain circumstances (such as after a change-user-type) the max value can actually + * be exceeded: this is allowed in order to keep the device in a usable state. + * An error is logged in {@link UserManagerService#upgradeProfileToTypeLU} */ private static int getMaxUsersOfTypePerParent(UserTypeDetails userTypeDetails) { final int defaultMax = userTypeDetails.getMaxAllowedPerParent(); @@ -5534,4 +5556,98 @@ public class UserManagerService extends IUserManager.Stub { } return mDevicePolicyManagerInternal; } + + @GuardedBy("mUsersLock") + @VisibleForTesting + void upgradeUserTypesLU(@NonNull List<UserTypeFactory.UserTypeUpgrade> upgradeOps, + @NonNull ArrayMap<String, UserTypeDetails> userTypes, + final int formerUserTypeVersion, + @NonNull Set<Integer> userIdsToWrite) { + for (UserTypeFactory.UserTypeUpgrade userTypeUpgrade : upgradeOps) { + if (DBG) { + Slog.i(LOG_TAG, "Upgrade: " + userTypeUpgrade.getFromType() + " to: " + + userTypeUpgrade.getToType() + " maxVersion: " + + userTypeUpgrade.getUpToVersion()); + } + + // upgrade user type if version up to getUpToVersion() + if (formerUserTypeVersion <= userTypeUpgrade.getUpToVersion()) { + for (int i = 0; i < mUsers.size(); i++) { + UserData userData = mUsers.valueAt(i); + if (userTypeUpgrade.getFromType().equals(userData.info.userType)) { + final UserTypeDetails newUserType = userTypes.get( + userTypeUpgrade.getToType()); + + if (newUserType == null) { + throw new IllegalStateException( + "Upgrade destination user type not defined: " + + userTypeUpgrade.getToType()); + } + + upgradeProfileToTypeLU(userData.info, newUserType); + userIdsToWrite.add(userData.info.id); + } + } + } + } + } + + /** + * Changes the user type of a profile to a new user type. + * @param userInfo The user to be updated. + * @param newUserType The new user type. + */ + @GuardedBy("mUsersLock") + @VisibleForTesting + void upgradeProfileToTypeLU(@NonNull UserInfo userInfo, @NonNull UserTypeDetails newUserType) { + Slog.i(LOG_TAG, "Upgrading user " + userInfo.id + + " from " + userInfo.userType + + " to " + newUserType.getName()); + + if (!userInfo.isProfile()) { + throw new IllegalStateException( + "Can only upgrade profile types. " + userInfo.userType + + " is not a profile type."); + } + + // Exceeded maximum profiles for parent user: log error, but allow upgrade + if (!canAddMoreProfilesToUser(newUserType.getName(), userInfo.profileGroupId, false)) { + Slog.w(LOG_TAG, + "Exceeded maximum profiles of type " + newUserType.getName() + " for user " + + userInfo.id + ". Maximum allowed= " + + newUserType.getMaxAllowedPerParent()); + } + + final UserTypeDetails oldUserType = mUserTypes.get(userInfo.userType); + final int oldFlags; + if (oldUserType != null) { + oldFlags = oldUserType.getDefaultUserInfoFlags(); + } else { + // if oldUserType is missing from config_user_types.xml -> can only assume FLAG_PROFILE + oldFlags = UserInfo.FLAG_PROFILE; + } + + //convert userData to newUserType + userInfo.userType = newUserType.getName(); + // remove old default flags and add newUserType's default flags + userInfo.flags = newUserType.getDefaultUserInfoFlags() | (userInfo.flags ^ oldFlags); + + // merge existing base restrictions with the new type's default restrictions + synchronized (mRestrictionsLock) { + if (!UserRestrictionsUtils.isEmpty(newUserType.getDefaultRestrictions())) { + final Bundle newRestrictions = UserRestrictionsUtils.clone( + mBaseUserRestrictions.getRestrictions(userInfo.id)); + UserRestrictionsUtils.merge(newRestrictions, + newUserType.getDefaultRestrictions()); + updateUserRestrictionsInternalLR(newRestrictions, userInfo.id); + if (DBG) { + Slog.i(LOG_TAG, "Updated user " + userInfo.id + + " restrictions to " + newRestrictions); + } + } + } + + // re-compute badge index + userInfo.profileBadge = getFreeProfileBadgeLU(userInfo.profileGroupId, userInfo.userType); + } } diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index d0c3a95eafc7..0ac3030ba5dc 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -34,7 +34,6 @@ import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; -import android.provider.Settings.Global; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.util.Log; @@ -670,15 +669,6 @@ public class UserRestrictionsUtils { Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, 0, userId); } break; - case UserManager.DISALLOW_CONFIG_LOCATION: - // When DISALLOW_CONFIG_LOCATION is set on any user, we undo the global - // kill switch. - if (newValue) { - android.provider.Settings.Global.putString( - context.getContentResolver(), - Global.LOCATION_GLOBAL_KILL_SWITCH, "0"); - } - break; case UserManager.DISALLOW_APPS_CONTROL: // Intentional fall-through case UserManager.DISALLOW_UNINSTALL_APPS: @@ -774,14 +764,6 @@ public class UserRestrictionsUtils { restriction = UserManager.DISALLOW_AMBIENT_DISPLAY; break; - case android.provider.Settings.Global.LOCATION_GLOBAL_KILL_SWITCH: - if ("0".equals(value)) { - return false; - } - restriction = UserManager.DISALLOW_CONFIG_LOCATION; - checkAllUser = true; - break; - case android.provider.Settings.System.SCREEN_BRIGHTNESS: case android.provider.Settings.System.SCREEN_BRIGHTNESS_FLOAT: case android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE: diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java index d840e5d8b882..5fa46b9e4635 100644 --- a/services/core/java/com/android/server/pm/UserTypeDetails.java +++ b/services/core/java/com/android/server/pm/UserTypeDetails.java @@ -174,6 +174,9 @@ public final class UserTypeDetails { /** * Returns the maximum number of this user type allowed per parent (for user types, like * profiles, that have parents). + * Under certain circumstances (such as after a change-user-type) the max value can actually + * be exceeded: this is allowed in order to keep the device in a usable state. + * An error is logged in {@link UserManagerService#upgradeProfileToTypeLU} * <p>Returns {@link #UNLIMITED_NUMBER_OF_USERS} to indicate that there is no hard limit. */ public int getMaxAllowedPerParent() { diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java index ba8a2ba6a14e..1d3aecdca8dd 100644 --- a/services/core/java/com/android/server/pm/UserTypeFactory.java +++ b/services/core/java/com/android/server/pm/UserTypeFactory.java @@ -49,6 +49,7 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; +import java.util.List; import java.util.function.Consumer; /** @@ -73,14 +74,7 @@ public final class UserTypeFactory { * @return mapping from the name of each user type to its {@link UserTypeDetails} object */ public static ArrayMap<String, UserTypeDetails> getUserTypes() { - final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>(); - builders.put(USER_TYPE_PROFILE_MANAGED, getDefaultTypeProfileManaged()); - builders.put(USER_TYPE_FULL_SYSTEM, getDefaultTypeFullSystem()); - builders.put(USER_TYPE_FULL_SECONDARY, getDefaultTypeFullSecondary()); - builders.put(USER_TYPE_FULL_GUEST, getDefaultTypeFullGuest()); - builders.put(USER_TYPE_FULL_DEMO, getDefaultTypeFullDemo()); - builders.put(USER_TYPE_FULL_RESTRICTED, getDefaultTypeFullRestricted()); - builders.put(USER_TYPE_SYSTEM_HEADLESS, getDefaultTypeSystemHeadless()); + final ArrayMap<String, UserTypeDetails.Builder> builders = getDefaultBuilders(); try (XmlResourceParser parser = Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) { @@ -94,6 +88,20 @@ public final class UserTypeFactory { return types; } + private static ArrayMap<String, UserTypeDetails.Builder> getDefaultBuilders() { + final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>(); + + builders.put(USER_TYPE_PROFILE_MANAGED, getDefaultTypeProfileManaged()); + builders.put(USER_TYPE_FULL_SYSTEM, getDefaultTypeFullSystem()); + builders.put(USER_TYPE_FULL_SECONDARY, getDefaultTypeFullSecondary()); + builders.put(USER_TYPE_FULL_GUEST, getDefaultTypeFullGuest()); + builders.put(USER_TYPE_FULL_DEMO, getDefaultTypeFullDemo()); + builders.put(USER_TYPE_FULL_RESTRICTED, getDefaultTypeFullRestricted()); + builders.put(USER_TYPE_SYSTEM_HEADLESS, getDefaultTypeSystemHeadless()); + + return builders; + } + /** * Returns the Builder for the default {@link UserManager#USER_TYPE_PROFILE_MANAGED} * configuration. @@ -232,6 +240,10 @@ public final class UserTypeFactory { isProfile = true; } else if ("full-type".equals(elementName)) { isProfile = false; + } else if ("change-user-type".equals(elementName)) { + // parsed in parseUserUpgrades + XmlUtils.skipCurrentTag(parser); + continue; } else { Slog.w(LOG_TAG, "Skipping unknown element " + elementName + " in " + parser.getPositionDescription()); @@ -291,7 +303,8 @@ public final class UserTypeFactory { while (XmlUtils.nextElementWithin(parser, depth)) { final String childName = parser.getName(); if ("default-restrictions".equals(childName)) { - final Bundle restrictions = UserRestrictionsUtils.readRestrictions(parser); + final Bundle restrictions = UserRestrictionsUtils + .readRestrictions(XmlUtils.makeTyped(parser)); builder.setDefaultRestrictions(restrictions); } else if (isProfile && "badge-labels".equals(childName)) { setResAttributeArray(parser, builder::setBadgeLabels); @@ -387,4 +400,132 @@ public final class UserTypeFactory { } fcn.accept(result); } + + /** + * Returns the user type version of the config XML file. + * @return user type version defined in XML file, 0 if none. + */ + public static int getUserTypeVersion() { + try (XmlResourceParser parser = + Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) { + return getUserTypeVersion(parser); + } + } + + @VisibleForTesting + static int getUserTypeVersion(XmlResourceParser parser) { + int version = 0; + + try { + XmlUtils.beginDocument(parser, "user-types"); + String versionValue = parser.getAttributeValue(null, "version"); + if (versionValue != null) { + try { + version = Integer.parseInt(versionValue); + } catch (NumberFormatException e) { + Slog.e(LOG_TAG, "Cannot parse value of '" + versionValue + "' for version in " + + parser.getPositionDescription(), e); + throw e; + } + } + } catch (XmlPullParserException | IOException e) { + Slog.w(LOG_TAG, "Cannot read user type configuration file.", e); + } + + return version; + } + + /** + * Obtains the user type upgrades for this device. + * @return The list of user type upgrades. + */ + public static List<UserTypeUpgrade> getUserTypeUpgrades() { + final List<UserTypeUpgrade> userUpgrades; + try (XmlResourceParser parser = + Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) { + userUpgrades = parseUserUpgrades(getDefaultBuilders(), parser); + } + return userUpgrades; + } + + @VisibleForTesting + static List<UserTypeUpgrade> parseUserUpgrades( + ArrayMap<String, UserTypeDetails.Builder> builders, XmlResourceParser parser) { + final List<UserTypeUpgrade> userUpgrades = new ArrayList<>(); + + try { + XmlUtils.beginDocument(parser, "user-types"); + for (XmlUtils.nextElement(parser); + parser.getEventType() != XmlResourceParser.END_DOCUMENT; + XmlUtils.nextElement(parser)) { + final String elementName = parser.getName(); + if ("change-user-type".equals(elementName)) { + final String fromType = parser.getAttributeValue(null, "from"); + final String toType = parser.getAttributeValue(null, "to"); + // Check that the base type doesn't change. + // Currently, only the base type of PROFILE is supported. + validateUserTypeIsProfile(fromType, builders); + validateUserTypeIsProfile(toType, builders); + + final int maxVersionToConvert; + try { + maxVersionToConvert = Integer.parseInt( + parser.getAttributeValue(null, "whenVersionLeq")); + } catch (NumberFormatException e) { + Slog.e(LOG_TAG, "Cannot parse value of whenVersionLeq in " + + parser.getPositionDescription(), e); + throw e; + } + + UserTypeUpgrade userTypeUpgrade = new UserTypeUpgrade(fromType, toType, + maxVersionToConvert); + userUpgrades.add(userTypeUpgrade); + continue; + } else { + XmlUtils.skipCurrentTag(parser); + continue; + } + } + } catch (XmlPullParserException | IOException e) { + Slog.w(LOG_TAG, "Cannot read user type configuration file.", e); + } + + return userUpgrades; + } + + private static void validateUserTypeIsProfile(String userType, + ArrayMap<String, UserTypeDetails.Builder> builders) { + UserTypeDetails.Builder builder = builders.get(userType); + if (builder != null && builder.getBaseType() != FLAG_PROFILE) { + throw new IllegalArgumentException("Illegal upgrade of user type " + userType + + " : Can only upgrade profiles user types"); + } + } + + /** + * Contains details required for an upgrade operation for {@link UserTypeDetails}; + */ + public static class UserTypeUpgrade { + private final String mFromType; + private final String mToType; + private final int mUpToVersion; + + public UserTypeUpgrade(String fromType, String toType, int upToVersion) { + mFromType = fromType; + mToType = toType; + mUpToVersion = upToVersion; + } + + public String getFromType() { + return mFromType; + } + + public String getToType() { + return mToType; + } + + public int getUpToVersion() { + return mUpToVersion; + } + } } diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index e5c93a32ae90..165647205a98 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -63,6 +63,7 @@ import android.util.SparseArray; import android.util.TypedXmlPullParser; import android.util.Xml; +import com.android.internal.R; import com.android.internal.util.ArrayUtils; import com.android.internal.util.XmlUtils; import com.android.server.LocalServices; @@ -72,7 +73,6 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal.SyncAda import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -766,9 +766,14 @@ public final class DefaultPermissionGrantPolicy { grantSystemFixedPermissionsToSystemPackage(pm, wearPackage, userId, PHONE_PERMISSIONS); // Fitness tracking on watches - grantPermissionsToSystemPackage(pm, + if (mContext.getResources().getBoolean(R.bool.config_trackerAppNeedsPermissions)) { + Log.d(TAG, "Wear: Skipping permission grant for Default fitness tracker app : " + + wearPackage); + } else { + grantPermissionsToSystemPackage(pm, getDefaultSystemHandlerActivityPackage(pm, ACTION_TRACK, userId), userId, SENSORS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS); + } } // Print Spooler @@ -1408,11 +1413,8 @@ public final class DefaultPermissionGrantPolicy { Slog.w(TAG, "Default permissions file " + file + " cannot be read"); continue; } - try ( - InputStream str = new BufferedInputStream(new FileInputStream(file)) - ) { - TypedXmlPullParser parser = Xml.newFastPullParser(); - parser.setInput(str, null); + try (InputStream str = new FileInputStream(file)) { + TypedXmlPullParser parser = Xml.resolvePullParser(str); parse(pm, parser, grantExceptions); } catch (XmlPullParserException | IOException e) { Slog.w(TAG, "Error reading default permissions file " + file, e); diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index fb1ed2f6b58b..52bb3d772387 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -205,6 +205,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { private static final int USER_PERMISSION_FLAGS = FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED; + /** All storage permissions */ + private static final List<String> STORAGE_PERMISSIONS = new ArrayList<>(); + /** If the permission of the value is granted, so is the key */ private static final Map<String, String> FULLER_PERMISSION_MAP = new HashMap<>(); @@ -213,6 +216,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { Manifest.permission.ACCESS_FINE_LOCATION); FULLER_PERMISSION_MAP.put(Manifest.permission.INTERACT_ACROSS_USERS, Manifest.permission.INTERACT_ACROSS_USERS_FULL); + STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE); + STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); + STORAGE_PERMISSIONS.add(Manifest.permission.ACCESS_MEDIA_LOCATION); } /** Lock to protect internal data access */ @@ -1243,47 +1249,53 @@ public class PermissionManagerService extends IPermissionManager.Stub { final long identity = Binder.clearCallingIdentity(); try { - synchronized (mLock) { - final UidPermissionState uidState = getUidStateLocked(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + packageName + " and user " - + userId); - return null; - } + return getAllowlistedRestrictedPermissionsInternal(pkg, flags, userId); + } finally { + Binder.restoreCallingIdentity(identity); + } + } - int queryFlags = 0; - if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0) { - queryFlags |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; - } - if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE) != 0) { - queryFlags |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; - } - if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) { - queryFlags |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; - } - if ((flags & PackageManager.FLAG_PERMISSION_ALLOWLIST_ROLE) != 0) { - queryFlags |= FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT; - } + @Nullable + private List<String> getAllowlistedRestrictedPermissionsInternal(@NonNull AndroidPackage pkg, + @PermissionWhitelistFlags int flags, @UserIdInt int userId) { + synchronized (mLock) { + final UidPermissionState uidState = getUidStateLocked(pkg, userId); + if (uidState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " + + userId); + return null; + } - ArrayList<String> whitelistedPermissions = null; + int queryFlags = 0; + if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0) { + queryFlags |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; + } + if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE) != 0) { + queryFlags |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; + } + if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) { + queryFlags |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; + } + if ((flags & PackageManager.FLAG_PERMISSION_ALLOWLIST_ROLE) != 0) { + queryFlags |= FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT; + } - final int permissionCount = ArrayUtils.size(pkg.getRequestedPermissions()); - for (int i = 0; i < permissionCount; i++) { - final String permissionName = pkg.getRequestedPermissions().get(i); - final int currentFlags = - uidState.getPermissionFlags(permissionName); - if ((currentFlags & queryFlags) != 0) { - if (whitelistedPermissions == null) { - whitelistedPermissions = new ArrayList<>(); - } - whitelistedPermissions.add(permissionName); + ArrayList<String> allowlistedPermissions = null; + + final int permissionCount = ArrayUtils.size(pkg.getRequestedPermissions()); + for (int i = 0; i < permissionCount; i++) { + final String permissionName = pkg.getRequestedPermissions().get(i); + final int currentFlags = + uidState.getPermissionFlags(permissionName); + if ((currentFlags & queryFlags) != 0) { + if (allowlistedPermissions == null) { + allowlistedPermissions = new ArrayList<>(); } + allowlistedPermissions.add(permissionName); } - - return whitelistedPermissions; } - } finally { - Binder.restoreCallingIdentity(identity); + + return allowlistedPermissions; } } @@ -1429,8 +1441,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { final long identity = Binder.clearCallingIdentity(); try { - setAllowlistedRestrictedPermissionsInternal(pkg, permissions, flags, - new int[] { userId }); + setAllowlistedRestrictedPermissionsInternal(pkg, permissions, flags, userId); } finally { Binder.restoreCallingIdentity(identity); } @@ -1453,13 +1464,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { return setAutoRevokeExemptedInternal(pkg, whitelisted, userId); } - private void setAutoRevokeExemptedInternal(@NonNull AndroidPackage pkg, boolean exempted, - @NonNull int[] userIds) { - for (final int userId : userIds) { - setAutoRevokeExemptedInternal(pkg, exempted, userId); - } - } - private boolean setAutoRevokeExemptedInternal(@NonNull AndroidPackage pkg, boolean exempted, @UserIdInt int userId) { final int packageUid = UserHandle.getUid(userId, pkg.getUid()); @@ -2358,6 +2362,48 @@ public class PermissionManagerService extends IPermissionManager.Stub { } /** + * If the app is updated, and has scoped storage permissions, then it is possible that the + * app updated in an attempt to get unscoped storage. If so, revoke all storage permissions. + * @param newPackage The new package that was installed + * @param oldPackage The old package that was updated + */ + private void revokeStoragePermissionsIfScopeExpandedInternal( + @NonNull AndroidPackage newPackage, + @NonNull AndroidPackage oldPackage) { + boolean downgradedSdk = oldPackage.getTargetSdkVersion() >= Build.VERSION_CODES.Q + && newPackage.getTargetSdkVersion() < Build.VERSION_CODES.Q; + boolean upgradedSdk = oldPackage.getTargetSdkVersion() < Build.VERSION_CODES.Q + && newPackage.getTargetSdkVersion() >= Build.VERSION_CODES.Q; + boolean newlyRequestsLegacy = !upgradedSdk && !oldPackage.isRequestLegacyExternalStorage() + && newPackage.isRequestLegacyExternalStorage(); + + if (!newlyRequestsLegacy && !downgradedSdk) { + return; + } + + final int callingUid = Binder.getCallingUid(); + final int userId = UserHandle.getUserId(newPackage.getUid()); + int numRequestedPermissions = newPackage.getRequestedPermissions().size(); + for (int i = 0; i < numRequestedPermissions; i++) { + PermissionInfo permInfo = getPermissionInfo(newPackage.getRequestedPermissions().get(i), + newPackage.getPackageName(), 0); + if (permInfo == null || !STORAGE_PERMISSIONS.contains(permInfo.name)) { + continue; + } + + EventLog.writeEvent(0x534e4554, "171430330", newPackage.getUid(), + "Revoking permission " + permInfo.name + " from package " + + newPackage.getPackageName() + " as either the sdk downgraded " + + downgradedSdk + " or newly requested legacy full storage " + + newlyRequestsLegacy); + + revokeRuntimePermissionInternal(permInfo.name, newPackage.getPackageName(), + false, callingUid, userId, null, mDefaultPermissionCallback); + } + + } + + /** * We might auto-grant permissions if any permission of the group is already granted. Hence if * the group of a granted permission changes we need to revoke it to avoid having permissions of * the new group auto-granted. @@ -3775,13 +3821,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { } private void grantRequestedRuntimePermissionsInternal(@NonNull AndroidPackage pkg, - @Nullable List<String> permissions, @NonNull int[] userIds) { - for (int userId : userIds) { - grantRequestedRuntimePermissionsForUser(pkg, permissions, userId); - } - } - - private void grantRequestedRuntimePermissionsForUser(@NonNull AndroidPackage pkg, @Nullable List<String> permissions, int userId) { final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED | PackageManager.FLAG_PERMISSION_POLICY_FIXED; @@ -3828,123 +3867,119 @@ public class PermissionManagerService extends IPermissionManager.Stub { private void setAllowlistedRestrictedPermissionsInternal(@NonNull AndroidPackage pkg, @Nullable List<String> permissions, @PermissionWhitelistFlags int allowlistFlags, - @UserIdInt int[] userIds) { - SparseArray<ArraySet<String>> oldGrantedRestrictedPermissions = new SparseArray<>(); + @UserIdInt int userId) { + ArraySet<String> oldGrantedRestrictedPermissions = null; boolean updatePermissions = false; final int permissionCount = pkg.getRequestedPermissions().size(); final int myUid = Process.myUid(); - for (int i = 0; i < userIds.length; i++) { - int userId = userIds[i]; - - for (int j = 0; j < permissionCount; j++) { - final String permissionName = pkg.getRequestedPermissions().get(j); - - final boolean isGranted; - synchronized (mLock) { - final Permission bp = mRegistry.getPermission(permissionName); - if (bp == null || !bp.isHardOrSoftRestricted()) { - continue; - } + for (int j = 0; j < permissionCount; j++) { + final String permissionName = pkg.getRequestedPermissions().get(j); - final UidPermissionState uidState = getUidStateLocked(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() - + " and user " + userId); - continue; - } - isGranted = uidState.isPermissionGranted(permissionName); + final boolean isGranted; + synchronized (mLock) { + final Permission bp = mRegistry.getPermission(permissionName); + if (bp == null || !bp.isHardOrSoftRestricted()) { + continue; } - if (isGranted) { - if (oldGrantedRestrictedPermissions.get(userId) == null) { - oldGrantedRestrictedPermissions.put(userId, new ArraySet<>()); - } - oldGrantedRestrictedPermissions.get(userId).add(permissionName); + final UidPermissionState uidState = getUidStateLocked(pkg, userId); + if (uidState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + + " and user " + userId); + continue; } + isGranted = uidState.isPermissionGranted(permissionName); + } - final int oldFlags = getPermissionFlagsInternal(permissionName, - pkg.getPackageName(), myUid, userId); - - int newFlags = oldFlags; - int mask = 0; - int whitelistFlagsCopy = allowlistFlags; - while (whitelistFlagsCopy != 0) { - final int flag = 1 << Integer.numberOfTrailingZeros(whitelistFlagsCopy); - whitelistFlagsCopy &= ~flag; - switch (flag) { - case FLAG_PERMISSION_WHITELIST_SYSTEM: { - mask |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; - if (permissions != null && permissions.contains(permissionName)) { - newFlags |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; - } else { - newFlags &= ~FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; - } + if (isGranted) { + if (oldGrantedRestrictedPermissions == null) { + oldGrantedRestrictedPermissions = new ArraySet<>(); + } + oldGrantedRestrictedPermissions.add(permissionName); + } + + final int oldFlags = getPermissionFlagsInternal(permissionName, + pkg.getPackageName(), myUid, userId); + + int newFlags = oldFlags; + int mask = 0; + int whitelistFlagsCopy = allowlistFlags; + while (whitelistFlagsCopy != 0) { + final int flag = 1 << Integer.numberOfTrailingZeros(whitelistFlagsCopy); + whitelistFlagsCopy &= ~flag; + switch (flag) { + case FLAG_PERMISSION_WHITELIST_SYSTEM: { + mask |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; + if (permissions != null && permissions.contains(permissionName)) { + newFlags |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; + } else { + newFlags &= ~FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; } - break; - case FLAG_PERMISSION_WHITELIST_UPGRADE: { - mask |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; - if (permissions != null && permissions.contains(permissionName)) { - newFlags |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; - } else { - newFlags &= ~FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; - } + } + break; + case FLAG_PERMISSION_WHITELIST_UPGRADE: { + mask |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; + if (permissions != null && permissions.contains(permissionName)) { + newFlags |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; + } else { + newFlags &= ~FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; } - break; - case FLAG_PERMISSION_WHITELIST_INSTALLER: { - mask |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; - if (permissions != null && permissions.contains(permissionName)) { - newFlags |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; - } else { - newFlags &= ~FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; - } + } + break; + case FLAG_PERMISSION_WHITELIST_INSTALLER: { + mask |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; + if (permissions != null && permissions.contains(permissionName)) { + newFlags |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; + } else { + newFlags &= ~FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; } - break; - case FLAG_PERMISSION_ALLOWLIST_ROLE: { - mask |= FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT; - if (permissions != null && permissions.contains(permissionName)) { - newFlags |= FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT; - } else { - newFlags &= ~FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT; - } + } + break; + case FLAG_PERMISSION_ALLOWLIST_ROLE: { + mask |= FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT; + if (permissions != null && permissions.contains(permissionName)) { + newFlags |= FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT; + } else { + newFlags &= ~FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT; } - break; } + break; } + } - if (oldFlags == newFlags) { - continue; - } + if (oldFlags == newFlags) { + continue; + } - updatePermissions = true; - - final boolean wasWhitelisted = (oldFlags - & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0; - final boolean isWhitelisted = (newFlags - & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0; - - // If the permission is policy fixed as granted but it is no longer - // on any of the whitelists we need to clear the policy fixed flag - // as whitelisting trumps policy i.e. policy cannot grant a non - // grantable permission. - if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) { - if (!isWhitelisted && isGranted) { - mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED; - newFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED; - } - } + updatePermissions = true; + + final boolean wasWhitelisted = (oldFlags + & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0; + final boolean isWhitelisted = (newFlags + & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0; - // If we are whitelisting an app that does not support runtime permissions - // we need to make sure it goes through the permission review UI at launch. - if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M - && !wasWhitelisted && isWhitelisted) { - mask |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; - newFlags |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; + // If the permission is policy fixed as granted but it is no longer + // on any of the whitelists we need to clear the policy fixed flag + // as whitelisting trumps policy i.e. policy cannot grant a non + // grantable permission. + if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) { + if (!isWhitelisted && isGranted) { + mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED; + newFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED; } + } - updatePermissionFlagsInternal(permissionName, pkg.getPackageName(), mask, newFlags, - myUid, userId, false, null /*callback*/); + // If we are whitelisting an app that does not support runtime permissions + // we need to make sure it goes through the permission review UI at launch. + if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M + && !wasWhitelisted && isWhitelisted) { + mask |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; + newFlags |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; } + + updatePermissionFlagsInternal(permissionName, pkg.getPackageName(), mask, newFlags, + myUid, userId, false, null /*callback*/); } if (updatePermissions) { @@ -3952,31 +3987,27 @@ public class PermissionManagerService extends IPermissionManager.Stub { restorePermissionState(pkg, false, pkg.getPackageName(), mDefaultPermissionCallback); // If this resulted in losing a permission we need to kill the app. - for (int i = 0; i < userIds.length; i++) { - int userId = userIds[i]; - ArraySet<String> oldPermsForUser = oldGrantedRestrictedPermissions.get(userId); - if (oldPermsForUser == null) { - continue; - } + if (oldGrantedRestrictedPermissions == null) { + return; + } - final int oldGrantedCount = oldPermsForUser.size(); - for (int j = 0; j < oldGrantedCount; j++) { - final String permissionName = oldPermsForUser.valueAt(j); - // Sometimes we create a new permission state instance during update. - final boolean isGranted; - synchronized (mLock) { - final UidPermissionState uidState = getUidStateLocked(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() - + " and user " + userId); - continue; - } - isGranted = uidState.isPermissionGranted(permissionName); - } - if (!isGranted) { - mDefaultPermissionCallback.onPermissionRevoked(pkg.getUid(), userId, null); - break; + final int oldGrantedCount = oldGrantedRestrictedPermissions.size(); + for (int j = 0; j < oldGrantedCount; j++) { + final String permissionName = oldGrantedRestrictedPermissions.valueAt(j); + // Sometimes we create a new permission state instance during update. + final boolean isGranted; + synchronized (mLock) { + final UidPermissionState uidState = getUidStateLocked(pkg, userId); + if (uidState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + + " and user " + userId); + continue; } + isGranted = uidState.isPermissionGranted(permissionName); + } + if (!isGranted) { + mDefaultPermissionCallback.onPermissionRevoked(pkg.getUid(), userId, null); + break; } } } @@ -4884,6 +4915,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { AsyncTask.execute(() -> { if (hasOldPkg) { revokeRuntimePermissionsIfGroupChangedInternal(pkg, oldPkg); + revokeStoragePermissionsIfScopeExpandedInternal(pkg, oldPkg); } if (hasPermissionDefinitionChanges) { revokeRuntimePermissionsIfPermissionDefinitionChangedInternal( @@ -4914,6 +4946,34 @@ public class PermissionManagerService extends IPermissionManager.Stub { return true; } + private void onPackageInstalledInternal(@NonNull AndroidPackage pkg, + @NonNull List<String> grantedPermissions, + @NonNull List<String> allowlistedRestrictedPermissions, int autoRevokePermissionsMode, + @UserIdInt int userId) { + addAllowlistedRestrictedPermissionsInternal(pkg, allowlistedRestrictedPermissions, + FLAG_PERMISSION_WHITELIST_INSTALLER, userId); + if (autoRevokePermissionsMode == AppOpsManager.MODE_ALLOWED + || autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED) { + setAutoRevokeExemptedInternal(pkg, + autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED, userId); + } + grantRequestedRuntimePermissionsInternal(pkg, grantedPermissions, userId); + } + + private void addAllowlistedRestrictedPermissionsInternal(@NonNull AndroidPackage pkg, + @NonNull List<String> allowlistedRestrictedPermissions, + @PermissionWhitelistFlags int flags, @UserIdInt int userId) { + List<String> permissions = getAllowlistedRestrictedPermissionsInternal(pkg, flags, userId); + if (permissions != null) { + ArraySet<String> permissionSet = new ArraySet<>(permissions); + permissionSet.addAll(allowlistedRestrictedPermissions); + permissions = new ArrayList<>(permissionSet); + } else { + permissions = allowlistedRestrictedPermissions; + } + setAllowlistedRestrictedPermissionsInternal(pkg, permissions, flags, userId); + } + private void onPackageRemovedInternal(@NonNull AndroidPackage pkg) { removeAllPermissionsInternal(pkg); } @@ -5080,28 +5140,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { return PermissionManagerService.this.getAppOpPermissionPackagesInternal(permissionName); } @Override - public void grantRequestedRuntimePermissions(@NonNull AndroidPackage pkg, - @Nullable List<String> permissions, @NonNull int[] userIds) { - Objects.requireNonNull(pkg, "pkg"); - Objects.requireNonNull(userIds, "userIds"); - grantRequestedRuntimePermissionsInternal(pkg, permissions, userIds); - } - @Override - public void setAllowlistedRestrictedPermissions(@NonNull AndroidPackage pkg, - @Nullable List<String> permissions, @PermissionWhitelistFlags int allowlistFlags, - @NonNull int[] userIds) { - Objects.requireNonNull(pkg, "pkg"); - Objects.requireNonNull(userIds, "userIds"); - setAllowlistedRestrictedPermissionsInternal(pkg, permissions, allowlistFlags, userIds); - } - @Override - public void setAutoRevokeExempted(@NonNull AndroidPackage pkg, boolean exempted, - @NonNull int[] userIds) { - Objects.requireNonNull(pkg, "pkg"); - Objects.requireNonNull(userIds, "userIds"); - setAutoRevokeExemptedInternal(pkg, exempted, userIds); - } - @Override public void updatePermissions(@NonNull String packageName, @Nullable AndroidPackage pkg) { PermissionManagerService.this .updatePermissions(packageName, pkg, mDefaultPermissionCallback); @@ -5372,6 +5410,20 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override + public void onPackageInstalled(@NonNull AndroidPackage pkg, + @NonNull List<String> grantedPermissions, + @NonNull List<String> allowlistedRestrictedPermissions, + int autoRevokePermissionsMode, @UserIdInt int userId) { + Objects.requireNonNull(pkg, "pkg"); + Objects.requireNonNull(grantedPermissions, "grantedPermissions"); + Objects.requireNonNull(allowlistedRestrictedPermissions, + "allowlistedRestrictedPermissions"); + Preconditions.checkArgumentNonNegative(userId, "userId"); + onPackageInstalledInternal(pkg, grantedPermissions, allowlistedRestrictedPermissions, + autoRevokePermissionsMode, userId); + } + + @Override public void onPackageRemoved(@NonNull AndroidPackage pkg) { Objects.requireNonNull(pkg); onPackageRemovedInternal(pkg); diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index 1becbedc29fb..457fe36ca2b8 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -20,7 +20,6 @@ import android.annotation.AppIdInt; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; -import android.content.pm.PackageManager; import android.content.pm.PermissionInfo; import android.permission.PermissionManagerInternal; @@ -190,42 +189,6 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager @UserIdInt int userId); /** - * Grant the requested runtime permissions for a package, or an explicit subset of them. - * - * @param pkg the package - * @param permissions the names of the subset of permissions to be granted, or {@code null} for - * granting all the requested permissions - * @param userIds the user IDs - */ - //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) - public abstract void grantRequestedRuntimePermissions(@NonNull AndroidPackage pkg, - @Nullable List<String> permissions, @NonNull int[] userIds); - - /** - * Set the allowlisted restricted permissions for a package, or an explicit subset of them. - * - * @param pkg the package - * @param permissions the names of the subset of permissions to be allowlisted, or {@code null} - * for allowlisting all the requested restricted permissions - * @param userIds the user IDs - */ - //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) - public abstract void setAllowlistedRestrictedPermissions( - @NonNull AndroidPackage pkg, @Nullable List<String> permissions, - @PackageManager.PermissionWhitelistFlags int allowlistFlags, @NonNull int[] userIds); - - /** - * Set whether a package is exempted from auto revoke. - * - * @param pkg the package - * @param exempted whether the package is exempted from auto revoke - * @param userIds the user IDs - */ - //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) - public abstract void setAutoRevokeExempted(@NonNull AndroidPackage pkg, boolean exempted, - @NonNull int[] userIds); - - /** * Update permissions when a package changed. * * <p><ol> @@ -526,6 +489,21 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager @Nullable AndroidPackage oldPkg); /** + * Callback when a package has been installed for certain users. + * + * @param pkg the installed package + * @param grantedPermissions the permissions to be granted + * @param allowlistedRestrictedPermissions the restricted permissions to be allowlisted + * @param autoRevokePermissionsMode the auto revoke permissions mode for this package + * @param userId the user ID this package is installed for + */ + //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) + public abstract void onPackageInstalled(@NonNull AndroidPackage pkg, + @NonNull List<String> grantedPermissions, + @NonNull List<String> allowlistedRestrictedPermissions, + int autoRevokePermissionsMode, @UserIdInt int userId); + + /** * Callback when a package has been removed. * * @param pkg the removed package diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java index dd287ca6ed00..1e4e0a6a04bc 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java @@ -224,10 +224,9 @@ public class BatterySaverController implements BatterySaverPolicyListener { mFileUpdater = new FileUpdater(context); mBatterySavingStats = batterySavingStats; + // TODO(79580230): remove plugin code and maybe screen on/off listeners? // Initialize plugins. - mPlugins = new Plugin[] { - new BatterySaverLocationPlugin(mContext) - }; + mPlugins = new Plugin[0]; PowerManager.invalidatePowerSaveModeCaches(); } diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java deleted file mode 100644 index a77d133f6c32..000000000000 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2017 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.power.batterysaver; - -import android.content.Context; -import android.os.PowerManager; -import android.provider.Settings; -import android.provider.Settings.Global; -import android.util.Slog; - -import com.android.server.power.batterysaver.BatterySaverController.Plugin; - -public class BatterySaverLocationPlugin implements Plugin { - private static final String TAG = "BatterySaverLocationPlugin"; - - private static final boolean DEBUG = BatterySaverController.DEBUG; - - private final Context mContext; - - public BatterySaverLocationPlugin(Context context) { - mContext = context; - } - - @Override - public void onBatterySaverChanged(BatterySaverController caller) { - if (DEBUG) { - Slog.d(TAG, "onBatterySaverChanged"); - } - updateLocationState(caller); - } - - @Override - public void onSystemReady(BatterySaverController caller) { - if (DEBUG) { - Slog.d(TAG, "onSystemReady"); - } - updateLocationState(caller); - } - - private void updateLocationState(BatterySaverController caller) { - final boolean kill = - (caller.getBatterySaverPolicy().getGpsMode() - == PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF) - && !caller.isInteractive(); - - if (DEBUG) { - Slog.d(TAG, "Battery saver " + (kill ? "stopping" : "restoring") + " location."); - } - Settings.Global.putInt(mContext.getContentResolver(), - Global.LOCATION_GLOBAL_KILL_SWITCH, kill ? 1 : 0); - } -} diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java index b33dc8fab9a5..7751397d9c31 100644 --- a/services/core/java/com/android/server/role/RoleUserState.java +++ b/services/core/java/com/android/server/role/RoleUserState.java @@ -28,6 +28,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; import android.util.Slog; +import android.util.TypedXmlPullParser; import android.util.Xml; import com.android.internal.annotations.GuardedBy; @@ -391,8 +392,7 @@ public class RoleUserState { private void readLegacyFileLocked() { File file = getFile(mUserId); try (FileInputStream in = new AtomicFile(file).openRead()) { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(in, null); + TypedXmlPullParser parser = Xml.resolvePullParser(in); parseXmlLocked(parser); Slog.i(LOG_TAG, "Read roles.xml successfully"); } catch (FileNotFoundException e) { @@ -402,7 +402,7 @@ public class RoleUserState { } } - private void parseXmlLocked(@NonNull XmlPullParser parser) throws IOException, + private void parseXmlLocked(@NonNull TypedXmlPullParser parser) throws IOException, XmlPullParserException { int type; int depth; @@ -421,9 +421,9 @@ public class RoleUserState { Slog.w(LOG_TAG, "Missing <" + TAG_ROLES + "> in roles.xml"); } - private void parseRolesLocked(@NonNull XmlPullParser parser) throws IOException, + private void parseRolesLocked(@NonNull TypedXmlPullParser parser) throws IOException, XmlPullParserException { - mVersion = Integer.parseInt(parser.getAttributeValue(null, ATTRIBUTE_VERSION)); + mVersion = parser.getAttributeInt(null, ATTRIBUTE_VERSION); mPackagesHash = parser.getAttributeValue(null, ATTRIBUTE_PACKAGES_HASH); mRoles.clear(); @@ -445,7 +445,7 @@ public class RoleUserState { } @NonNull - private ArraySet<String> parseRoleHoldersLocked(@NonNull XmlPullParser parser) + private ArraySet<String> parseRoleHoldersLocked(@NonNull TypedXmlPullParser parser) throws IOException, XmlPullParserException { ArraySet<String> roleHolders = new ArraySet<>(); diff --git a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java index d2614e4698d1..4f3101dc318c 100644 --- a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java +++ b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java @@ -51,13 +51,10 @@ import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.FastXmlSerializer; -import com.android.internal.util.Preconditions; import com.android.server.pm.Installer; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileInputStream; @@ -65,7 +62,6 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -319,12 +315,12 @@ public class CacheQuotaStrategy implements RemoteCallback.OnResultListener { } @VisibleForTesting - static void saveToXml(XmlSerializer out, + static void saveToXml(TypedXmlSerializer out, List<CacheQuotaHint> requests, long bytesWhenCalculated) throws IOException { out.startDocument(null, true); out.startTag(null, CACHE_INFO_TAG); int requestSize = requests.size(); - out.attribute(null, ATTR_PREVIOUS_BYTES, Long.toString(bytesWhenCalculated)); + out.attributeLong(null, ATTR_PREVIOUS_BYTES, bytesWhenCalculated); for (int i = 0; i < requestSize; i++) { CacheQuotaHint request = requests.get(i); @@ -333,8 +329,8 @@ public class CacheQuotaStrategy implements RemoteCallback.OnResultListener { if (uuid != null) { out.attribute(null, ATTR_UUID, request.getVolumeUuid()); } - out.attribute(null, ATTR_UID, Integer.toString(request.getUid())); - out.attribute(null, ATTR_QUOTA_IN_BYTES, Long.toString(request.getQuota())); + out.attributeInt(null, ATTR_UID, request.getUid()); + out.attributeLong(null, ATTR_QUOTA_IN_BYTES, request.getQuota()); out.endTag(null, TAG_QUOTA); } out.endTag(null, CACHE_INFO_TAG); @@ -364,8 +360,7 @@ public class CacheQuotaStrategy implements RemoteCallback.OnResultListener { final List<CacheQuotaHint> quotas = new ArrayList<>(); long previousBytes; try { - previousBytes = Long.parseLong(parser.getAttributeValue( - null, ATTR_PREVIOUS_BYTES)); + previousBytes = parser.getAttributeLong(null, ATTR_PREVIOUS_BYTES); } catch (NumberFormatException e) { throw new IllegalStateException( "Previous bytes formatted incorrectly; aborting quota read."); @@ -389,14 +384,14 @@ public class CacheQuotaStrategy implements RemoteCallback.OnResultListener { } @VisibleForTesting - static CacheQuotaHint getRequestFromXml(XmlPullParser parser) { + static CacheQuotaHint getRequestFromXml(TypedXmlPullParser parser) { try { String uuid = parser.getAttributeValue(null, ATTR_UUID); - int uid = Integer.parseInt(parser.getAttributeValue(null, ATTR_UID)); - long bytes = Long.parseLong(parser.getAttributeValue(null, ATTR_QUOTA_IN_BYTES)); + int uid = parser.getAttributeInt(null, ATTR_UID); + long bytes = parser.getAttributeLong(null, ATTR_QUOTA_IN_BYTES); return new CacheQuotaHint.Builder() .setVolumeUuid(uuid).setUid(uid).setQuota(bytes).build(); - } catch (NumberFormatException e) { + } catch (XmlPullParserException e) { Slog.e(TAG, "Invalid cache quota request, skipping."); return null; } diff --git a/services/core/java/com/android/server/timezone/PackageStatusStorage.java b/services/core/java/com/android/server/timezone/PackageStatusStorage.java index 7652c438c295..fd0df8d090ec 100644 --- a/services/core/java/com/android/server/timezone/PackageStatusStorage.java +++ b/services/core/java/com/android/server/timezone/PackageStatusStorage.java @@ -129,7 +129,7 @@ final class PackageStatusStorage { @GuardedBy("this") private PackageStatus getPackageStatusLocked() throws ParseException { try (FileInputStream fis = mPackageStatusFile.openRead()) { - XmlPullParser parser = parseToPackageStatusTag(fis); + TypedXmlPullParser parser = parseToPackageStatusTag(fis); Integer checkStatus = getNullableIntAttribute(parser, ATTRIBUTE_CHECK_STATUS); if (checkStatus == null) { return null; @@ -254,7 +254,7 @@ final class PackageStatusStorage { @GuardedBy("this") private int getCurrentOptimisticLockId() throws ParseException { try (FileInputStream fis = mPackageStatusFile.openRead()) { - XmlPullParser parser = parseToPackageStatusTag(fis); + TypedXmlPullParser parser = parseToPackageStatusTag(fis); return getIntAttribute(parser, ATTRIBUTE_OPTIMISTIC_LOCK_ID); } catch (IOException e) { ParseException e2 = new ParseException("Unable to read file", 0); @@ -264,7 +264,7 @@ final class PackageStatusStorage { } /** Returns a parser or throws ParseException, never returns null. */ - private static XmlPullParser parseToPackageStatusTag(FileInputStream fis) + private static TypedXmlPullParser parseToPackageStatusTag(FileInputStream fis) throws ParseException { try { TypedXmlPullParser parser = Xml.resolvePullParser(fis); @@ -358,7 +358,7 @@ final class PackageStatusStorage { } } - private static Integer getNullableIntAttribute(XmlPullParser parser, String attributeName) + private static Integer getNullableIntAttribute(TypedXmlPullParser parser, String attributeName) throws ParseException { String attributeValue = parser.getAttributeValue(null, attributeName); try { @@ -374,7 +374,7 @@ final class PackageStatusStorage { } } - private static int getIntAttribute(XmlPullParser parser, String attributeName) + private static int getIntAttribute(TypedXmlPullParser parser, String attributeName) throws ParseException { Integer value = getNullableIntAttribute(parser, attributeName); if (value == null) { diff --git a/services/core/java/com/android/server/tv/PersistentDataStore.java b/services/core/java/com/android/server/tv/PersistentDataStore.java index 355c38544cb9..d3c9b3bbe7f5 100644 --- a/services/core/java/com/android/server/tv/PersistentDataStore.java +++ b/services/core/java/com/android/server/tv/PersistentDataStore.java @@ -26,6 +26,7 @@ import android.os.UserHandle; import android.text.TextUtils; import android.util.AtomicFile; import android.util.Slog; +import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import android.util.Xml; @@ -166,7 +167,7 @@ final class PersistentDataStore { return; } - XmlPullParser parser; + TypedXmlPullParser parser; try { parser = Xml.resolvePullParser(is); loadFromXml(parser); @@ -237,7 +238,7 @@ final class PersistentDataStore { private static final String ATTR_STRING = "string"; private static final String ATTR_ENABLED = "enabled"; - private void loadFromXml(XmlPullParser parser) + private void loadFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { XmlUtils.beginDocument(parser, TAG_TV_INPUT_MANAGER_STATE); final int outerDepth = parser.getDepth(); @@ -255,7 +256,7 @@ final class PersistentDataStore { } } - private void loadBlockedRatingsFromXml(XmlPullParser parser) + private void loadBlockedRatingsFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, outerDepth)) { @@ -270,7 +271,7 @@ final class PersistentDataStore { } } - private void saveToXml(XmlSerializer serializer) throws IOException { + private void saveToXml(TypedXmlSerializer serializer) throws IOException { serializer.startDocument(null, true); serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); serializer.startTag(null, TAG_TV_INPUT_MANAGER_STATE); @@ -284,7 +285,7 @@ final class PersistentDataStore { } serializer.endTag(null, TAG_BLOCKED_RATINGS); serializer.startTag(null, TAG_PARENTAL_CONTROLS); - serializer.attribute(null, ATTR_ENABLED, Boolean.toString(mParentalControlsEnabled)); + serializer.attributeBoolean(null, ATTR_ENABLED, mParentalControlsEnabled); serializer.endTag(null, TAG_PARENTAL_CONTROLS); serializer.endTag(null, TAG_TV_INPUT_MANAGER_STATE); serializer.endDocument(); diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java index 367b966a46ed..beb11ed4ea0c 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java @@ -20,6 +20,7 @@ import android.media.tv.TvInputService.PriorityHintUseCaseType; import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import android.util.TypedXmlPullParser; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; @@ -114,9 +115,7 @@ public class UseCasePriorityHints { protected void parseInternal(InputStream in) throws IOException, XmlPullParserException { try { - XmlPullParser parser = Xml.newPullParser(); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); - parser.setInput(in, null); + TypedXmlPullParser parser = Xml.resolvePullParser(in); parser.nextTag(); readUseCase(parser); in.close(); @@ -137,7 +136,7 @@ public class UseCasePriorityHints { } } - private void readUseCase(XmlPullParser parser) + private void readUseCase(TypedXmlPullParser parser) throws XmlPullParserException, IOException { parser.require(XmlPullParser.START_TAG, NS, "config"); while (parser.next() != XmlPullParser.END_TAG) { @@ -176,7 +175,7 @@ public class UseCasePriorityHints { } } - private void skip(XmlPullParser parser) throws XmlPullParserException, IOException { + private void skip(TypedXmlPullParser parser) throws XmlPullParserException, IOException { if (parser.getEventType() != XmlPullParser.START_TAG) { throw new IllegalStateException(); } @@ -193,7 +192,7 @@ public class UseCasePriorityHints { } } - private int readAttributeToInt(String attributeName, XmlPullParser parser) { + private int readAttributeToInt(String attributeName, TypedXmlPullParser parser) { return Integer.valueOf(parser.getAttributeValue(null, attributeName)); } @@ -203,7 +202,7 @@ public class UseCasePriorityHints { } @PriorityHintUseCaseType - private static int formatTypeToNum(String attributeName, XmlPullParser parser) { + private static int formatTypeToNum(String attributeName, TypedXmlPullParser parser) { String useCaseName = parser.getAttributeValue(null, attributeName); switch (useCaseName) { case "USE_CASE_BACKGROUND": diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java index bbb5374ea68a..dcc15999d882 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java @@ -668,22 +668,22 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { if (TAG_URI_GRANT.equals(tag)) { final int sourceUserId; final int targetUserId; - final int userHandle = readIntAttribute(in, - ATTR_USER_HANDLE, UserHandle.USER_NULL); + final int userHandle = in.getAttributeInt(null, ATTR_USER_HANDLE, + UserHandle.USER_NULL); if (userHandle != UserHandle.USER_NULL) { // For backwards compatibility. sourceUserId = userHandle; targetUserId = userHandle; } else { - sourceUserId = readIntAttribute(in, ATTR_SOURCE_USER_ID); - targetUserId = readIntAttribute(in, ATTR_TARGET_USER_ID); + sourceUserId = in.getAttributeInt(null, ATTR_SOURCE_USER_ID); + targetUserId = in.getAttributeInt(null, ATTR_TARGET_USER_ID); } final String sourcePkg = in.getAttributeValue(null, ATTR_SOURCE_PKG); final String targetPkg = in.getAttributeValue(null, ATTR_TARGET_PKG); final Uri uri = Uri.parse(in.getAttributeValue(null, ATTR_URI)); - final boolean prefix = readBooleanAttribute(in, ATTR_PREFIX); - final int modeFlags = readIntAttribute(in, ATTR_MODE_FLAGS); - final long createdTime = readLongAttribute(in, ATTR_CREATED_TIME, now); + final boolean prefix = in.getAttributeBoolean(null, ATTR_PREFIX, false); + final int modeFlags = in.getAttributeInt(null, ATTR_MODE_FLAGS); + final long createdTime = in.getAttributeLong(null, ATTR_CREATED_TIME, now); // Validity check that provider still belongs to source package // Both direct boot aware and unaware packages are fine as we @@ -1319,14 +1319,14 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { out.startTag(null, TAG_URI_GRANTS); for (UriPermission.Snapshot perm : persist) { out.startTag(null, TAG_URI_GRANT); - writeIntAttribute(out, ATTR_SOURCE_USER_ID, perm.uri.sourceUserId); - writeIntAttribute(out, ATTR_TARGET_USER_ID, perm.targetUserId); + out.attributeInt(null, ATTR_SOURCE_USER_ID, perm.uri.sourceUserId); + out.attributeInt(null, ATTR_TARGET_USER_ID, perm.targetUserId); out.attribute(null, ATTR_SOURCE_PKG, perm.sourcePkg); out.attribute(null, ATTR_TARGET_PKG, perm.targetPkg); out.attribute(null, ATTR_URI, String.valueOf(perm.uri.uri)); writeBooleanAttribute(out, ATTR_PREFIX, perm.uri.prefix); - writeIntAttribute(out, ATTR_MODE_FLAGS, perm.persistedModeFlags); - writeLongAttribute(out, ATTR_CREATED_TIME, perm.persistedCreateTime); + out.attributeInt(null, ATTR_MODE_FLAGS, perm.persistedModeFlags); + out.attributeLong(null, ATTR_CREATED_TIME, perm.persistedCreateTime); out.endTag(null, TAG_URI_GRANT); } out.endTag(null, TAG_URI_GRANTS); diff --git a/services/core/java/com/android/server/wm/utils/DeviceConfigInterface.java b/services/core/java/com/android/server/utils/DeviceConfigInterface.java index ab7e7f63cafd..ff609031b57c 100644 --- a/services/core/java/com/android/server/wm/utils/DeviceConfigInterface.java +++ b/services/core/java/com/android/server/utils/DeviceConfigInterface.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.wm.utils; +package com.android.server.utils; import android.annotation.NonNull; import android.annotation.Nullable; @@ -54,6 +54,11 @@ public interface DeviceConfigInterface { boolean getBoolean(@NonNull String namespace, @NonNull String name, boolean defaultValue); /** + * @see DeviceConfig#getFloat + */ + float getFloat(@NonNull String namespace, @NonNull String name, float defaultValue); + + /** * @see DeviceConfig#addOnPropertiesChangedListener */ void addOnPropertiesChangedListener(@NonNull String namespace, @NonNull Executor executor, @@ -96,6 +101,12 @@ public interface DeviceConfigInterface { } @Override + public float getFloat(@NonNull String namespace, @NonNull String name, + float defaultValue) { + return DeviceConfig.getFloat(namespace, name, defaultValue); + } + + @Override public void addOnPropertiesChangedListener(String namespace, Executor executor, DeviceConfig.OnPropertiesChangedListener listener) { DeviceConfig.addOnPropertiesChangedListener(namespace, executor, listener); diff --git a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java b/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java index 12c6a7a397e3..fe51d7408153 100644 --- a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java +++ b/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java @@ -99,6 +99,7 @@ public abstract class LocalEventLog { private long mLastLogRealtimeMs; public LocalEventLog(int size) { + Preconditions.checkArgument(size > 0); mLog = new Log[size]; mLogSize = 0; mLogEndIndex = 0; @@ -163,7 +164,7 @@ public abstract class LocalEventLog { if (mLogSize == mLog.length) { // if log is full, size will remain the same, but update the start time - mStartRealtimeMs += event.getTimeDeltaMs(); + mStartRealtimeMs += mLog[startIndex()].getTimeDeltaMs(); } else { // otherwise add an item mLogSize++; diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index e07540a0c746..bbe86beceb82 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -100,12 +100,12 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.PackageMonitor; import com.android.internal.os.BackgroundThread; import com.android.internal.util.DumpUtils; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.JournaledFile; import com.android.server.EventLogTags; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.SystemService.TargetUser; import com.android.server.pm.UserManagerInternal; import com.android.server.utils.TimingsTraceAndSlog; import com.android.server.wm.WindowManagerInternal; @@ -114,7 +114,6 @@ import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.BufferedOutputStream; import java.io.File; @@ -125,7 +124,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -2909,11 +2907,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub private void saveSettingsLocked(int userId) { JournaledFile journal = makeJournaledFile(userId); FileOutputStream fstream = null; - BufferedOutputStream stream = null; try { fstream = new FileOutputStream(journal.chooseForWrite(), false); - stream = new BufferedOutputStream(fstream); - TypedXmlSerializer out = Xml.resolveSerializer(stream); + TypedXmlSerializer out = Xml.resolveSerializer(fstream); out.startDocument(null, true); WallpaperData wallpaper; @@ -2929,56 +2925,56 @@ public class WallpaperManagerService extends IWallpaperManager.Stub out.endDocument(); - stream.flush(); // also flushes fstream + fstream.flush(); FileUtils.sync(fstream); - stream.close(); // also closes fstream + fstream.close(); journal.commit(); } catch (IOException e) { - IoUtils.closeQuietly(stream); + IoUtils.closeQuietly(fstream); journal.rollback(); } } - private void writeWallpaperAttributes(XmlSerializer out, String tag, WallpaperData wallpaper) + private void writeWallpaperAttributes(TypedXmlSerializer out, String tag, + WallpaperData wallpaper) throws IllegalArgumentException, IllegalStateException, IOException { if (DEBUG) { Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId); } final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY); out.startTag(null, tag); - out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId)); - out.attribute(null, "width", Integer.toString(wpdData.mWidth)); - out.attribute(null, "height", Integer.toString(wpdData.mHeight)); + out.attributeInt(null, "id", wallpaper.wallpaperId); + out.attributeInt(null, "width", wpdData.mWidth); + out.attributeInt(null, "height", wpdData.mHeight); - out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left)); - out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top)); - out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right)); - out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom)); + out.attributeInt(null, "cropLeft", wallpaper.cropHint.left); + out.attributeInt(null, "cropTop", wallpaper.cropHint.top); + out.attributeInt(null, "cropRight", wallpaper.cropHint.right); + out.attributeInt(null, "cropBottom", wallpaper.cropHint.bottom); if (wpdData.mPadding.left != 0) { - out.attribute(null, "paddingLeft", Integer.toString(wpdData.mPadding.left)); + out.attributeInt(null, "paddingLeft", wpdData.mPadding.left); } if (wpdData.mPadding.top != 0) { - out.attribute(null, "paddingTop", Integer.toString(wpdData.mPadding.top)); + out.attributeInt(null, "paddingTop", wpdData.mPadding.top); } if (wpdData.mPadding.right != 0) { - out.attribute(null, "paddingRight", Integer.toString(wpdData.mPadding.right)); + out.attributeInt(null, "paddingRight", wpdData.mPadding.right); } if (wpdData.mPadding.bottom != 0) { - out.attribute(null, "paddingBottom", Integer.toString(wpdData.mPadding.bottom)); + out.attributeInt(null, "paddingBottom", wpdData.mPadding.bottom); } if (wallpaper.primaryColors != null) { int colorsCount = wallpaper.primaryColors.getMainColors().size(); - out.attribute(null, "colorsCount", Integer.toString(colorsCount)); + out.attributeInt(null, "colorsCount", colorsCount); if (colorsCount > 0) { for (int i = 0; i < colorsCount; i++) { final Color wc = wallpaper.primaryColors.getMainColors().get(i); - out.attribute(null, "colorValue"+i, Integer.toString(wc.toArgb())); + out.attributeInt(null, "colorValue" + i, wc.toArgb()); } } - out.attribute(null, "colorHints", - Integer.toString(wallpaper.primaryColors.getColorHints())); + out.attributeInt(null, "colorHints", wallpaper.primaryColors.getColorHints()); } out.attribute(null, "name", wallpaper.name); @@ -3028,12 +3024,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } - private int getAttributeInt(XmlPullParser parser, String name, int defValue) { - String value = parser.getAttributeValue(null, name); - if (value == null) { - return defValue; - } - return Integer.parseInt(value); + private int getAttributeInt(TypedXmlPullParser parser, String name, int defValue) { + return parser.getAttributeInt(null, name, defValue); } /** @@ -3213,11 +3205,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } - private void parseWallpaperAttributes(XmlPullParser parser, WallpaperData wallpaper, - boolean keepDimensionHints) { - final String idString = parser.getAttributeValue(null, "id"); - if (idString != null) { - final int id = wallpaper.wallpaperId = Integer.parseInt(idString); + private void parseWallpaperAttributes(TypedXmlPullParser parser, WallpaperData wallpaper, + boolean keepDimensionHints) throws XmlPullParserException { + final int id = parser.getAttributeInt(null, "id", -1); + if (id != -1) { + wallpaper.wallpaperId = id; if (id > mWallpaperId) { mWallpaperId = id; } @@ -3228,8 +3220,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub final DisplayData wpData = getDisplayDataOrCreate(DEFAULT_DISPLAY); if (!keepDimensionHints) { - wpData.mWidth = Integer.parseInt(parser.getAttributeValue(null, "width")); - wpData.mHeight = Integer.parseInt(parser.getAttributeValue(null, "height")); + wpData.mWidth = parser.getAttributeInt(null, "width"); + wpData.mHeight = parser.getAttributeInt(null, "height"); } wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0); wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 913c3e580adf..75273ecb7534 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -18,7 +18,6 @@ package com.android.server.wm; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; -import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX; import static android.app.ActivityOptions.ANIM_CLIP_REVEAL; import static android.app.ActivityOptions.ANIM_CUSTOM; import static android.app.ActivityOptions.ANIM_NONE; @@ -96,18 +95,18 @@ import static android.view.Display.COLOR_MODE_DEFAULT; import static android.view.Display.INVALID_DISPLAY; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; +import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND; +import static android.view.WindowManager.TRANSIT_OLD_UNSET; import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; -import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND; -import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE; -import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE; -import static android.view.WindowManager.TRANSIT_OLD_UNSET; -import static android.view.WindowManager.TransitionOldType; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; @@ -183,6 +182,7 @@ import static com.android.server.wm.IdentifierProto.USER_ID; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION; +import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE; import static com.android.server.wm.Task.ActivityState.DESTROYED; import static com.android.server.wm.Task.ActivityState.DESTROYING; import static com.android.server.wm.Task.ActivityState.FINISHING; @@ -194,7 +194,6 @@ import static com.android.server.wm.Task.ActivityState.RESUMED; import static com.android.server.wm.Task.ActivityState.STARTED; import static com.android.server.wm.Task.ActivityState.STOPPED; import static com.android.server.wm.Task.ActivityState.STOPPING; -import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE; import static com.android.server.wm.TaskPersister.DEBUG; import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION; import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; @@ -277,6 +276,8 @@ import android.util.Log; import android.util.MergedConfiguration; import android.util.Slog; import android.util.TimeUtils; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; import android.view.AppTransitionAnimationSpec; import android.view.DisplayCutout; @@ -291,6 +292,7 @@ import android.view.SurfaceControl.Transaction; import android.view.WindowInsets.Type; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; +import android.view.WindowManager.TransitionOldType; import android.view.animation.Animation; import android.window.WindowContainerToken; @@ -320,9 +322,7 @@ import com.android.server.wm.utils.InsetUtils; import com.google.android.collect.Sets; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.IOException; @@ -6372,14 +6372,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } void setRequestedOrientation(int requestedOrientation) { - setOrientation(requestedOrientation, mayFreezeScreenLocked()); - mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged( - task.mTaskId, requestedOrientation); - } - - private void setOrientation(int requestedOrientation, boolean freezeScreenIfNeeded) { - final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null; - setOrientation(requestedOrientation, binder, this); + setOrientation(requestedOrientation, this); // Push the new configuration to the requested app in case where it's not pushed, e.g. when // the request is handled at task level with letterbox. @@ -6387,6 +6380,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mLastReportedConfiguration.getMergedConfiguration())) { ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */); } + + mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged( + task.mTaskId, requestedOrientation); } /* @@ -6403,8 +6399,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return; } - final IBinder freezeToken = mayFreezeScreenLocked() ? appToken : null; - if (onDescendantOrientationChanged(freezeToken, this)) { + if (onDescendantOrientationChanged(this)) { // The app is just becoming visible, and the parent Task has updated with the // orientation request. Update the size compat mode. updateSizeCompatMode(); @@ -7081,6 +7076,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return true; } + if (isState(DESTROYED)) { + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Skipping config check " + + "in destroyed state %s", this); + return true; + } + if (!ignoreVisibility && (mState == STOPPING || mState == STOPPED || !shouldBeVisible())) { ProtoLog.v(WM_DEBUG_CONFIGURATION, "Skipping config check " + "invisible: %s", this); @@ -7464,9 +7465,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A || (info.flags & FLAG_NO_HISTORY) != 0; } - void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException { - out.attribute(null, ATTR_ID, String.valueOf(createTime)); - out.attribute(null, ATTR_LAUNCHEDFROMUID, String.valueOf(launchedFromUid)); + void saveToXml(TypedXmlSerializer out) throws IOException, XmlPullParserException { + out.attributeLong(null, ATTR_ID, createTime); + out.attributeInt(null, ATTR_LAUNCHEDFROMUID, launchedFromUid); if (launchedFromPackage != null) { out.attribute(null, ATTR_LAUNCHEDFROMPACKAGE, launchedFromPackage); } @@ -7476,8 +7477,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (resolvedType != null) { out.attribute(null, ATTR_RESOLVEDTYPE, resolvedType); } - out.attribute(null, ATTR_COMPONENTSPECIFIED, String.valueOf(componentSpecified)); - out.attribute(null, ATTR_USERID, String.valueOf(mUserId)); + out.attributeBoolean(null, ATTR_COMPONENTSPECIFIED, componentSpecified); + out.attributeInt(null, ATTR_USERID, mUserId); if (taskDescription != null) { taskDescription.saveToXml(out); @@ -7494,43 +7495,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } - static ActivityRecord restoreFromXml(XmlPullParser in, + static ActivityRecord restoreFromXml(TypedXmlPullParser in, ActivityTaskSupervisor taskSupervisor) throws IOException, XmlPullParserException { Intent intent = null; PersistableBundle persistentState = null; - int launchedFromUid = 0; - String launchedFromPackage = null; - String launchedFromFeature = null; - String resolvedType = null; - boolean componentSpecified = false; - int userId = 0; - long createTime = -1; + int launchedFromUid = in.getAttributeInt(null, ATTR_LAUNCHEDFROMUID, 0); + String launchedFromPackage = in.getAttributeValue(null, ATTR_LAUNCHEDFROMPACKAGE); + String launchedFromFeature = in.getAttributeValue(null, ATTR_LAUNCHEDFROMFEATURE); + String resolvedType = in.getAttributeValue(null, ATTR_RESOLVEDTYPE); + boolean componentSpecified = in.getAttributeBoolean(null, ATTR_COMPONENTSPECIFIED, false); + int userId = in.getAttributeInt(null, ATTR_USERID, 0); + long createTime = in.getAttributeLong(null, ATTR_ID, -1); final int outerDepth = in.getDepth(); - TaskDescription taskDescription = new TaskDescription(); - for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) { - final String attrName = in.getAttributeName(attrNdx); - final String attrValue = in.getAttributeValue(attrNdx); - if (DEBUG) Slog.d(TaskPersister.TAG, - "ActivityRecord: attribute name=" + attrName + " value=" + attrValue); - if (ATTR_ID.equals(attrName)) { - createTime = Long.parseLong(attrValue); - } else if (ATTR_LAUNCHEDFROMUID.equals(attrName)) { - launchedFromUid = Integer.parseInt(attrValue); - } else if (ATTR_LAUNCHEDFROMPACKAGE.equals(attrName)) { - launchedFromPackage = attrValue; - } else if (ATTR_LAUNCHEDFROMFEATURE.equals(attrName)) { - launchedFromFeature = attrValue; - } else if (ATTR_RESOLVEDTYPE.equals(attrName)) { - resolvedType = attrValue; - } else if (ATTR_COMPONENTSPECIFIED.equals(attrName)) { - componentSpecified = Boolean.parseBoolean(attrValue); - } else if (ATTR_USERID.equals(attrName)) { - userId = Integer.parseInt(attrValue); - } else if (!attrName.startsWith(ATTR_TASKDESCRIPTION_PREFIX)) { - Log.d(TAG, "Unknown ActivityRecord attribute=" + attrName); - } - } + TaskDescription taskDescription = new TaskDescription(); taskDescription.restoreFromXml(in); int event; diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index e12dc2bd56a8..8ba76be65cb6 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -2738,9 +2738,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } - @Override - public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) { - return getFilteredTasks(maxNum, false /* filterForVisibleRecents */); + List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) { + return getTasks(maxNum, false /* filterForVisibleRecents */); } /** @@ -2748,7 +2747,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { * be visible in the recent task list in systemui */ @Override - public List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum, + public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum, boolean filterOnlyVisibleRecents) { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); diff --git a/services/core/java/com/android/server/wm/AppWarnings.java b/services/core/java/com/android/server/wm/AppWarnings.java index d40dea299ff6..55200b566979 100644 --- a/services/core/java/com/android/server/wm/AppWarnings.java +++ b/services/core/java/com/android/server/wm/AppWarnings.java @@ -492,7 +492,7 @@ class AppWarnings { } out.startTag(null, "package"); out.attribute(null, "name", pkg); - out.attribute(null, "flags", Integer.toString(mode)); + out.attributeInt(null, "flags", mode); out.endTag(null, "package"); } diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java index 89c5f62a252a..4b349542e347 100644 --- a/services/core/java/com/android/server/wm/CompatModePackages.java +++ b/services/core/java/com/android/server/wm/CompatModePackages.java @@ -43,7 +43,6 @@ import com.android.internal.protolog.common.ProtoLog; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileInputStream; @@ -113,14 +112,7 @@ public final class CompatModePackages { if ("pkg".equals(tagName)) { String pkg = parser.getAttributeValue(null, "name"); if (pkg != null) { - String mode = parser.getAttributeValue(null, "mode"); - int modeInt = 0; - if (mode != null) { - try { - modeInt = Integer.parseInt(mode); - } catch (NumberFormatException e) { - } - } + int modeInt = parser.getAttributeInt(null, "mode", 0); mPackages.put(pkg, modeInt); } } @@ -396,7 +388,7 @@ public final class CompatModePackages { } out.startTag(null, "pkg"); out.attribute(null, "name", pkg); - out.attribute(null, "mode", Integer.toString(mode)); + out.attributeInt(null, "mode", mode); out.endTag(null, "pkg"); } diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index 60a62dc0b64c..36a1ef9f49b4 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -182,6 +182,11 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { // writing to proto (which has significant cost if we write a lot of empty configurations). mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration); mRequestedOverrideConfiguration.setTo(overrideConfiguration); + final Rect newBounds = mRequestedOverrideConfiguration.windowConfiguration.getBounds(); + if (mHasOverrideConfiguration && providesMaxBounds() + && diffRequestedOverrideMaxBounds(newBounds) != BOUNDS_CHANGE_NONE) { + mRequestedOverrideConfiguration.windowConfiguration.setMaxBounds(newBounds); + } // Update full configuration of this container and all its children. final ConfigurationContainer parent = getParent(); onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY); @@ -341,9 +346,6 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration()); mRequestsTmpConfig.windowConfiguration.setBounds(bounds); - if (overrideMaxBounds) { - mRequestsTmpConfig.windowConfiguration.setMaxBounds(bounds); - } onRequestedOverrideConfigurationChanged(mRequestsTmpConfig); return boundsChange; diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index 48e030050b07..a4ac16f2ec77 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -33,7 +33,6 @@ import static com.android.server.wm.WindowContainerChildProto.DISPLAY_AREA; import android.annotation.Nullable; import android.content.res.Configuration; import android.graphics.Rect; -import android.os.IBinder; import android.util.proto.ProtoOutputStream; import android.window.DisplayAreaInfo; import android.window.IDisplayAreaOrganizer; @@ -151,12 +150,11 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { } @Override - boolean onDescendantOrientationChanged(IBinder freezeDisplayToken, - WindowContainer requestingContainer) { + boolean onDescendantOrientationChanged(WindowContainer requestingContainer) { // If this is set to ignore the orientation request, we don't propagate descendant // orientation request. return !mIgnoreOrientationRequest - && super.onDescendantOrientationChanged(freezeDisplayToken, requestingContainer); + && super.onDescendantOrientationChanged(requestingContainer); } /** diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index ccc85f834bc1..5df50500f3e2 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -55,6 +55,8 @@ import static android.view.WindowInsets.Type.displayCutout; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.systemBars; import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; +import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; +import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; @@ -73,8 +75,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; -import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; -import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.DisplayAreaOrganizer.FEATURE_ROOT; @@ -93,7 +93,6 @@ import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_C import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static com.android.server.wm.DisplayContentProto.APP_TRANSITION; -import static com.android.server.wm.DisplayContentProto.IME_POLICY; import static com.android.server.wm.DisplayContentProto.CLOSING_APPS; import static com.android.server.wm.DisplayContentProto.CURRENT_FOCUS; import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES; @@ -105,6 +104,7 @@ import static com.android.server.wm.DisplayContentProto.FOCUSED_APP; import static com.android.server.wm.DisplayContentProto.FOCUSED_ROOT_TASK_ID; import static com.android.server.wm.DisplayContentProto.ID; import static com.android.server.wm.DisplayContentProto.IME_INSETS_SOURCE_PROVIDER; +import static com.android.server.wm.DisplayContentProto.IME_POLICY; import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_CONTROL_TARGET; import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_INPUT_TARGET; import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_TARGET; @@ -559,6 +559,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ InsetsControlTarget mInputMethodControlTarget; + /** The surface parent of the IME container. */ + private SurfaceControl mInputMethodSurfaceParent; + /** If true hold off on modifying the animation layer of mInputMethodTarget */ boolean mInputMethodTargetWaitingAnim; @@ -1300,10 +1303,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } @Override - boolean onDescendantOrientationChanged(IBinder freezeDisplayToken, - WindowContainer requestingContainer) { + boolean onDescendantOrientationChanged(WindowContainer requestingContainer) { final Configuration config = updateOrientation( - getRequestedOverrideConfiguration(), freezeDisplayToken, false /* forceUpdate */); + getRequestedOverrideConfiguration(), requestingContainer, false /* forceUpdate */); // If display rotation class tells us that it doesn't consider app requested orientation, // this display won't rotate just because of an app changes its requested orientation. Thus // it indicates that this display chooses not to handle this request. @@ -1355,11 +1357,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * @param currentConfig The current requested override configuration (it is usually set from * the last {@link #sendNewConfiguration}) of the display. It is used to * check if the configuration container has the latest state. - * @param freezeDisplayToken Freeze the app window token if the orientation is changed. + * @param freezeDisplayWindow Freeze the app window if the orientation is changed. * @param forceUpdate See {@link DisplayRotation#updateRotationUnchecked(boolean)} */ - Configuration updateOrientation(Configuration currentConfig, IBinder freezeDisplayToken, - boolean forceUpdate) { + Configuration updateOrientation(Configuration currentConfig, + WindowContainer freezeDisplayWindow, boolean forceUpdate) { if (!mDisplayReady) { return null; } @@ -1368,9 +1370,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (updateOrientation(forceUpdate)) { // If we changed the orientation but mOrientationChangeComplete is already true, // we used seamless rotation, and we don't need to freeze the screen. - if (freezeDisplayToken != null && !mWmService.mRoot.mOrientationChangeComplete) { - final ActivityRecord activity = getActivityRecord(freezeDisplayToken); - if (activity != null) { + if (freezeDisplayWindow != null && !mWmService.mRoot.mOrientationChangeComplete) { + final ActivityRecord activity = freezeDisplayWindow.asActivityRecord(); + if (activity != null && activity.mayFreezeScreenLocked()) { activity.startFreezingScreen(); } } @@ -1677,6 +1679,28 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } } + void notifyInsetsChanged(Consumer<WindowState> dispatchInsetsChanged) { + if (mFixedRotationLaunchingApp != null) { + // The insets state of fixed rotation app is a rotated copy. Make sure the visibilities + // of insets sources are consistent with the latest state. + final InsetsState rotatedState = + mFixedRotationLaunchingApp.getFixedRotationTransformInsetsState(); + if (rotatedState != null) { + final InsetsState state = mInsetsStateController.getRawInsetsState(); + for (int i = 0; i < InsetsState.SIZE; i++) { + final InsetsSource source = state.peekSource(i); + if (source != null) { + rotatedState.setSourceVisible(i, source.isVisible()); + } + } + } + } + forAllWindows(dispatchInsetsChanged, true /* traverseTopToBottom */); + if (mRemoteInsetsControlTarget != null) { + mRemoteInsetsControlTarget.notifyInsetsChanged(); + } + } + /** * Update rotation of the display. * @@ -3610,7 +3634,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp ProtoLog.i(WM_DEBUG_IME, "setInputMethodTarget %s", target); mInputMethodTarget = target; mInputMethodTargetWaitingAnim = targetWaitingAnim; - assignWindowLayers(true /* setLayoutNeeded */); + + // 1. Reparent the IME container window to the target root DA to get the correct bounds and + // config. (Only happens when the target window is in a different root DA) if (target != null) { RootDisplayArea targetRoot = target.getRootDisplayArea(); if (targetRoot != null) { @@ -3619,7 +3645,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp targetRoot.placeImeContainer(mImeWindowsContainers); } } + // 2. Reparent the IME container surface to either the input target app, or the IME window + // parent. updateImeParent(); + // 3. Assign window layers based on the IME surface parent to make sure it is on top of the + // app. + assignWindowLayers(true /* setLayoutNeeded */); + // 4. Update the IME control target to apply any inset change and animation. updateImeControlTarget(); } @@ -3649,7 +3681,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp void updateImeParent() { final SurfaceControl newParent = computeImeParent(); - if (newParent != null) { + if (newParent != null && newParent != mInputMethodSurfaceParent) { + mInputMethodSurfaceParent = newParent; getPendingTransaction().reparent(mImeWindowsContainers.mSurfaceControl, newParent); scheduleAnimation(); } @@ -4426,18 +4459,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // // In the case of split-screen windowing mode, we need to elevate the IME above the // docked divider while keeping the app itself below the docked divider, so instead - // we use relative layering of the IME targets child windows, and place the IME in - // the non-app layer (see {@link AboveAppWindowContainers#assignChildLayers}). + // we will put the docked divider below the IME. @see #assignRelativeLayerForImeTargetChild // // In the case the IME target is animating, the animation Z order may be different // than the WindowContainer Z order, so it's difficult to be sure we have the correct - // IME target. In this case we just layer the IME over all transitions by placing it - // in the above applications layer. + // IME target. In this case we just layer the IME over its parent surface. // - // In the case where we have no IME target we assign it where its base layer would - // place it in the AboveAppWindowContainers. + // In the case where we have no IME target we let its window parent to place it. // - // Keep IME window in mAboveAppWindowsContainers as long as app's starting window + // Keep IME window in surface parent as long as app's starting window // exists so it get's layered above the starting window. if (imeTarget != null && !(imeTarget.mActivityRecord != null && imeTarget.mActivityRecord.hasStartingWindow()) && ( @@ -4448,6 +4478,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // TODO: We need to use an extra level on the app surface to ensure // this is always above SurfaceView but always below attached window. 1); + } else if (mInputMethodSurfaceParent != null) { + // The IME surface parent may not be its window parent's surface + // (@see #computeImeParent), so set relative layer here instead of letting the window + // parent to assign layer. + mImeWindowsContainers.assignRelativeLayer(t, mInputMethodSurfaceParent, 1); } super.assignChildLayers(t); } @@ -5197,7 +5232,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mCurrentOverrideConfigurationChanges = currOverrideConfig.diff(overrideConfiguration); super.onRequestedOverrideConfigurationChanged(overrideConfiguration); mCurrentOverrideConfigurationChanges = 0; - mWmService.setNewDisplayOverrideConfiguration(overrideConfiguration, this); + mWmService.setNewDisplayOverrideConfiguration(currOverrideConfig, this); mAtmService.addWindowLayoutReasons( ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED); } @@ -5689,4 +5724,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } return count; } + + MagnificationSpec getMagnificationSpec() { + return mMagnificationSpec; + } } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 042dd6db9800..826b7259a9ff 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -66,7 +66,6 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BA import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; @@ -279,7 +278,6 @@ public class DisplayPolicy { private volatile boolean mKeyguardDrawComplete; private volatile boolean mWindowManagerDrawComplete; - private final ArraySet<WindowState> mScreenDecorWindows = new ArraySet<>(); private WindowState mStatusBar = null; private WindowState mNotificationShade = null; private final int[] mStatusBarHeightForRotation = new int[4]; @@ -864,19 +862,7 @@ public class DisplayPolicy { * @param attrs The window layout parameters to be modified. These values * are modified in-place. */ - public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs, - int callingPid, int callingUid) { - - final boolean isScreenDecor = (attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0; - if (mScreenDecorWindows.contains(win)) { - if (!isScreenDecor) { - // No longer has the flag set, so remove from the set. - mScreenDecorWindows.remove(win); - } - } else if (isScreenDecor && hasStatusBarServicePermission(callingPid, callingUid)) { - mScreenDecorWindows.add(win); - } - + public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs) { switch (attrs.type) { case TYPE_SYSTEM_OVERLAY: case TYPE_SECURE_SYSTEM_OVERLAY: @@ -966,11 +952,6 @@ public class DisplayPolicy { * WindowManagerImpl.ADD_MULTIPLE_SINGLETON */ int validateAddingWindowLw(WindowManager.LayoutParams attrs, int callingPid, int callingUid) { - if ((attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0) { - mContext.enforcePermission( - android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid, - "DisplayPolicy"); - } if ((attrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0) { mContext.enforcePermission( android.Manifest.permission.INTERNAL_SYSTEM_WINDOW, callingPid, callingUid, @@ -1090,10 +1071,6 @@ public class DisplayPolicy { * @param attrs Information about the window to be added. */ void addWindowLw(WindowState win, WindowManager.LayoutParams attrs) { - if ((attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0) { - mScreenDecorWindows.add(win); - } - switch (attrs.type) { case TYPE_NOTIFICATION_SHADE: mNotificationShade = win; @@ -1275,7 +1252,6 @@ public class DisplayPolicy { if (mLastFocusedWindow == win) { mLastFocusedWindow = null; } - mScreenDecorWindows.remove(win); } private int getStatusBarHeight(DisplayFrames displayFrames) { @@ -1457,14 +1433,12 @@ public class DisplayPolicy { } final int fl = attrs.flags; - final int pfl = attrs.privateFlags; final boolean layoutInScreenAndInsetDecor = (fl & FLAG_LAYOUT_IN_SCREEN) != 0 && (fl & FLAG_LAYOUT_INSET_DECOR) != 0; - final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0; final DisplayFrames displayFrames = isFixedRotationTransforming ? windowToken.getFixedRotationTransformDisplayFrames() : mDisplayContent.mDisplayFrames; - if (layoutInScreenAndInsetDecor && !screenDecor) { + if (layoutInScreenAndInsetDecor) { outDisplayCutout.set( displayFrames.mDisplayCutout.calculateRelativeTo(outFrame).getDisplayCutout()); } else { @@ -1564,7 +1538,6 @@ public class DisplayPolicy { simulatedWindowFrames, barContentFrames, contentFrame -> layoutStatusBar(displayFrames, contentFrame)); } - layoutScreenDecorWindows(displayFrames, simulatedWindowFrames); } /** @@ -1585,7 +1558,6 @@ public class DisplayPolicy { layoutNavigationBar(displayFrames, uiMode, null /* simulatedContentFrame */); layoutStatusBar(displayFrames, null /* simulatedContentFrame */); - layoutScreenDecorWindows(displayFrames, null /* simulatedFrames */); } void updateHideNavInputEventReceiver() { @@ -1640,47 +1612,6 @@ public class DisplayPolicy { state.getSource(ITYPE_BOTTOM_DISPLAY_CUTOUT).setFrame(u.left, s.bottom, u.right, u.bottom); } - /** - * Layout the decor windows with {@link #PRIVATE_FLAG_IS_SCREEN_DECOR}. - * - * @param displayFrames The display frames to be layouted. - * @param simulatedFrames Non-null if the caller only needs the result of display frames (see - * {@link WindowState#mSimulatedWindowFrames}). - */ - private void layoutScreenDecorWindows(DisplayFrames displayFrames, - WindowFrames simulatedFrames) { - if (mScreenDecorWindows.isEmpty()) { - return; - } - - sTmpRect.setEmpty(); - final int displayId = displayFrames.mDisplayId; - - for (int i = mScreenDecorWindows.size() - 1; i >= 0; --i) { - final WindowState w = mScreenDecorWindows.valueAt(i); - if (w.getDisplayId() != displayId || !w.isVisible()) { - // Skip if not on the same display or not visible. - continue; - } - - final boolean isSimulatedLayout = simulatedFrames != null; - if (isSimulatedLayout) { - w.setSimulatedWindowFrames(simulatedFrames); - } - getRotatedWindowBounds(displayFrames, w, sTmpScreenDecorFrame); - final WindowFrames windowFrames = w.getLayoutingWindowFrames(); - windowFrames.setFrames(sTmpScreenDecorFrame /* parentFrame */, - sTmpScreenDecorFrame /* displayFrame */); - try { - w.computeFrame(displayFrames); - } finally { - if (isSimulatedLayout) { - w.setSimulatedWindowFrames(null); - } - } - } - } - private void layoutStatusBar(DisplayFrames displayFrames, Rect simulatedContentFrame) { // decide where the status bar goes ahead of time if (mStatusBar == null) { @@ -1819,8 +1750,7 @@ public class DisplayPolicy { // We've already done the navigation bar, status bar, and all screen decor windows. If the // status bar can receive input, we need to layout it again to accommodate for the IME // window. - if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar - || mScreenDecorWindows.contains(win)) { + if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar) { return; } final WindowManager.LayoutParams attrs = win.getAttrs(); diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java index 78f1426348a7..aa603aba0f78 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java @@ -335,36 +335,31 @@ class DisplayWindowSettingsProvider implements SettingsProvider { return fileData; } - private static int getIntAttribute(XmlPullParser parser, String name, int defaultValue) { - try { - final String str = parser.getAttributeValue(null, name); - return str != null ? Integer.parseInt(str) : defaultValue; - } catch (NumberFormatException e) { - Slog.w(TAG, "Failed to parse display window settings attribute: " + name, e); - return defaultValue; - } + private static int getIntAttribute(TypedXmlPullParser parser, String name, int defaultValue) { + return parser.getAttributeInt(null, name, defaultValue); } @Nullable - private static Integer getIntegerAttribute(XmlPullParser parser, String name, + private static Integer getIntegerAttribute(TypedXmlPullParser parser, String name, @Nullable Integer defaultValue) { try { - final String str = parser.getAttributeValue(null, name); - return str != null ? Integer.valueOf(str) : defaultValue; - } catch (NumberFormatException e) { - Slog.w(TAG, "Failed to parse display window settings attribute: " + name, e); + return parser.getAttributeInt(null, name); + } catch (Exception ignored) { return defaultValue; } } @Nullable - private static Boolean getBooleanAttribute(XmlPullParser parser, String name, + private static Boolean getBooleanAttribute(TypedXmlPullParser parser, String name, @Nullable Boolean defaultValue) { - final String str = parser.getAttributeValue(null, name); - return str != null ? Boolean.valueOf(str) : defaultValue; + try { + return parser.getAttributeBoolean(null, name); + } catch (Exception ignored) { + return defaultValue; + } } - private static void readDisplay(XmlPullParser parser, FileData fileData) + private static void readDisplay(TypedXmlPullParser parser, FileData fileData) throws NumberFormatException, XmlPullParserException, IOException { String name = parser.getAttributeValue(null, "name"); if (name != null) { @@ -407,7 +402,7 @@ class DisplayWindowSettingsProvider implements SettingsProvider { XmlUtils.skipCurrentTag(parser); } - private static void readConfig(XmlPullParser parser, FileData fileData) + private static void readConfig(TypedXmlPullParser parser, FileData fileData) throws NumberFormatException, XmlPullParserException, IOException { fileData.mIdentifierType = getIntAttribute(parser, "identifier", @@ -432,8 +427,7 @@ class DisplayWindowSettingsProvider implements SettingsProvider { out.startTag(null, "display-settings"); out.startTag(null, "config"); - out.attribute(null, "identifier", - Integer.toString(data.mIdentifierType)); + out.attributeInt(null, "identifier", data.mIdentifierType); out.endTag(null, "config"); for (Map.Entry<String, SettingsEntry> entry @@ -447,8 +441,7 @@ class DisplayWindowSettingsProvider implements SettingsProvider { out.startTag(null, "display"); out.attribute(null, "name", displayIdentifier); if (settingsEntry.mWindowingMode != WindowConfiguration.WINDOWING_MODE_UNDEFINED) { - out.attribute(null, "windowingMode", - Integer.toString(settingsEntry.mWindowingMode)); + out.attributeInt(null, "windowingMode", settingsEntry.mWindowingMode); } if (settingsEntry.mUserRotationMode != null) { out.attribute(null, "userRotationMode", @@ -459,22 +452,18 @@ class DisplayWindowSettingsProvider implements SettingsProvider { settingsEntry.mUserRotation.toString()); } if (settingsEntry.mForcedWidth != 0 && settingsEntry.mForcedHeight != 0) { - out.attribute(null, "forcedWidth", - Integer.toString(settingsEntry.mForcedWidth)); - out.attribute(null, "forcedHeight", - Integer.toString(settingsEntry.mForcedHeight)); + out.attributeInt(null, "forcedWidth", settingsEntry.mForcedWidth); + out.attributeInt(null, "forcedHeight", settingsEntry.mForcedHeight); } if (settingsEntry.mForcedDensity != 0) { - out.attribute(null, "forcedDensity", - Integer.toString(settingsEntry.mForcedDensity)); + out.attributeInt(null, "forcedDensity", settingsEntry.mForcedDensity); } if (settingsEntry.mForcedScalingMode != null) { out.attribute(null, "forcedScalingMode", settingsEntry.mForcedScalingMode.toString()); } if (settingsEntry.mRemoveContentMode != REMOVE_CONTENT_MODE_UNDEFINED) { - out.attribute(null, "removeContentMode", - Integer.toString(settingsEntry.mRemoveContentMode)); + out.attributeInt(null, "removeContentMode", settingsEntry.mRemoveContentMode); } if (settingsEntry.mShouldShowWithInsecureKeyguard != null) { out.attribute(null, "shouldShowWithInsecureKeyguard", diff --git a/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java b/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java index cdc14cd11228..92baadf5ee69 100644 --- a/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java +++ b/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java @@ -27,7 +27,7 @@ import android.util.ArraySet; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; -import com.android.server.wm.utils.DeviceConfigInterface; +import com.android.server.utils.DeviceConfigInterface; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/wm/ImpressionAttestationController.java b/services/core/java/com/android/server/wm/ImpressionAttestationController.java index 4793e1b6fb9f..b0afc57b647b 100644 --- a/services/core/java/com/android/server/wm/ImpressionAttestationController.java +++ b/services/core/java/com/android/server/wm/ImpressionAttestationController.java @@ -18,6 +18,9 @@ package com.android.server.wm; import static android.service.attestation.ImpressionAttestationService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; + import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; @@ -29,7 +32,9 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.Resources; +import android.graphics.Matrix; import android.graphics.Rect; +import android.graphics.RectF; import android.hardware.HardwareBuffer; import android.os.Binder; import android.os.Bundle; @@ -43,6 +48,7 @@ import android.service.attestation.IImpressionAttestationService; import android.service.attestation.ImpressionAttestationService; import android.service.attestation.ImpressionToken; import android.util.Slog; +import android.view.MagnificationSpec; import com.android.internal.annotations.GuardedBy; @@ -59,7 +65,8 @@ import java.util.function.BiConsumer; * blocking calls into another service. */ public class ImpressionAttestationController { - private static final String TAG = "ImpressionAttestationController"; + private static final String TAG = + TAG_WITH_CLASS_NAME ? "ImpressionAttestationController" : TAG_WM; private static final boolean DEBUG = false; private final Object mServiceConnectionLock = new Object(); @@ -81,6 +88,10 @@ public class ImpressionAttestationController { private final String mSalt; + private final float[] mTmpFloat9 = new float[9]; + private final Matrix mTmpMatrix = new Matrix(); + private final RectF mTmpRectF = new RectF(); + private interface Command { void run(IImpressionAttestationService service) throws RemoteException; } @@ -151,6 +162,79 @@ public class ImpressionAttestationController { } /** + * Calculate the bounds to take the screenshot when generating the impression token. This takes + * into account window transform, magnification, and display bounds. + * + * Call while holding {@link WindowManagerService#mGlobalLock} + * + * @param win Window that the impression token is generated for. + * @param boundsInWindow The bounds passed in about where in the window to take the screenshot. + * @param outBounds The result of the calculated bounds + */ + void calculateImpressionTokenBoundsLocked(WindowState win, Rect boundsInWindow, + Rect outBounds) { + if (DEBUG) { + Slog.d(TAG, "calculateImpressionTokenBounds: boundsInWindow=" + boundsInWindow); + } + outBounds.set(boundsInWindow); + + DisplayContent displayContent = win.getDisplayContent(); + if (displayContent == null) { + return; + } + + // Intersect boundsInWindow with the window to make sure it's not outside the window + // requesting the token. Offset the window bounds to 0,0 since the boundsInWindow are + // offset from the window location, not display. + final Rect windowBounds = new Rect(); + win.getBounds(windowBounds); + windowBounds.offsetTo(0, 0); + outBounds.intersectUnchecked(windowBounds); + + if (DEBUG) { + Slog.d(TAG, "calculateImpressionTokenBounds: boundsIntersectWindow=" + outBounds); + } + + if (outBounds.isEmpty()) { + return; + } + + // Transform the bounds using the window transform in case there's a scale or offset. + // This allows the bounds to be in display space. + win.getTransformationMatrix(mTmpFloat9, mTmpMatrix); + mTmpRectF.set(outBounds); + mTmpMatrix.mapRect(mTmpRectF, mTmpRectF); + outBounds.set((int) mTmpRectF.left, (int) mTmpRectF.top, (int) mTmpRectF.right, + (int) mTmpRectF.bottom); + if (DEBUG) { + Slog.d(TAG, "calculateImpressionTokenBounds: boundsInDisplay=" + outBounds); + } + + // Apply the magnification spec values to the bounds since the content could be magnified + final MagnificationSpec magSpec = displayContent.getMagnificationSpec(); + if (magSpec != null) { + outBounds.scale(magSpec.scale); + outBounds.offset((int) magSpec.offsetX, (int) magSpec.offsetY); + } + + if (DEBUG) { + Slog.d(TAG, "calculateImpressionTokenBounds: boundsWithMagnification=" + outBounds); + } + + if (outBounds.isEmpty()) { + return; + } + + // Intersect with the display bounds since it shouldn't take a screenshot of content + // outside the display since it's not visible to the user. + final Rect displayBounds = displayContent.getBounds(); + outBounds.intersectUnchecked(displayBounds); + if (DEBUG) { + Slog.d(TAG, "calculateImpressionTokenBounds: finalBounds=" + outBounds); + } + } + + /** * Run a command, starting the service connection if necessary. */ private void connectAndRun(@NonNull Command command) { diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index b49d83d93d54..25d779f0fb33 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -40,6 +40,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT; @@ -655,6 +656,7 @@ final class InputMonitor { || type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_DOCK_DIVIDER || type == TYPE_ACCESSIBILITY_OVERLAY - || type == TYPE_INPUT_CONSUMER; + || type == TYPE_INPUT_CONSUMER + || type == TYPE_VOICE_INTERACTION; } } diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index 9d70fd771f31..752d6b4fc908 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -496,10 +496,7 @@ class InsetsStateController { } void notifyInsetsChanged() { - mDisplayContent.forAllWindows(mDispatchInsetsChanged, true /* traverseTopToBottom */); - if (mDisplayContent.mRemoteInsetsControlTarget != null) { - mDisplayContent.mRemoteInsetsControlTarget.notifyInsetsChanged(); - } + mDisplayContent.notifyInsetsChanged(mDispatchInsetsChanged); } void dump(String prefix, PrintWriter pw) { diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java index 2f8cfdd7002c..391e6598dca2 100644 --- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java +++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java @@ -27,26 +27,24 @@ import android.util.ArraySet; import android.util.AtomicFile; import android.util.Slog; import android.util.SparseArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import android.view.DisplayInfo; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.FastXmlSerializer; import com.android.server.LocalServices; import com.android.server.pm.PackageList; import com.android.server.wm.LaunchParamsController.LaunchParams; -import libcore.io.IoUtils; - import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlSerializer; -import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; -import java.io.FileReader; import java.io.IOException; -import java.io.StringWriter; +import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -195,12 +193,9 @@ class LaunchParamsPersister { continue; } - BufferedReader reader = null; - try { - reader = new BufferedReader(new FileReader(paramsFile)); + try (InputStream in = new FileInputStream(paramsFile)) { final PersistableLaunchParams params = new PersistableLaunchParams(); - final XmlPullParser parser = Xml.newPullParser(); - parser.setInput(reader); + final TypedXmlPullParser parser = Xml.resolvePullParser(in); int event; while ((event = parser.next()) != XmlPullParser.END_DOCUMENT && event != XmlPullParser.END_TAG) { @@ -223,8 +218,6 @@ class LaunchParamsPersister { } catch (Exception e) { Slog.w(TAG, "Failed to restore launch params for " + name, e); filesToDelete.add(paramsFile); - } finally { - IoUtils.closeQuietly(reader); } } @@ -410,12 +403,11 @@ class LaunchParamsPersister { mLaunchParams = launchParams; } - private StringWriter saveParamsToXml() { - final StringWriter writer = new StringWriter(); - final XmlSerializer serializer = new FastXmlSerializer(); - + private byte[] saveParamsToXml() { try { - serializer.setOutput(writer); + final ByteArrayOutputStream os = new ByteArrayOutputStream(); + final TypedXmlSerializer serializer = Xml.resolveSerializer(os); + serializer.startDocument(/* encoding */ null, /* standalone */ true); serializer.startTag(null, TAG_LAUNCH_PARAMS); @@ -425,7 +417,7 @@ class LaunchParamsPersister { serializer.endDocument(); serializer.flush(); - return writer; + return os.toByteArray(); } catch (IOException e) { return null; } @@ -433,7 +425,7 @@ class LaunchParamsPersister { @Override public void process() { - final StringWriter writer = saveParamsToXml(); + final byte[] data = saveParamsToXml(); final File launchParamFolder = getLaunchParamFolder(mUserId); if (!launchParamFolder.isDirectory() && !launchParamFolder.mkdirs()) { @@ -447,7 +439,7 @@ class LaunchParamsPersister { FileOutputStream stream = null; try { stream = atomicFile.startWrite(); - stream.write(writer.toString().getBytes()); + stream.write(data); } catch (Exception e) { Slog.e(TAG, "Failed to write param file for " + mComponentName, e); if (stream != null) { @@ -513,17 +505,16 @@ class LaunchParamsPersister { */ long mTimestamp; - void saveToXml(XmlSerializer serializer) throws IOException { + void saveToXml(TypedXmlSerializer serializer) throws IOException { serializer.attribute(null, ATTR_DISPLAY_UNIQUE_ID, mDisplayUniqueId); - serializer.attribute(null, ATTR_WINDOWING_MODE, - Integer.toString(mWindowingMode)); + serializer.attributeInt(null, ATTR_WINDOWING_MODE, mWindowingMode); serializer.attribute(null, ATTR_BOUNDS, mBounds.flattenToString()); if (mWindowLayoutAffinity != null) { serializer.attribute(null, ATTR_WINDOW_LAYOUT_AFFINITY, mWindowLayoutAffinity); } } - void restore(File xmlFile, XmlPullParser parser) { + void restore(File xmlFile, TypedXmlPullParser parser) { for (int i = 0; i < parser.getAttributeCount(); ++i) { final String attrValue = parser.getAttributeValue(i); switch (parser.getAttributeName(i)) { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 707354823817..cbeaecf7f58c 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -1804,10 +1804,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> Configuration config = null; if (displayContent != null) { config = displayContent.updateOrientation( - getDisplayOverrideConfiguration(displayId), - starting != null && starting.mayFreezeScreenLocked() - ? starting.appToken : null, - true /* forceUpdate */); + getDisplayOverrideConfiguration(displayId), starting, true /* forceUpdate */); } // Visibilities may change so let the starting activity have a chance to report. Can't do it // when visibility is changed in each AppWindowToken because it may trigger wrong diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index b46e796698e4..c414c6421dc8 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -38,7 +38,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITION import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.Nullable; -import android.app.ActivityManagerInternal; import android.app.PendingIntent; import android.content.ClipData; import android.content.ClipDescription; @@ -56,6 +55,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.Trace; import android.os.UserHandle; +import android.service.attestation.ImpressionToken; import android.text.TextUtils; import android.util.ArraySet; import android.util.MergedConfiguration; @@ -848,4 +848,15 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { Binder.restoreCallingIdentity(identity); } } + + @Override + public ImpressionToken generateImpressionToken(IWindow window, Rect boundsInWindow, + String hashAlgorithm) { + final long origId = Binder.clearCallingIdentity(); + try { + return mService.generateImpressionToken(this, window, boundsInWindow, hashAlgorithm); + } finally { + Binder.restoreCallingIdentity(origId); + } + } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index ebf5989d6b55..fb441fa94893 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -196,6 +196,8 @@ import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.Log; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; import android.view.DisplayInfo; import android.view.RemoteAnimationAdapter; @@ -2249,7 +2251,7 @@ class Task extends WindowContainer<WindowContainer> { // the rotation animation needs to capture snapshot earlier to avoid animating from // an intermediate state. if (oldOrientation != getOrientation()) { - onDescendantOrientationChanged(null, this); + onDescendantOrientationChanged(this); } } finally { if (pipChanging) { @@ -3305,9 +3307,8 @@ class Task extends WindowContainer<WindowContainer> { } @Override - public boolean onDescendantOrientationChanged(IBinder freezeDisplayToken, - WindowContainer requestingContainer) { - if (super.onDescendantOrientationChanged(freezeDisplayToken, requestingContainer)) { + public boolean onDescendantOrientationChanged(WindowContainer requestingContainer) { + if (super.onDescendantOrientationChanged(requestingContainer)) { return true; } @@ -4099,6 +4100,7 @@ class Task extends WindowContainer<WindowContainer> { ? rootTask.mTaskId : INVALID_TASK_ID; info.isFocused = isFocused(); + info.isVisible = hasVisibleChildren(); } @Nullable PictureInPictureParams getPictureInPictureParams() { @@ -4483,14 +4485,14 @@ class Task extends WindowContainer<WindowContainer> { /** * Saves this {@link Task} to XML using given serializer. */ - void saveToXml(XmlSerializer out) throws Exception { + void saveToXml(TypedXmlSerializer out) throws Exception { if (DEBUG_RECENTS) Slog.i(TAG_RECENTS, "Saving task=" + this); - out.attribute(null, ATTR_TASKID, String.valueOf(mTaskId)); + out.attributeInt(null, ATTR_TASKID, mTaskId); if (realActivity != null) { out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString()); } - out.attribute(null, ATTR_REALACTIVITY_SUSPENDED, String.valueOf(realActivitySuspended)); + out.attributeBoolean(null, ATTR_REALACTIVITY_SUSPENDED, realActivitySuspended); if (origActivity != null) { out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString()); } @@ -4509,37 +4511,36 @@ class Task extends WindowContainer<WindowContainer> { if (mWindowLayoutAffinity != null) { out.attribute(null, ATTR_WINDOW_LAYOUT_AFFINITY, mWindowLayoutAffinity); } - out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset)); - out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents)); - out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode)); - out.attribute(null, ATTR_USERID, String.valueOf(mUserId)); - out.attribute(null, ATTR_USER_SETUP_COMPLETE, String.valueOf(mUserSetupComplete)); - out.attribute(null, ATTR_EFFECTIVE_UID, String.valueOf(effectiveUid)); - out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved)); - out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity)); + out.attributeBoolean(null, ATTR_ROOTHASRESET, rootWasReset); + out.attributeBoolean(null, ATTR_AUTOREMOVERECENTS, autoRemoveRecents); + out.attributeBoolean(null, ATTR_ASKEDCOMPATMODE, askedCompatMode); + out.attributeInt(null, ATTR_USERID, mUserId); + out.attributeBoolean(null, ATTR_USER_SETUP_COMPLETE, mUserSetupComplete); + out.attributeInt(null, ATTR_EFFECTIVE_UID, effectiveUid); + out.attributeLong(null, ATTR_LASTTIMEMOVED, mLastTimeMoved); + out.attributeBoolean(null, ATTR_NEVERRELINQUISH, mNeverRelinquishIdentity); if (lastDescription != null) { out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString()); } if (getTaskDescription() != null) { getTaskDescription().saveToXml(out); } - out.attribute(null, ATTR_TASK_AFFILIATION, String.valueOf(mAffiliatedTaskId)); - out.attribute(null, ATTR_PREV_AFFILIATION, String.valueOf(mPrevAffiliateTaskId)); - out.attribute(null, ATTR_NEXT_AFFILIATION, String.valueOf(mNextAffiliateTaskId)); - out.attribute(null, ATTR_CALLING_UID, String.valueOf(mCallingUid)); + out.attributeInt(null, ATTR_TASK_AFFILIATION, mAffiliatedTaskId); + out.attributeInt(null, ATTR_PREV_AFFILIATION, mPrevAffiliateTaskId); + out.attributeInt(null, ATTR_NEXT_AFFILIATION, mNextAffiliateTaskId); + out.attributeInt(null, ATTR_CALLING_UID, mCallingUid); out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage); out.attribute(null, ATTR_CALLING_FEATURE_ID, mCallingFeatureId == null ? "" : mCallingFeatureId); - out.attribute(null, ATTR_RESIZE_MODE, String.valueOf(mResizeMode)); - out.attribute(null, ATTR_SUPPORTS_PICTURE_IN_PICTURE, - String.valueOf(mSupportsPictureInPicture)); + out.attributeInt(null, ATTR_RESIZE_MODE, mResizeMode); + out.attributeBoolean(null, ATTR_SUPPORTS_PICTURE_IN_PICTURE, mSupportsPictureInPicture); if (mLastNonFullscreenBounds != null) { out.attribute( null, ATTR_NON_FULLSCREEN_BOUNDS, mLastNonFullscreenBounds.flattenToString()); } - out.attribute(null, ATTR_MIN_WIDTH, String.valueOf(mMinWidth)); - out.attribute(null, ATTR_MIN_HEIGHT, String.valueOf(mMinHeight)); - out.attribute(null, ATTR_PERSIST_TASK_VERSION, String.valueOf(PERSIST_TASK_VERSION)); + out.attributeInt(null, ATTR_MIN_WIDTH, mMinWidth); + out.attributeInt(null, ATTR_MIN_HEIGHT, mMinHeight); + out.attributeInt(null, ATTR_PERSIST_TASK_VERSION, PERSIST_TASK_VERSION); if (affinityIntent != null) { out.startTag(null, TAG_AFFINITYINTENT); @@ -4564,7 +4565,7 @@ class Task extends WindowContainer<WindowContainer> { } private static boolean saveActivityToXml( - ActivityRecord r, ActivityRecord first, XmlSerializer out) { + ActivityRecord r, ActivityRecord first, TypedXmlSerializer out) { if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() || ((r.intent.getFlags() & FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_RETAIN_IN_RECENTS) == FLAG_ACTIVITY_NEW_DOCUMENT) @@ -4583,7 +4584,7 @@ class Task extends WindowContainer<WindowContainer> { } } - static Task restoreFromXml(XmlPullParser in, ActivityTaskSupervisor taskSupervisor) + static Task restoreFromXml(TypedXmlPullParser in, ActivityTaskSupervisor taskSupervisor) throws IOException, XmlPullParserException { Intent intent = null; Intent affinityIntent = null; @@ -5764,6 +5765,10 @@ class Task extends WindowContainer<WindowContainer> { starting, configChanges, preserveWindows, notifyClients, userLeaving), true /* traverseTopToBottom */); + // Notify WM shell that task visibilities may have changed + forAllTasks(task -> task.dispatchTaskInfoChangedIfNeeded(/* force */ false), + true /* traverseTopToBottom */); + if (mTranslucentActivityWaiting != null && mUndrawnActivitiesBelowTopTranslucent.isEmpty()) { // Nothing is getting drawn or everything was already visible, don't wait for diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java index c976bc207061..855dd7e416b7 100644 --- a/services/core/java/com/android/server/wm/TaskPersister.java +++ b/services/core/java/com/android/server/wm/TaskPersister.java @@ -30,26 +30,28 @@ import android.util.AtomicFile; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlSerializer; import java.io.BufferedReader; import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; -import java.io.StringWriter; +import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -305,12 +307,9 @@ public class TaskPersister implements PersisterQueue.Listener { continue; } - BufferedReader reader = null; boolean deleteFile = false; - try { - reader = new BufferedReader(new FileReader(taskFile)); - final XmlPullParser in = Xml.newPullParser(); - in.setInput(reader); + try (InputStream is = new FileInputStream(taskFile)) { + final TypedXmlPullParser in = Xml.resolvePullParser(is); int event; while (((event = in.next()) != XmlPullParser.END_DOCUMENT) && @@ -360,7 +359,6 @@ public class TaskPersister implements PersisterQueue.Listener { Slog.e(TAG, "Failing file: " + fileToString(taskFile)); deleteFile = true; } finally { - IoUtils.closeQuietly(reader); if (deleteFile) { if (DEBUG) Slog.d(TAG, "Deleting file=" + taskFile.getName()); taskFile.delete(); @@ -513,11 +511,10 @@ public class TaskPersister implements PersisterQueue.Listener { mService = service; } - private StringWriter saveToXml(Task task) throws Exception { + private byte[] saveToXml(Task task) throws Exception { if (DEBUG) Slog.d(TAG, "saveToXml: task=" + task); - final XmlSerializer xmlSerializer = new FastXmlSerializer(); - StringWriter stringWriter = new StringWriter(); - xmlSerializer.setOutput(stringWriter); + final ByteArrayOutputStream os = new ByteArrayOutputStream(); + final TypedXmlSerializer xmlSerializer = Xml.resolveSerializer(os); if (DEBUG) { xmlSerializer.setFeature( @@ -534,13 +531,13 @@ public class TaskPersister implements PersisterQueue.Listener { xmlSerializer.endDocument(); xmlSerializer.flush(); - return stringWriter; + return os.toByteArray(); } @Override public void process() { // Write out one task. - StringWriter stringWriter = null; + byte[] data = null; Task task = mTask; if (DEBUG) Slog.d(TAG, "Writing task=" + task); synchronized (mService.mGlobalLock) { @@ -548,12 +545,12 @@ public class TaskPersister implements PersisterQueue.Listener { // Still there. try { if (DEBUG) Slog.d(TAG, "Saving task=" + task); - stringWriter = saveToXml(task); + data = saveToXml(task); } catch (Exception e) { } } } - if (stringWriter != null) { + if (data != null) { // Write out xml file while not holding mService lock. FileOutputStream file = null; AtomicFile atomicFile = null; @@ -567,8 +564,7 @@ public class TaskPersister implements PersisterQueue.Listener { atomicFile = new AtomicFile(new File(userTasksDir, String.valueOf(task.mTaskId) + TASK_FILENAME_SUFFIX)); file = atomicFile.startWrite(); - file.write(stringWriter.toString().getBytes()); - file.write('\n'); + file.write(data); atomicFile.finishWrite(file); } catch (IOException e) { if (file != null) { diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index a1bb89d26f2f..cf6468d66b57 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -1139,18 +1139,15 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * Called when this container or one of its descendants changed its requested orientation, and * wants this container to handle it or pass it to its parent. * - * @param freezeDisplayToken freeze this app window token if display needs to freeze * @param requestingContainer the container which orientation request has changed * @return {@code true} if handled; {@code false} otherwise. */ - boolean onDescendantOrientationChanged(@Nullable IBinder freezeDisplayToken, - @Nullable WindowContainer requestingContainer) { + boolean onDescendantOrientationChanged(@Nullable WindowContainer requestingContainer) { final WindowContainer parent = getParent(); if (parent == null) { return false; } - return parent.onDescendantOrientationChanged(freezeDisplayToken, - requestingContainer); + return parent.onDescendantOrientationChanged(requestingContainer); } /** @@ -1224,14 +1221,13 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } /** - * Calls {@link #setOrientation(int, IBinder, ActivityRecord)} with {@code null} to the last 2 + * Calls {@link #setOrientation(int, IBinder, WindowContainer)} with {@code null} to the last 2 * parameters. * * @param orientation the specified orientation. */ void setOrientation(int orientation) { - setOrientation(orientation, null /* freezeDisplayToken */, - null /* ActivityRecord */); + setOrientation(orientation, null /* requestingContainer */); } /** @@ -1240,14 +1236,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * * @param orientation the specified orientation. Needs to be one of {@link * android.content.pm.ActivityInfo.ScreenOrientation}. - * @param freezeDisplayToken uses this token to freeze display if orientation change is not - * done. Display will not be frozen if this is {@code null}, which - * should only happen in tests. * @param requestingContainer the container which orientation request has changed. Mostly used * to ensure it gets correct configuration. */ - void setOrientation(int orientation, @Nullable IBinder freezeDisplayToken, - @Nullable WindowContainer requestingContainer) { + void setOrientation(int orientation, @Nullable WindowContainer requestingContainer) { if (mOrientation == orientation) { return; } @@ -1259,7 +1251,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< // Resolve the requested orientation. onConfigurationChanged(parent.getConfiguration()); } - onDescendantOrientationChanged(freezeDisplayToken, requestingContainer); + onDescendantOrientationChanged(requestingContainer); } } diff --git a/services/core/java/com/android/server/wm/WindowManagerConstants.java b/services/core/java/com/android/server/wm/WindowManagerConstants.java index b0c5dbc6cca3..a5ebf9ac74b9 100644 --- a/services/core/java/com/android/server/wm/WindowManagerConstants.java +++ b/services/core/java/com/android/server/wm/WindowManagerConstants.java @@ -23,7 +23,7 @@ import android.provider.AndroidDeviceConfig; import android.provider.DeviceConfig; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.wm.utils.DeviceConfigInterface; +import com.android.server.utils.DeviceConfigInterface; import java.io.PrintWriter; import java.util.Objects; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 80b021650c83..25049ae6d8b5 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -48,6 +48,7 @@ import static android.provider.Settings.Global.DEVELOPMENT_IGNORE_VENDOR_DISPLAY import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; +import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; @@ -78,7 +79,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; -import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_RELAUNCH; import static android.view.WindowManagerGlobal.ADD_OKAY; @@ -199,6 +199,7 @@ import android.os.Trace; import android.os.UserHandle; import android.os.WorkSource; import android.provider.Settings; +import android.service.attestation.ImpressionToken; import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; import android.sysprop.SurfaceFlingerProperties; @@ -256,9 +257,9 @@ import android.view.View; import android.view.WindowContentFrameStats; import android.view.WindowInsets; import android.view.WindowManager; +import android.view.WindowManager.DisplayImePolicy; import android.view.WindowManager.LayoutParams; import android.view.WindowManager.RemoveContentMode; -import android.view.WindowManager.DisplayImePolicy; import android.view.WindowManagerGlobal; import android.view.WindowManagerPolicyConstants.PointerEventListener; import android.window.ClientWindowFrames; @@ -288,8 +289,8 @@ import com.android.server.input.InputManagerService; import com.android.server.policy.WindowManagerPolicy; import com.android.server.policy.WindowManagerPolicy.ScreenOffListener; import com.android.server.power.ShutdownThread; +import com.android.server.utils.DeviceConfigInterface; import com.android.server.utils.PriorityDump; -import com.android.server.wm.utils.DeviceConfigInterface; import java.io.BufferedWriter; import java.io.DataInputStream; @@ -767,6 +768,8 @@ public class WindowManagerService extends IWindowManager.Stub final EmbeddedWindowController mEmbeddedWindowController; final AnrController mAnrController; + private final ImpressionAttestationController mImpressionAttestationController; + @VisibleForTesting final class SettingsObserver extends ContentObserver { private final Uri mDisplayInversionEnabledUri = @@ -1367,6 +1370,7 @@ public class WindowManagerService extends IWindowManager.Stub mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources( mContext.getResources()); + mImpressionAttestationController = new ImpressionAttestationController(mContext); setGlobalShadowSettings(); mAnrController = new AnrController(this); mStartingSurfaceController = new StartingSurfaceController(this); @@ -1627,7 +1631,7 @@ public class WindowManagerService extends IWindowManager.Stub } final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); - displayPolicy.adjustWindowParamsLw(win, win.mAttrs, callingPid, callingUid); + displayPolicy.adjustWindowParamsLw(win, win.mAttrs); win.updateRequestedVisibility(requestedVisibility); res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid); @@ -2197,7 +2201,7 @@ public class WindowManagerService extends IWindowManager.Stub int flagChanges = 0; int privateFlagChanges = 0; if (attrs != null) { - displayPolicy.adjustWindowParamsLw(win, attrs, pid, uid); + displayPolicy.adjustWindowParamsLw(win, attrs); win.mToken.adjustWindowParams(win, attrs); int disableFlags = (attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility) & DISABLE_MASK; @@ -8428,4 +8432,64 @@ public class WindowManagerService extends IWindowManager.Stub SystemClock.sleep(durationMs); } } + + @Override + public String[] getSupportedImpressionAlgorithms() { + return mImpressionAttestationController.getSupportedImpressionAlgorithms(); + } + + @Override + public boolean verifyImpressionToken(ImpressionToken impressionToken) { + return mImpressionAttestationController.verifyImpressionToken(impressionToken); + } + + ImpressionToken generateImpressionToken(Session session, IWindow window, + Rect boundsInWindow, String hashAlgorithm) { + final SurfaceControl displaySurfaceControl; + final Rect boundsInDisplay = new Rect(boundsInWindow); + synchronized (mGlobalLock) { + final WindowState win = windowForClientLocked(session, window, false); + if (win == null) { + Slog.w(TAG, "Failed to generate impression token. Invalid window"); + return null; + } + + DisplayContent displayContent = win.getDisplayContent(); + if (displayContent == null) { + Slog.w(TAG, "Failed to generate impression token. Window is not on a display"); + return null; + } + + displaySurfaceControl = displayContent.getSurfaceControl(); + mImpressionAttestationController.calculateImpressionTokenBoundsLocked(win, + boundsInWindow, boundsInDisplay); + + if (boundsInDisplay.isEmpty()) { + Slog.w(TAG, "Failed to generate impression token. Bounds are not on screen"); + return null; + } + } + + // A screenshot of the entire display is taken rather than just the window. This is + // because if we take a screenshot of the window, it will not include content that might + // be covering it with the same uid. We want to make sure we include content that's + // covering to ensure we get as close as possible to what the user sees + final int uid = session.mUid; + SurfaceControl.LayerCaptureArgs args = + new SurfaceControl.LayerCaptureArgs.Builder(displaySurfaceControl) + .setUid(uid) + .setSourceCrop(boundsInDisplay) + .build(); + + SurfaceControl.ScreenshotHardwareBuffer screenshotHardwareBuffer = + SurfaceControl.captureLayers(args); + if (screenshotHardwareBuffer == null + || screenshotHardwareBuffer.getHardwareBuffer() == null) { + Slog.w(TAG, "Failed to generate impression token. Failed to take screenshot"); + return null; + } + + return mImpressionAttestationController.generateImpressionToken( + screenshotHardwareBuffer.getHardwareBuffer(), boundsInWindow, hashAlgorithm); + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java index d29534e8080e..1a147b9f73e4 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java @@ -98,6 +98,8 @@ class ActiveAdmin { private static final String TAG_PASSWORD_HISTORY_LENGTH = "password-history-length"; private static final String TAG_MIN_PASSWORD_LENGTH = "min-password-length"; private static final String TAG_PASSWORD_QUALITY = "password-quality"; + private static final String TAG_PASSWORD_QUALITY_APPLIES_TO_PARENT = + "password-quality-applies-parent"; private static final String TAG_POLICIES = "policies"; private static final String TAG_CROSS_PROFILE_WIDGET_PROVIDERS = "cross-profile-widget-providers"; @@ -143,6 +145,7 @@ class ActiveAdmin { @NonNull PasswordPolicy mPasswordPolicy = new PasswordPolicy(); + boolean mPasswordPolicyAppliesToParent = true; @DevicePolicyManager.PasswordComplexity int mPasswordComplexity = PASSWORD_COMPLEXITY_NONE; @@ -333,6 +336,9 @@ class ActiveAdmin { writeAttributeValueToXml( out, TAG_MIN_PASSWORD_NONLETTER, mPasswordPolicy.nonLetter); } + + writeAttributeValueToXml(out, TAG_PASSWORD_QUALITY_APPLIES_TO_PARENT, + mPasswordPolicyAppliesToParent); } if (passwordHistoryLength != DEF_PASSWORD_HISTORY_LENGTH) { writeAttributeValueToXml( @@ -626,6 +632,9 @@ class ActiveAdmin { } else if (TAG_MIN_PASSWORD_NONLETTER.equals(tag)) { mPasswordPolicy.nonLetter = Integer.parseInt( parser.getAttributeValue(null, ATTR_VALUE)); + } else if (TAG_PASSWORD_QUALITY_APPLIES_TO_PARENT.equals(tag)) { + mPasswordPolicyAppliesToParent = Boolean.parseBoolean( + parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_MAX_TIME_TO_UNLOCK.equals(tag)) { maximumTimeToUnlock = Long.parseLong( parser.getAttributeValue(null, ATTR_VALUE)); @@ -995,6 +1004,9 @@ class ActiveAdmin { pw.print("minimumPasswordNonLetter="); pw.println(mPasswordPolicy.nonLetter); + pw.print("passwordPolicyAppliesToParent="); + pw.println(mPasswordPolicyAppliesToParent); + pw.print("maximumTimeToUnlock="); pw.println(maximumTimeToUnlock); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index ce61d50df1d9..05b1e4253bc0 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -101,4 +101,9 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { public boolean canProfileOwnerResetPasswordWhenLocked(int userId) { return false; } + + public boolean hasKeyPair(String callerPackage, String alias) { + // STOPSHIP: implement delegation code in ArcDevicePolicyManagerWrapperService & nuke this. + return false; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 6ca27b597987..83b4c823e4c5 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -155,10 +155,12 @@ import android.app.admin.SystemUpdateInfo; import android.app.admin.SystemUpdatePolicy; import android.app.admin.UnsafeStateException; import android.app.backup.IBackupManager; +import android.app.compat.CompatChanges; import android.app.trust.TrustManager; import android.app.usage.UsageStatsManagerInternal; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; +import android.compat.annotation.EnabledSince; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -527,6 +529,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) private static final long USE_SET_LOCATION_ENABLED = 117835097L; + /** + * Admin apps targeting Android S+ may not use + * {@link android.app.admin.DevicePolicyManager#setPasswordQuality} to set password quality + * on the {@code DevicePolicyManager} instance obtained by calling + * {@link android.app.admin.DevicePolicyManager#getParentProfileInstance}. + * Instead, they should use + * {@link android.app.admin.DevicePolicyManager#setRequiredPasswordComplexity} to set + * coarse-grained password requirements device-wide. + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S) + private static final long PREVENT_SETTING_PASSWORD_QUALITY_ON_PARENT = 165573442L; + final Context mContext; final Injector mInjector; final IPackageManager mIPackageManager; @@ -1396,6 +1411,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public long systemCurrentTimeMillis() { return System.currentTimeMillis(); } + + public boolean isChangeEnabled(long changeId, String packageName, int userId) { + return CompatChanges.isChangeEnabled(changeId, packageName, UserHandle.of(userId)); + } } /** @@ -3315,6 +3334,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { getTargetSdk(profileOwner.getPackageName(), userHandle) > Build.VERSION_CODES.M; } + private boolean canSetPasswordQualityOnParent(String packageName, int userId) { + return !mInjector.isChangeEnabled( + PREVENT_SETTING_PASSWORD_QUALITY_ON_PARENT, packageName, userId); + } + @Override public void setPasswordQuality(ComponentName who, int quality, boolean parent) { if (!mHasFeature) { @@ -3323,7 +3347,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); validateQualityConstant(quality); - final int userId = mInjector.userHandleGetCallingUserId(); + final CallerIdentity caller = getCallerIdentity(who); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDeviceOwner(caller) || isSystemUid(caller)); + + final boolean qualityMayApplyToParent = + canSetPasswordQualityOnParent(who.getPackageName(), caller.getUserId()); + if (!qualityMayApplyToParent) { + Preconditions.checkArgument(!parent, + "Profile Owner may not apply password quality requirements device-wide"); + } + + final int userId = caller.getUserId(); synchronized (getLockObject()) { ActiveAdmin ap = getActiveAdminForCallerLocked( who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent); @@ -3332,6 +3367,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (passwordPolicy.quality != quality) { passwordPolicy.quality = quality; ap.mPasswordComplexity = PASSWORD_COMPLEXITY_NONE; + ap.mPasswordPolicyAppliesToParent = qualityMayApplyToParent; resetInactivePasswordRequirementsIfRPlus(userId, ap); updatePasswordValidityCheckpointLocked(userId, parent); updatePasswordQualityCacheForUserGroup(userId); @@ -4063,7 +4099,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { synchronized (getLockObject()) { List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(userId); for (ActiveAdmin admin : admins) { - adminMetrics.add(admin.mPasswordPolicy.getMinMetrics()); + final boolean isAdminOfUser = userId == admin.getUserHandle().getIdentifier(); + // Use the password metrics from the admin in one of three cases: + // (1) The admin is of the user we're getting the minimum metrics for. The admin + // always affects the user it's managing. This applies also to the parent + // ActiveAdmin instance: It'd have the same user handle. + // (2) The mPasswordPolicyAppliesToParent field is true: That indicates the + // call to setPasswordQuality was made by an admin that may affect the parent. + if (isAdminOfUser || admin.mPasswordPolicyAppliesToParent) { + adminMetrics.add(admin.mPasswordPolicy.getMinMetrics()); + } } } return PasswordMetrics.merge(adminMetrics); @@ -4155,13 +4200,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { /* shouldIncludeProfileAdmins */ (user) -> user.id == profileUser || !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id)); ArrayList<PasswordMetrics> adminMetrics = new ArrayList<>(admins.size()); + int maxRequiredComplexity = PASSWORD_COMPLEXITY_NONE; for (ActiveAdmin admin : admins) { adminMetrics.add(admin.mPasswordPolicy.getMinMetrics()); + maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity); } - //TODO: Take complexity into account, would need to take complexity from all admins - //in the admins list. return PasswordMetrics.validatePasswordMetrics(PasswordMetrics.merge(adminMetrics), - PASSWORD_COMPLEXITY_NONE, false, metrics).isEmpty(); + maxRequiredComplexity, false, metrics).isEmpty(); } } @@ -4255,28 +4300,33 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { admin.mPasswordComplexity = passwordComplexity; // Reset the password policy. admin.mPasswordPolicy = new PasswordPolicy(); + admin.mPasswordPolicyAppliesToParent = true; updatePasswordValidityCheckpointLocked(caller.getUserId(), calledOnParent); updatePasswordQualityCacheForUserGroup(caller.getUserId()); saveSettingsLocked(caller.getUserId()); - //TODO: Log password complexity change if security logging is enabled. }); } + logPasswordComplexityRequiredIfSecurityLogEnabled(admin.info.getComponent(), + caller.getUserId(), calledOnParent, passwordComplexity); } //TODO: Log metrics. } + private void logPasswordComplexityRequiredIfSecurityLogEnabled(ComponentName who, int userId, + boolean parent, int complexity) { + if (SecurityLog.isLoggingEnabled()) { + final int affectedUserId = parent ? getProfileParentId(userId) : userId; + SecurityLog.writeEvent(SecurityLog.TAG_PASSWORD_COMPLEXITY_REQUIRED, + who.getPackageName(), userId, affectedUserId, complexity); + } + } + private int getEffectivePasswordComplexityRequirementLocked(@UserIdInt int userHandle) { ensureLocked(); List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle); int maxRequiredComplexity = PASSWORD_COMPLEXITY_NONE; for (ActiveAdmin admin : admins) { - final ComponentName adminComponent = admin.info.getComponent(); - final int adminUser = admin.getUserHandle().getIdentifier(); - // Password complexity is only taken into account from DO/PO - if (isDeviceOwner(adminComponent, adminUser) - || isProfileOwnerUncheckedLocked(adminComponent, adminUser)) { - maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity); - } + maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity); } return maxRequiredComplexity; } @@ -4301,6 +4351,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override + public int getAggregatedPasswordComplexityForUser(int userId) { + if (!mHasFeature) { + return PASSWORD_COMPLEXITY_NONE; + } + + final CallerIdentity caller = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId)); + + synchronized (getLockObject()) { + return getEffectivePasswordComplexityRequirementLocked(userId); + } + } + + + @Override public int getCurrentFailedPasswordAttempts(int userHandle, boolean parent) { if (!mLockPatternUtils.hasSecureLockScreen()) { return 0; @@ -5046,6 +5111,30 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override + public boolean hasKeyPair(String callerPackage, String alias) { + final CallerIdentity caller = getCallerIdentity(callerPackage); + Preconditions.checkCallAuthorization(canManageCertificates(caller)); + + return mInjector.binderWithCleanCallingIdentity(() -> { + try (KeyChainConnection keyChainConnection = + KeyChain.bindAsUser(mContext, caller.getUserHandle())) { + return keyChainConnection.getService().containsKeyPair(alias); + } catch (RemoteException e) { + Log.e(LOG_TAG, "Querying keypair", e); + } catch (InterruptedException e) { + Log.w(LOG_TAG, "Interrupted while querying keypair", e); + Thread.currentThread().interrupt(); + } + return false; + }); + } + + private boolean canManageCertificates(CallerIdentity caller) { + return isProfileOwner(caller) || isDeviceOwner(caller) + || isCallerDelegate(caller, DELEGATION_CERT_INSTALL); + } + + @Override public boolean setKeyGrantForApp(ComponentName who, String callerPackage, String alias, String packageName, boolean hasGrant) { Preconditions.checkStringNotEmpty(alias, "Alias to grant cannot be empty"); @@ -5123,9 +5212,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { */ if (hasProfileOwner(caller.getUserId())) { // Make sure that the caller is the profile owner or delegate. - Preconditions.checkCallAuthorization( - isDeviceOwner(caller) || isProfileOwner(caller) || isCallerDelegate( - caller, DELEGATION_CERT_INSTALL)); + Preconditions.checkCallAuthorization(canManageCertificates(caller)); // Verify that the managed profile is on an organization-owned device and as such // the profile owner can access Device IDs. if (isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId())) { diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java index e48b67167cdf..9f895c678dd0 100644 --- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java @@ -35,6 +35,8 @@ import android.os.Handler; import android.os.HandlerThread; import android.util.Log; import android.util.SparseArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import androidx.test.InstrumentationRegistry; @@ -181,7 +183,7 @@ public class AppOpsUpgradeTest { boolean parse() { try (FileInputStream stream = new FileInputStream(mFile)) { - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(stream, StandardCharsets.UTF_8.name()); int type; while ((type = parser.next()) != XmlPullParser.START_TAG diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 0c2fab83ee66..343b156e443a 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -51,7 +51,6 @@ android_test { "testng", "junit", "platform-compat-test-rules", - ], aidl: { @@ -117,6 +116,7 @@ java_library { "utils/**/*.java", "utils/**/*.kt", "utils-mockito/**/*.kt", + ":services.core-sources-deviceconfig-interface", ], static_libs: [ "junit", @@ -133,6 +133,7 @@ java_library { "utils/**/*.java", "utils/**/*.kt", "utils-mockito/**/*.kt", + ":services.core-sources-deviceconfig-interface", ], static_libs: [ "junit", diff --git a/services/tests/servicestests/res/xml/usertypes_test_full.xml b/services/tests/servicestests/res/xml/usertypes_test_full.xml index a281dcaafb3d..099ccbe5b5f6 100644 --- a/services/tests/servicestests/res/xml/usertypes_test_full.xml +++ b/services/tests/servicestests/res/xml/usertypes_test_full.xml @@ -22,4 +22,7 @@ <item res='@*android:color/profile_badge_1' /> </badge-colors> </full-type> + + <change-user-type from="android.old.name" to="android.test.1" whenVersionLeq="1" /> + </user-types> diff --git a/services/tests/servicestests/res/xml/usertypes_test_profile.xml b/services/tests/servicestests/res/xml/usertypes_test_profile.xml index b6c8fbdcdd20..daa7d7b7341a 100644 --- a/services/tests/servicestests/res/xml/usertypes_test_profile.xml +++ b/services/tests/servicestests/res/xml/usertypes_test_profile.xml @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<user-types> +<user-types version="1234"> <profile-type name='android.test.2' max-allowed-per-parent='12' @@ -32,4 +32,7 @@ <default-restrictions no_remove_user='true' no_bluetooth='true' /> </profile-type> <profile-type name='custom.test.1' max-allowed-per-parent='14' /> + + <change-user-type from="android.test.1" to="android.test.2" whenVersionLeq="1233" /> + </user-types> diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java index 904e93b7d8cf..e2b48d439a47 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java @@ -127,6 +127,8 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi // Used as an override when set to nonzero. private long mCurrentTimeMillis = 0; + private final Map<Long, Pair<String, Integer>> mEnabledChanges = new ArrayMap<>(); + public MockInjector(MockSystemServices services, DpmMockContext context) { super(context); this.services = services; @@ -487,5 +489,33 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi public long systemCurrentTimeMillis() { return mCurrentTimeMillis != 0 ? mCurrentTimeMillis : System.currentTimeMillis(); } + + public void setChangeEnabledForPackage( + long changeId, boolean enabled, String packageName, int userId) { + if (enabled) { + mEnabledChanges.put(changeId, Pair.create(packageName, userId)); + } else { + mEnabledChanges.remove(changeId); + } + } + + public void clearEnabledChanges() { + mEnabledChanges.clear(); + } + + @Override + public boolean isChangeEnabled(long changeId, String packageName, int userId) { + Pair<String, Integer> packageAndUser = mEnabledChanges.get(changeId); + if (packageAndUser == null) { + return false; + } + + if (!packageAndUser.first.equals(packageName) + || !packageAndUser.second.equals(userId)) { + return false; + } + + return true; + } } } 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 c0c82d53b4f0..77090a8e0d83 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -5090,11 +5090,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { doReturn(true).when(getServices().lockPatternUtils) .isSeparateProfileChallengeEnabled(managedProfileUserId); - dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC); - parentDpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_NUMERIC); + dpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_HIGH); + parentDpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_MEDIUM); when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) - .thenReturn(computeForPassword("1234".getBytes())); + .thenReturn(computeForPassword("184342".getBytes())); // Numeric password is compliant with current requirement (QUALITY_NUMERIC set explicitly // on the parent admin) @@ -5107,6 +5107,68 @@ public class DevicePolicyManagerTest extends DpmTestBase { managedProfileUserId)).isFalse(); } + @Test + public void testCanSetPasswordRequirementOnParentPreS() throws Exception { + final int managedProfileUserId = CALLER_USER_HANDLE; + final int managedProfileAdminUid = + UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID); + mContext.binder.callingUid = managedProfileAdminUid; + addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R); + dpms.mMockInjector.setChangeEnabledForPackage(165573442L, false, + admin1.getPackageName(), managedProfileUserId); + + parentDpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); + assertThat(parentDpm.getPasswordQuality(admin1)) + .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); + } + + @Test + public void testCannotSetPasswordRequirementOnParent() throws Exception { + final int managedProfileUserId = CALLER_USER_HANDLE; + final int managedProfileAdminUid = + UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID); + mContext.binder.callingUid = managedProfileAdminUid; + addManagedProfile(admin1, managedProfileAdminUid, admin1); + dpms.mMockInjector.setChangeEnabledForPackage(165573442L, true, + admin1.getPackageName(), managedProfileUserId); + + try { + assertExpectException(IllegalArgumentException.class, null, () -> + parentDpm.setPasswordQuality( + admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX)); + } finally { + dpms.mMockInjector.clearEnabledChanges(); + } + } + + @Test + public void testPasswordQualityAppliesToParentPreS() throws Exception { + final int managedProfileUserId = CALLER_USER_HANDLE; + final int managedProfileAdminUid = + UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID); + mContext.binder.callingUid = managedProfileAdminUid; + addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R); + when(getServices().userManager.getProfileParent(CALLER_USER_HANDLE)) + .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0)); + + dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); + assertThat(parentDpm.getPasswordQuality(null)) + .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); + } + + @Test + public void testPasswordQualityDoesNotApplyToParentPostS() throws Exception { + final int managedProfileUserId = CALLER_USER_HANDLE; + final int managedProfileAdminUid = + UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID); + mContext.binder.callingUid = managedProfileAdminUid; + addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R); + + dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); + assertThat(parentDpm.getPasswordQuality(admin1)) + .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); + } + private void setActivePasswordState(PasswordMetrics passwordMetrics) throws Exception { final int userHandle = UserHandle.getUserId(mContext.binder.callingUid); @@ -7014,20 +7076,32 @@ public class DevicePolicyManagerTest extends DpmTestBase { * @param adminUid uid of the admin package. * @param copyFromAdmin package information for {@code admin} will be built based on this * component's information. + * @param appTargetSdk admin's target SDK level */ private void addManagedProfile( - ComponentName admin, int adminUid, ComponentName copyFromAdmin) throws Exception { + ComponentName admin, int adminUid, ComponentName copyFromAdmin, int appTargetSdk) + throws Exception { final int userId = UserHandle.getUserId(adminUid); getServices().addUser(userId, 0, UserManager.USER_TYPE_PROFILE_MANAGED, UserHandle.USER_SYSTEM); mContext.callerPermissions.addAll(OWNER_SETUP_PERMISSIONS); - setUpPackageManagerForFakeAdmin(admin, adminUid, copyFromAdmin); + setUpPackageManagerForFakeAdmin(admin, adminUid, /* enabledSetting= */ null, + appTargetSdk, copyFromAdmin); dpm.setActiveAdmin(admin, false, userId); assertThat(dpm.setProfileOwner(admin, null, userId)).isTrue(); mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS); } /** + * Same as {@code addManagedProfile} above, except using development API level as the API + * level of the admin. + */ + private void addManagedProfile( + ComponentName admin, int adminUid, ComponentName copyFromAdmin) throws Exception { + addManagedProfile(admin, adminUid, copyFromAdmin, VERSION_CODES.CUR_DEVELOPMENT); + } + + /** * Convert String[] to StringParceledListSlice. */ private static StringParceledListSlice asSlice(String[] s) { diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java index 3aa5a80d814f..d58d71fb44c3 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java @@ -23,6 +23,8 @@ import static org.mockito.Mockito.when; import android.app.admin.FactoryResetProtectionPolicy; import android.os.Parcel; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import androidx.test.runner.AndroidJUnit4; @@ -98,7 +100,7 @@ public class FactoryResetProtectionPolicyTest { throws Exception { ByteArrayOutputStream outStream = serialize(policy); ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new InputStreamReader(inStream)); assertThat(parser.next()).isEqualTo(XmlPullParser.START_TAG); @@ -109,7 +111,7 @@ public class FactoryResetProtectionPolicyTest { throws Exception { ByteArrayOutputStream outStream = serialize(policy); ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); - XmlPullParser parser = mock(XmlPullParser.class); + TypedXmlPullParser parser = mock(TypedXmlPullParser.class); when(parser.next()).thenThrow(XmlPullParserException.class); parser.setInput(new InputStreamReader(inStream)); @@ -120,7 +122,7 @@ public class FactoryResetProtectionPolicyTest { private ByteArrayOutputStream serialize(FactoryResetProtectionPolicy policy) throws IOException { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); - final XmlSerializer outXml = new FastXmlSerializer(); + final TypedXmlSerializer outXml = Xml.newFastSerializer(); outXml.setOutput(outStream, StandardCharsets.UTF_8.name()); outXml.startDocument(null, true); outXml.startTag(null, TAG_FACTORY_RESET_PROTECTION_POLICY); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java index 0a9aad771ff0..1308a3e74881 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java @@ -29,6 +29,8 @@ import static org.junit.Assert.fail; import android.app.admin.FreezePeriod; import android.app.admin.SystemUpdatePolicy; import android.os.Parcel; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import androidx.test.runner.AndroidJUnit4; @@ -471,7 +473,7 @@ public final class SystemUpdatePolicyTest { // Test XML serialization ByteArrayOutputStream outStream = new ByteArrayOutputStream(); - final XmlSerializer outXml = new FastXmlSerializer(); + final TypedXmlSerializer outXml = Xml.newFastSerializer(); outXml.setOutput(outStream, StandardCharsets.UTF_8.name()); outXml.startDocument(null, true); outXml.startTag(null, "ota"); @@ -481,7 +483,7 @@ public final class SystemUpdatePolicyTest { outXml.flush(); ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new InputStreamReader(inStream)); assertThat(parser.next()).isEqualTo(XmlPullParser.START_TAG); checkFreezePeriods(SystemUpdatePolicy.restoreFromXml(parser), expectedPeriods); diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index 325ba118c711..cb5ca04c1fde 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -16,49 +16,97 @@ package com.android.server.display; +import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS; +import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS; +import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS; +import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS; +import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE; +import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE; + +import static com.android.server.display.DisplayModeDirector.Vote.PRIORITY_FLICKER; + import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import android.annotation.NonNull; +import android.content.ContentResolver; import android.content.Context; +import android.content.ContextWrapper; +import android.database.ContentObserver; +import android.hardware.Sensor; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; import android.os.Handler; import android.os.Looper; +import android.provider.DeviceConfig; +import android.provider.Settings; +import android.test.mock.MockContentResolver; +import android.util.Slog; import android.util.SparseArray; import android.view.Display; -import androidx.test.InstrumentationRegistry; +import androidx.test.core.app.ApplicationProvider; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.util.Preconditions; +import com.android.internal.util.test.FakeSettingsProvider; +import com.android.internal.util.test.FakeSettingsProviderRule; import com.android.server.display.DisplayModeDirector.BrightnessObserver; import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs; import com.android.server.display.DisplayModeDirector.Vote; +import com.android.server.testutils.FakeDeviceConfigInterface; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + @SmallTest @RunWith(AndroidJUnit4.class) public class DisplayModeDirectorTest { // The tolerance within which we consider something approximately equals. + private static final String TAG = "DisplayModeDirectorTest"; + private static final boolean DEBUG = false; private static final float FLOAT_TOLERANCE = 0.01f; private Context mContext; + private FakesInjector mInjector; + private Handler mHandler; + @Rule + public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext())); + final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContext); + when(mContext.getContentResolver()).thenReturn(resolver); + mInjector = new FakesInjector(); + mHandler = new Handler(Looper.getMainLooper()); } private DisplayModeDirector createDirectorFromRefreshRateArray( float[] refreshRates, int baseModeId) { DisplayModeDirector director = - new DisplayModeDirector(mContext, new Handler(Looper.getMainLooper())); + new DisplayModeDirector(mContext, mHandler, mInjector); int displayId = 0; Display.Mode[] modes = new Display.Mode[refreshRates.length]; for (int i = 0; i < refreshRates.length; i++) { @@ -159,9 +207,9 @@ public class DisplayModeDirectorTest { } @Test - public void testBrightnessHasLowerPriorityThanUser() { - assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_REFRESH_RATE); - assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_SIZE); + public void testFlickerHasLowerPriorityThanUser() { + assertTrue(PRIORITY_FLICKER < Vote.PRIORITY_APP_REQUEST_REFRESH_RATE); + assertTrue(PRIORITY_FLICKER < Vote.PRIORITY_APP_REQUEST_SIZE); int displayId = 0; DisplayModeDirector director = createDirectorFromFpsRange(60, 90); @@ -169,7 +217,7 @@ public class DisplayModeDirectorTest { SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(displayId, votes); votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90)); - votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60)); + votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60)); director.injectVotesByDisplay(votesByDisplay); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); @@ -177,7 +225,7 @@ public class DisplayModeDirectorTest { votes.clear(); votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90)); - votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90)); + votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(90, 90)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); @@ -185,7 +233,7 @@ public class DisplayModeDirectorTest { votes.clear(); votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90)); - votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60)); + votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); @@ -193,7 +241,7 @@ public class DisplayModeDirectorTest { votes.clear(); votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 60)); - votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90)); + votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(90, 90)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); @@ -202,10 +250,10 @@ public class DisplayModeDirectorTest { @Test public void testAppRequestRefreshRateRange() { - // Confirm that the app request range doesn't include low brightness or min refresh rate - // settings, but does include everything else. + // Confirm that the app request range doesn't include flicker or min refresh rate settings, + // but does include everything else. assertTrue( - Vote.PRIORITY_LOW_BRIGHTNESS < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF); + PRIORITY_FLICKER < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF); assertTrue(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF); assertTrue(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE @@ -216,7 +264,7 @@ public class DisplayModeDirectorTest { SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(displayId, votes); - votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60)); + votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60)); director.injectVotesByDisplay(votesByDisplay); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); @@ -310,7 +358,7 @@ public class DisplayModeDirectorTest { SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(displayId, votes); - votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(0, 60)); + votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(0, 60)); votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, Vote.forRefreshRates(60, 90)); votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90)); votes.put(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, Vote.forRefreshRates(60, 60)); @@ -398,4 +446,343 @@ public class DisplayModeDirectorTest { DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); assertThat(desiredSpecs.allowGroupSwitching).isTrue(); } + + @Test + public void testBrightnessObserverGetsUpdatedRefreshRatesForZone() { + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0); + SensorManager sensorManager = createMockSensorManager(createLightSensor()); + + final int initialRefreshRate = 60; + mInjector.getDeviceConfig().setRefreshRateInLowZone(initialRefreshRate); + director.start(sensorManager); + assertThat(director.getBrightnessObserver().getRefreshRateInLowZone()) + .isEqualTo(initialRefreshRate); + + final int updatedRefreshRate = 90; + mInjector.getDeviceConfig().setRefreshRateInLowZone(updatedRefreshRate); + // Need to wait for the property change to propagate to the main thread. + waitForIdleSync(); + assertThat(director.getBrightnessObserver().getRefreshRateInLowZone()) + .isEqualTo(updatedRefreshRate); + } + + @Test + public void testBrightnessObserverThresholdsInZone() { + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0); + SensorManager sensorManager = createMockSensorManager(createLightSensor()); + + final int[] initialDisplayThresholds = { 10 }; + final int[] initialAmbientThresholds = { 20 }; + + final FakeDeviceConfig config = mInjector.getDeviceConfig(); + config.setLowDisplayBrightnessThresholds(initialDisplayThresholds); + config.setLowAmbientBrightnessThresholds(initialAmbientThresholds); + director.start(sensorManager); + + assertThat(director.getBrightnessObserver().getLowDisplayBrightnessThresholds()) + .isEqualTo(initialDisplayThresholds); + assertThat(director.getBrightnessObserver().getLowAmbientBrightnessThresholds()) + .isEqualTo(initialAmbientThresholds); + + final int[] updatedDisplayThresholds = { 9, 14 }; + final int[] updatedAmbientThresholds = { -1, 19 }; + config.setLowDisplayBrightnessThresholds(updatedDisplayThresholds); + config.setLowAmbientBrightnessThresholds(updatedAmbientThresholds); + // Need to wait for the property change to propagate to the main thread. + waitForIdleSync(); + assertThat(director.getBrightnessObserver().getLowDisplayBrightnessThresholds()) + .isEqualTo(updatedDisplayThresholds); + assertThat(director.getBrightnessObserver().getLowAmbientBrightnessThresholds()) + .isEqualTo(updatedAmbientThresholds); + } + + @Test + public void testLockFpsForLowZone() throws Exception { + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); + setPeakRefreshRate(90); + director.getSettingsObserver().setDefaultRefreshRate(90); + director.getBrightnessObserver().setDefaultDisplayState(true); + + final FakeDeviceConfig config = mInjector.getDeviceConfig(); + config.setRefreshRateInLowZone(90); + config.setLowDisplayBrightnessThresholds(new int[] { 10 }); + config.setLowAmbientBrightnessThresholds(new int[] { 20 }); + + Sensor lightSensor = createLightSensor(); + SensorManager sensorManager = createMockSensorManager(lightSensor); + + director.start(sensorManager); + + ArgumentCaptor<SensorEventListener> listenerCaptor = + ArgumentCaptor.forClass(SensorEventListener.class); + Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1))) + .registerListener( + listenerCaptor.capture(), + eq(lightSensor), + anyInt(), + any(Handler.class)); + SensorEventListener listener = listenerCaptor.getValue(); + + setBrightness(10); + // Sensor reads 20 lux, + listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 20 /*lux*/)); + + Vote vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER); + assertVoteForRefreshRateLocked(vote, 90 /*fps*/); + + setBrightness(125); + // Sensor reads 1000 lux, + listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000 /*lux*/)); + + vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER); + assertThat(vote).isNull(); + } + + @Test + public void testLockFpsForHighZone() throws Exception { + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); + setPeakRefreshRate(90 /*fps*/); + director.getSettingsObserver().setDefaultRefreshRate(90); + director.getBrightnessObserver().setDefaultDisplayState(true); + + final FakeDeviceConfig config = mInjector.getDeviceConfig(); + config.setRefreshRateInHighZone(60); + config.setHighDisplayBrightnessThresholds(new int[] { 255 }); + config.setHighAmbientBrightnessThresholds(new int[] { 8000 }); + + Sensor lightSensor = createLightSensor(); + SensorManager sensorManager = createMockSensorManager(lightSensor); + + director.start(sensorManager); + + ArgumentCaptor<SensorEventListener> listenerCaptor = + ArgumentCaptor.forClass(SensorEventListener.class); + Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1))) + .registerListener( + listenerCaptor.capture(), + eq(lightSensor), + anyInt(), + any(Handler.class)); + SensorEventListener listener = listenerCaptor.getValue(); + + setBrightness(100); + // Sensor reads 2000 lux, + listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000)); + + Vote vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER); + assertThat(vote).isNull(); + + setBrightness(255); + // Sensor reads 9000 lux, + listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000)); + + vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER); + assertVoteForRefreshRateLocked(vote, 60 /*fps*/); + } + + private void assertVoteForRefreshRateLocked(Vote vote, float refreshRate) { + assertThat(vote).isNotNull(); + final DisplayModeDirector.RefreshRateRange expectedRange = + new DisplayModeDirector.RefreshRateRange(refreshRate, refreshRate); + assertThat(vote.refreshRateRange).isEqualTo(expectedRange); + } + + private static class FakeDeviceConfig extends FakeDeviceConfigInterface { + @Override + public String getProperty(String namespace, String name) { + Preconditions.checkArgument(DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace)); + return super.getProperty(namespace, name); + } + + @Override + public void addOnPropertiesChangedListener( + String namespace, + Executor executor, + DeviceConfig.OnPropertiesChangedListener listener) { + Preconditions.checkArgument(DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace)); + super.addOnPropertiesChangedListener(namespace, executor, listener); + } + + void setRefreshRateInLowZone(int fps) { + putPropertyAndNotify( + DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_LOW_ZONE, + String.valueOf(fps)); + } + + void setLowDisplayBrightnessThresholds(int[] brightnessThresholds) { + String thresholds = toPropertyValue(brightnessThresholds); + + if (DEBUG) { + Slog.e(TAG, "Brightness Thresholds = " + thresholds); + } + + putPropertyAndNotify( + DeviceConfig.NAMESPACE_DISPLAY_MANAGER, + KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS, + thresholds); + } + + void setLowAmbientBrightnessThresholds(int[] ambientThresholds) { + String thresholds = toPropertyValue(ambientThresholds); + + if (DEBUG) { + Slog.e(TAG, "Ambient Thresholds = " + thresholds); + } + + putPropertyAndNotify( + DeviceConfig.NAMESPACE_DISPLAY_MANAGER, + KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS, + thresholds); + } + + void setRefreshRateInHighZone(int fps) { + putPropertyAndNotify( + DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_HIGH_ZONE, + String.valueOf(fps)); + } + + void setHighDisplayBrightnessThresholds(int[] brightnessThresholds) { + String thresholds = toPropertyValue(brightnessThresholds); + + if (DEBUG) { + Slog.e(TAG, "Brightness Thresholds = " + thresholds); + } + + putPropertyAndNotify( + DeviceConfig.NAMESPACE_DISPLAY_MANAGER, + KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS, + thresholds); + } + + void setHighAmbientBrightnessThresholds(int[] ambientThresholds) { + String thresholds = toPropertyValue(ambientThresholds); + + if (DEBUG) { + Slog.e(TAG, "Ambient Thresholds = " + thresholds); + } + + putPropertyAndNotify( + DeviceConfig.NAMESPACE_DISPLAY_MANAGER, + KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS, + thresholds); + } + + @NonNull + private static String toPropertyValue(@NonNull int[] intArray) { + return Arrays.stream(intArray) + .mapToObj(Integer::toString) + .collect(Collectors.joining(",")); + } + } + + private void setBrightness(int brightness) { + Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, + brightness); + mInjector.notifyBrightnessChanged(); + waitForIdleSync(); + } + + private void setPeakRefreshRate(float fps) { + Settings.System.putFloat(mContext.getContentResolver(), Settings.System.PEAK_REFRESH_RATE, + fps); + mInjector.notifyPeakRefreshRateChanged(); + waitForIdleSync(); + } + + private static SensorManager createMockSensorManager(Sensor... sensors) { + SensorManager sensorManager = Mockito.mock(SensorManager.class); + when(sensorManager.getSensorList(anyInt())).then((invocation) -> { + List<Sensor> requestedSensors = new ArrayList<>(); + int type = invocation.getArgument(0); + for (Sensor sensor : sensors) { + if (sensor.getType() == type || type == Sensor.TYPE_ALL) { + requestedSensors.add(sensor); + } + } + return requestedSensors; + }); + + when(sensorManager.getDefaultSensor(anyInt())).then((invocation) -> { + int type = invocation.getArgument(0); + for (Sensor sensor : sensors) { + if (sensor.getType() == type) { + return sensor; + } + } + return null; + }); + return sensorManager; + } + + private static Sensor createLightSensor() { + try { + return TestUtils.createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT); + } catch (Exception e) { + // There's nothing we can do if this fails, just throw a RuntimeException so that we + // don't have to mark every function that might call this as throwing Exception + throw new RuntimeException("Failed to create a light sensor", e); + } + } + + private void waitForIdleSync() { + mHandler.runWithScissors(() -> { }, 500 /*timeout*/); + } + + static class FakesInjector implements DisplayModeDirector.Injector { + private final FakeDeviceConfig mDeviceConfig; + private ContentObserver mBrightnessObserver; + private ContentObserver mPeakRefreshRateObserver; + + FakesInjector() { + mDeviceConfig = new FakeDeviceConfig(); + } + + @NonNull + public FakeDeviceConfig getDeviceConfig() { + return mDeviceConfig; + } + + @Override + public void registerBrightnessObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer) { + if (mBrightnessObserver != null) { + throw new IllegalStateException("Tried to register a second brightness observer"); + } + mBrightnessObserver = observer; + } + + @Override + public void unregisterBrightnessObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer) { + mBrightnessObserver = null; + } + + void notifyBrightnessChanged() { + if (mBrightnessObserver != null) { + mBrightnessObserver.dispatchChange(false /*selfChange*/, DISPLAY_BRIGHTNESS_URI); + } + } + + @Override + public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer) { + mPeakRefreshRateObserver = observer; + } + + void notifyPeakRefreshRateChanged() { + if (mPeakRefreshRateObserver != null) { + mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/, + PEAK_REFRESH_RATE_URI); + } + } + + @Override + public boolean isDeviceInteractive(@NonNull Context context) { + return true; + } + } } diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java index 9ef755791c80..20f9b703e29d 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java @@ -27,6 +27,8 @@ import static org.junit.Assert.fail; import android.content.om.OverlayInfo; import android.text.TextUtils; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import androidx.test.runner.AndroidJUnit4; @@ -403,7 +405,7 @@ public class OverlayManagerSettingsTests { private int countXmlTags(String xml, String tagToLookFor) throws Exception { int count = 0; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new StringReader(xml)); int event = parser.getEventType(); while (event != XmlPullParser.END_DOCUMENT) { @@ -418,7 +420,7 @@ public class OverlayManagerSettingsTests { private int countXmlAttributesWhere(String xml, String tag, String attr, String value) throws Exception { int count = 0; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new StringReader(xml)); int event = parser.getEventType(); while (event != XmlPullParser.END_DOCUMENT) { diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java index bbb83b6b46cf..11d00f0fd406 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java @@ -29,6 +29,8 @@ import android.util.AtomicFile; import android.util.Slog; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import androidx.test.runner.AndroidJUnit4; diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java index 89c3d5006347..90658055ad6f 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java @@ -25,6 +25,8 @@ import android.content.Context; import android.content.pm.PackageParser; import android.content.pm.Signature; import android.util.TypedXmlPullParser; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import androidx.test.InstrumentationRegistry; diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java index 194ae055e01f..23fcf701a8bb 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -98,6 +98,8 @@ import android.os.UserHandle; import android.test.suitebuilder.annotation.SmallTest; import android.util.Log; import android.util.SparseArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.frameworks.servicestests.R; @@ -8695,7 +8697,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // Write ShareTargets to Xml ByteArrayOutputStream outStream = new ByteArrayOutputStream(); - final XmlSerializer outXml = new FastXmlSerializer(); + final TypedXmlSerializer outXml = Xml.newFastSerializer(); outXml.setOutput(outStream, StandardCharsets.UTF_8.name()); outXml.startDocument(null, true); for (int i = 0; i < expectedValues.size(); i++) { @@ -8706,7 +8708,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // Read ShareTargets from Xml ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new InputStreamReader(inStream)); List<ShareTargetInfo> shareTargets = new ArrayList<>(); int type; diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java index 4fac9dc391e3..dfc25e0c7cb6 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java @@ -21,6 +21,7 @@ import static android.content.pm.UserInfo.FLAG_DISABLED; import static android.content.pm.UserInfo.FLAG_EPHEMERAL; import static android.content.pm.UserInfo.FLAG_FULL; import static android.content.pm.UserInfo.FLAG_GUEST; +import static android.content.pm.UserInfo.FLAG_INITIALIZED; import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; import static android.content.pm.UserInfo.FLAG_PROFILE; import static android.content.pm.UserInfo.FLAG_RESTRICTED; @@ -44,6 +45,7 @@ import android.content.pm.UserInfo.UserInfoFlag; import android.os.Looper; import android.os.Parcel; import android.os.UserHandle; +import android.os.UserManager; import android.text.TextUtils; import androidx.test.InstrumentationRegistry; @@ -206,6 +208,8 @@ public class UserManagerServiceUserInfoTest { @Test public void testUpgradeIfNecessaryLP_9() { final int versionToTest = 9; + // do not trigger a user type upgrade + final int userTypeVersion = UserTypeFactory.getUserTypeVersion(); mUserManagerService.putUserInfo(createUser(100, FLAG_MANAGED_PROFILE, null)); mUserManagerService.putUserInfo(createUser(101, @@ -216,7 +220,7 @@ public class UserManagerServiceUserInfoTest { mUserManagerService.putUserInfo(createUser(105, FLAG_SYSTEM | FLAG_FULL, null)); mUserManagerService.putUserInfo(createUser(106, FLAG_DEMO | FLAG_FULL, null)); - mUserManagerService.upgradeIfNecessaryLP(null, versionToTest - 1); + mUserManagerService.upgradeIfNecessaryLP(null, versionToTest - 1, userTypeVersion); assertTrue(mUserManagerService.isUserOfType(100, USER_TYPE_PROFILE_MANAGED)); assertTrue((mUserManagerService.getUserInfo(100).flags & FLAG_PROFILE) != 0); @@ -278,4 +282,86 @@ public class UserManagerServiceUserInfoTest { two.convertedFromPreCreated); } } + + /** Tests upgrading profile types */ + @Test + public void testUpgradeProfileType_updateTypeAndFlags() { + final int userId = 42; + final String newUserTypeName = "new.user.type"; + final String oldUserTypeName = USER_TYPE_PROFILE_MANAGED; + + UserTypeDetails.Builder oldUserTypeBuilder = new UserTypeDetails.Builder() + .setName(oldUserTypeName) + .setBaseType(FLAG_PROFILE) + .setDefaultUserInfoPropertyFlags(FLAG_MANAGED_PROFILE) + .setMaxAllowedPerParent(32) + .setIconBadge(401) + .setBadgeColors(402, 403, 404) + .setBadgeLabels(23, 24, 25); + UserTypeDetails oldUserType = oldUserTypeBuilder.createUserTypeDetails(); + + UserInfo userInfo = createUser(userId, + oldUserType.getDefaultUserInfoFlags() | FLAG_INITIALIZED, oldUserTypeName); + mUserManagerService.putUserInfo(userInfo); + + UserTypeDetails.Builder newUserTypeBuilder = new UserTypeDetails.Builder() + .setName(newUserTypeName) + .setBaseType(FLAG_PROFILE) + .setMaxAllowedPerParent(32) + .setIconBadge(401) + .setBadgeColors(402, 403, 404) + .setBadgeLabels(23, 24, 25); + UserTypeDetails newUserType = newUserTypeBuilder.createUserTypeDetails(); + + mUserManagerService.upgradeProfileToTypeLU(userInfo, newUserType); + + assertTrue(mUserManagerService.isUserOfType(userId, newUserTypeName)); + assertTrue((mUserManagerService.getUserInfo(userId).flags & FLAG_PROFILE) != 0); + assertTrue((mUserManagerService.getUserInfo(userId).flags & FLAG_MANAGED_PROFILE) == 0); + assertTrue((mUserManagerService.getUserInfo(userId).flags & FLAG_INITIALIZED) != 0); + } + + @Test + public void testUpgradeProfileType_updateRestrictions() { + final int userId = 42; + final String newUserTypeName = "new.user.type"; + final String oldUserTypeName = USER_TYPE_PROFILE_MANAGED; + + UserTypeDetails.Builder oldUserTypeBuilder = new UserTypeDetails.Builder() + .setName(oldUserTypeName) + .setBaseType(FLAG_PROFILE) + .setDefaultUserInfoPropertyFlags(FLAG_MANAGED_PROFILE) + .setMaxAllowedPerParent(32) + .setIconBadge(401) + .setBadgeColors(402, 403, 404) + .setBadgeLabels(23, 24, 25); + UserTypeDetails oldUserType = oldUserTypeBuilder.createUserTypeDetails(); + + UserInfo userInfo = createUser(userId, oldUserType.getDefaultUserInfoFlags(), + oldUserTypeName); + mUserManagerService.putUserInfo(userInfo); + mUserManagerService.setUserRestriction(UserManager.DISALLOW_CAMERA, true, userId); + mUserManagerService.setUserRestriction(UserManager.DISALLOW_PRINTING, true, userId); + + UserTypeDetails.Builder newUserTypeBuilder = new UserTypeDetails.Builder() + .setName(newUserTypeName) + .setBaseType(FLAG_PROFILE) + .setMaxAllowedPerParent(32) + .setIconBadge(401) + .setBadgeColors(402, 403, 404) + .setBadgeLabels(23, 24, 25) + .setDefaultRestrictions( + UserManagerServiceUserTypeTest.makeRestrictionsBundle( + UserManager.DISALLOW_WALLPAPER)); + UserTypeDetails newUserType = newUserTypeBuilder.createUserTypeDetails(); + + mUserManagerService.upgradeProfileToTypeLU(userInfo, newUserType); + + assertTrue(mUserManagerService.getUserRestrictions(userId).getBoolean( + UserManager.DISALLOW_PRINTING)); + assertTrue(mUserManagerService.getUserRestrictions(userId).getBoolean( + UserManager.DISALLOW_CAMERA)); + assertTrue(mUserManagerService.getUserRestrictions(userId).getBoolean( + UserManager.DISALLOW_WALLPAPER)); + } } diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java index 8e74c903534f..ee30f68de7a0 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java @@ -51,6 +51,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.List; + /** * Tests for {@link UserTypeDetails} and {@link UserTypeFactory}. * @@ -358,13 +360,56 @@ public class UserManagerServiceUserTypeTest { () -> UserTypeFactory.customizeBuilders(builders, parser)); } + @Test + public void testUserTypeFactoryVersion_versionMissing() { + final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_eraseArray); + assertEquals(0, UserTypeFactory.getUserTypeVersion(parser)); + } + + @Test + public void testUserTypeFactoryVersion_versionPresent() { + final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_profile); + assertEquals(1234, UserTypeFactory.getUserTypeVersion(parser)); + } + + @Test + public void testUserTypeFactoryUpgrades_validUpgrades() { + final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>(); + builders.put("name", getMinimalBuilder()); + + final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_profile); + List<UserTypeFactory.UserTypeUpgrade> upgrades = UserTypeFactory.parseUserUpgrades(builders, + parser); + + assertFalse(upgrades.isEmpty()); + UserTypeFactory.UserTypeUpgrade upgrade = upgrades.get(0); + assertEquals("android.test.1", upgrade.getFromType()); + assertEquals("android.test.2", upgrade.getToType()); + assertEquals(1233, upgrade.getUpToVersion()); + } + + @Test + public void testUserTypeFactoryUpgrades_illegalBaseTypeUpgrade() { + final String userTypeFull = "android.test.1"; + final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>(); + builders.put(userTypeFull, new UserTypeDetails.Builder() + .setName(userTypeFull) + .setBaseType(FLAG_FULL)); + + final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_full); + + // parser is illegal because the "to" upgrade type is not a profile, but a full user + assertThrows(IllegalArgumentException.class, + () -> UserTypeFactory.parseUserUpgrades(builders, parser)); + } + /** Returns a minimal {@link UserTypeDetails.Builder} that can legitimately be created. */ private UserTypeDetails.Builder getMinimalBuilder() { return new UserTypeDetails.Builder().setName("name").setBaseType(FLAG_FULL); } /** Creates a Bundle of the given String restrictions, each set to true. */ - private Bundle makeRestrictionsBundle(String ... restrictions) { + public static Bundle makeRestrictionsBundle(String ... restrictions) { final Bundle bundle = new Bundle(); for (String restriction : restrictions) { bundle.putBoolean(restriction, true); diff --git a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java index 0f9bf2f6cd86..8bccce102887 100644 --- a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java +++ b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java @@ -367,7 +367,7 @@ public class SearchablesTest extends AndroidTestCase { * for you if needed; if you already have this information around, it can * be much more efficient to supply it here. * - * @return Returns an XmlPullParser allowing you to parse out the XML + * @return Returns an TypedXmlPullParser allowing you to parse out the XML * data. Returns null if the xml resource could not be found for any * reason. */ diff --git a/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java b/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java index 1d62e01c068d..7ac493828610 100644 --- a/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java +++ b/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java @@ -21,6 +21,8 @@ import static com.google.common.truth.Truth.assertThat; import android.app.usage.CacheQuotaHint; import android.test.AndroidTestCase; import android.util.Pair; +import android.util.TypedXmlSerializer; +import android.util.Xml; import com.android.internal.util.FastXmlSerializer; @@ -37,12 +39,12 @@ import java.util.List; @RunWith(JUnit4.class) public class CacheQuotaStrategyTest extends AndroidTestCase { StringWriter mWriter; - FastXmlSerializer mOut; + TypedXmlSerializer mOut; @Before public void setUp() throws Exception { mWriter = new StringWriter(); - mOut = new FastXmlSerializer(); + mOut = Xml.newFastSerializer(); mOut.setOutput(mWriter); } diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/FakeDeviceConfigInterface.java b/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java index 2904a5b73646..a67f64596ef5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/utils/FakeDeviceConfigInterface.java +++ b/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.wm.utils; +package com.android.server.testutils; import android.annotation.NonNull; import android.provider.DeviceConfig; @@ -22,6 +22,7 @@ import android.util.ArrayMap; import android.util.Pair; import com.android.internal.util.Preconditions; +import com.android.server.utils.DeviceConfigInterface; import java.lang.reflect.Constructor; import java.util.HashMap; @@ -122,6 +123,19 @@ public class FakeDeviceConfigInterface implements DeviceConfigInterface { } @Override + public float getFloat(String namespace, String name, float defaultValue) { + String value = getProperty(namespace, name); + if (value == null) { + return defaultValue; + } + try { + return Float.parseFloat(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + @Override public boolean getBoolean(String namespace, String name, boolean defaultValue) { String value = getProperty(namespace, name); return value != null ? Boolean.parseBoolean(value) : defaultValue; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java index c50792866582..4a4d9bc174fb 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java @@ -54,6 +54,8 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.IntArray; import android.util.SparseArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.util.FastXmlSerializer; @@ -267,7 +269,7 @@ public class ManagedServicesTest extends UiServiceTestCase { mIpm, approvalLevel); // approved services aren't in xml - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(new byte[]{})), null); writeExpectedValuesToSettings(approvalLevel); @@ -335,7 +337,7 @@ public class ManagedServicesTest extends UiServiceTestCase { String testComponent = "user.test.component/C1"; String resolvedValue = (approvalLevel == APPROVAL_BY_COMPONENT) ? testComponent : testPackage; - XmlPullParser parser = + TypedXmlPullParser parser = getParserWithEntries(service, getXmlEntry(resolvedValue, 0, true)); service.readXml(parser, null, true, 10); @@ -357,7 +359,7 @@ public class ManagedServicesTest extends UiServiceTestCase { String resolvedValue = (approvalLevel == APPROVAL_BY_COMPONENT) ? testComponent : testPackage; String xmlEntry = getXmlEntry(resolvedValue, 0, true, false); - XmlPullParser parser = getParserWithEntries(service, xmlEntry); + TypedXmlPullParser parser = getParserWithEntries(service, xmlEntry); service.readXml(parser, null, true, 0); @@ -384,7 +386,7 @@ public class ManagedServicesTest extends UiServiceTestCase { ManagedServices service2 = new TestManagedServices( getContext(), mLock, mUserProfiles, mIpm, APPROVAL_BY_COMPONENT); - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); BufferedOutputStream outStream = new BufferedOutputStream(baos); serializer.setOutput(outStream, "utf-8"); @@ -396,7 +398,7 @@ public class ManagedServicesTest extends UiServiceTestCase { serializer.endDocument(); outStream.flush(); - final XmlPullParser parser = Xml.newPullParser(); + final TypedXmlPullParser parser = Xml.newFastPullParser(); BufferedInputStream input = new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())); @@ -536,7 +538,7 @@ public class ManagedServicesTest extends UiServiceTestCase { service, Collections.singletonList(service.getPackageName(resolvedValue10)), 10); - XmlPullParser parser = + TypedXmlPullParser parser = getParserWithEntries( service, getXmlEntry(resolvedValue0, 0, true), @@ -544,7 +546,7 @@ public class ManagedServicesTest extends UiServiceTestCase { service.readXml(parser, null, false, UserHandle.USER_ALL); // Write backup. - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); @@ -557,7 +559,7 @@ public class ManagedServicesTest extends UiServiceTestCase { service.setPackageOrComponentEnabled(resolvedValue10, 10, true, false); // Parse backup via restore. - XmlPullParser restoreParser = Xml.newPullParser(); + TypedXmlPullParser restoreParser = Xml.newFastPullParser(); restoreParser.setInput( new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), null); restoreParser.nextTag(); @@ -608,7 +610,7 @@ public class ManagedServicesTest extends UiServiceTestCase { addExpectedServices(service, entriesExpectedToHaveServices, userInfo.id); } - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); @@ -618,7 +620,7 @@ public class ManagedServicesTest extends UiServiceTestCase { serializer.endDocument(); serializer.flush(); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); @@ -640,7 +642,7 @@ public class ManagedServicesTest extends UiServiceTestCase { mIpm, approvalLevel); loadXml(service); - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); @@ -666,7 +668,7 @@ public class ManagedServicesTest extends UiServiceTestCase { mIpm, approvalLevel); loadXml(service); - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); @@ -674,7 +676,7 @@ public class ManagedServicesTest extends UiServiceTestCase { serializer.endDocument(); serializer.flush(); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); byte[] rawOutput = baos.toByteArray(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(rawOutput)), null); @@ -1387,7 +1389,7 @@ public class ManagedServicesTest extends UiServiceTestCase { private void loadXml(ManagedServices service) throws Exception { String xmlString = createXml(service); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xmlString.getBytes())), null); parser.nextTag(); @@ -1423,7 +1425,7 @@ public class ManagedServicesTest extends UiServiceTestCase { return xml.toString(); } - private XmlPullParser getParserWithEntries(ManagedServices service, String... xmlEntries) + private TypedXmlPullParser getParserWithEntries(ManagedServices service, String... xmlEntries) throws Exception { final StringBuffer xml = new StringBuffer(); xml.append("<" + service.getConfig().xmlTag + ">\n"); @@ -1432,7 +1434,7 @@ public class ManagedServicesTest extends UiServiceTestCase { } xml.append("</" + service.getConfig().xmlTag + ">"); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.toString().getBytes())), null); parser.nextTag(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java index f649911b6bb9..8c2038b59a0f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java @@ -36,6 +36,8 @@ import android.content.pm.UserInfo; import android.os.UserHandle; import android.os.UserManager; import android.util.IntArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.server.UiServiceTestCase; @@ -126,7 +128,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase { + "<service_listing approved=\"b/b\" user=\"10\" primary=\"true\" />" + "</enabled_assistants>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.toString().getBytes())), null); parser.nextTag(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 09a4289ece3f..60832374ac28 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -160,6 +160,8 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import android.widget.RemoteViews; @@ -3446,7 +3448,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { new BufferedInputStream(new ByteArrayInputStream(upgradeXml.getBytes())), false, UserHandle.USER_ALL); - verify(mSnoozeHelper, times(1)).readXml(any(XmlPullParser.class), anyLong()); + verify(mSnoozeHelper, times(1)).readXml(any(TypedXmlPullParser.class), anyLong()); } @Test @@ -3887,12 +3889,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationChannel channel = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT); channel.setSound(Uri.EMPTY, null); - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); channel.writeXmlForBackup(serializer, getContext()); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); NotificationChannel restored = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT); @@ -3915,7 +3917,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationChannel channel = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT); channel.setVibrationPattern(new long[0]); - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); channel.writeXml(serializer); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 7ec8689e5387..98c4a2da6a4f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -99,6 +99,8 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.Pair; import android.util.StatsEvent; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import androidx.test.InstrumentationRegistry; @@ -280,7 +282,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { private ByteArrayOutputStream writeXmlAndPurge( String pkg, int uid, boolean forBackup, int userId, String... channelIds) throws Exception { - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); @@ -300,7 +302,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { private void loadByteArrayXml(byte[] byteArray, boolean forRestore, int userId) throws Exception { - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(byteArray)), null); parser.nextTag(); mHelper.readXml(parser, forRestore, userId); @@ -717,7 +719,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG_N_MR1}, new int[]{ UID_N_MR1}); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); @@ -772,7 +774,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" visibility=\"" + Notification.VISIBILITY_PRIVATE + "\" />\n" + "</ranking>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(preupgradeXml.getBytes())), null); parser.nextTag(); @@ -2559,7 +2561,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + " importance=\"3\"/>" + "</package>" + "</ranking>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -2580,7 +2582,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + " importance=\"3\"/>" + "</package>" + "</ranking>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -2612,7 +2614,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + " importance=\"3\"/>" + "</package>" + "</ranking>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -2753,7 +2755,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "<channel id=\"b\" name=\"b\" importance=\"3\"/>" + "</package>" + "</ranking>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -2779,7 +2781,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "<channel id=\"c\" name=\"c\" importance=\"3\"/>" + "</package>" + "</ranking>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -3047,7 +3049,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { toAdd.add(new Pair(PKG_O, UID_O)); mHelper.updateDefaultApps(UserHandle.getUserId(UID_O), null, toAdd); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); @@ -3121,7 +3123,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "<channel id=\"" + extraChannel1 + "\" name=\"hi\" importance=\"3\"/>" + "</package>" + "</ranking>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -3154,12 +3156,12 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "</ranking>"; // trigger a restore for both users - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xmlUser0.getBytes())), null); parser.nextTag(); mHelper.readXml(parser, true, 0); - parser = Xml.newPullParser(); + parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xmlUser10.getBytes())), null); parser.nextTag(); @@ -3242,7 +3244,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"foo:placeholder_id\"/>" + "</package>" + "</ranking>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -3261,7 +3263,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"other\"/>" + "</package>" + "</ranking>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -3280,7 +3282,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "<channel id=\"id\" name=\"hi\" importance=\"3\"/>" + "</package>" + "</ranking>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java index 3deeea2d4577..35b224a24061 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java @@ -43,6 +43,8 @@ import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.test.suitebuilder.annotation.SmallTest; import android.util.IntArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import androidx.test.runner.AndroidJUnit4; @@ -95,7 +97,7 @@ public class SnoozeHelperTest extends UiServiceTestCase { + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" " + "pkg=\"pkg\" key=\"key2\" time=\"" + max_time_str + "\"/>" + "</snoozed-notifications>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml_string.getBytes())), null); mSnoozeHelper.readXml(parser, 1); @@ -114,12 +116,12 @@ public class SnoozeHelperTest extends UiServiceTestCase { + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" " + "pkg=\"pkg\" key=\"key2\" time=\"" + max_time_str + "\"/>" + "</snoozed-notifications>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml_string.getBytes())), null); mSnoozeHelper.readXml(parser, 1); ByteArrayOutputStream baos = new ByteArrayOutputStream(); - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); mSnoozeHelper.writeXml(serializer); @@ -137,7 +139,7 @@ public class SnoozeHelperTest extends UiServiceTestCase { + "<context version=\"1\" user-id=\"0\" notification=\"notification\" " + "pkg=\"pkg\" key=\"key2\" id=\"uri\"/>" + "</snoozed-notifications>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml_string.getBytes())), null); mSnoozeHelper.readXml(parser, 1); @@ -151,7 +153,7 @@ public class SnoozeHelperTest extends UiServiceTestCase { throws XmlPullParserException, IOException { NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); mSnoozeHelper.snooze(r, 999999999); - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); @@ -159,7 +161,7 @@ public class SnoozeHelperTest extends UiServiceTestCase { serializer.endDocument(); serializer.flush(); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), "utf-8"); mSnoozeHelper.readXml(parser, 1); @@ -175,7 +177,7 @@ public class SnoozeHelperTest extends UiServiceTestCase { NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); mSnoozeHelper.snooze(r, 0); // Thread.sleep(100); - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); @@ -183,7 +185,7 @@ public class SnoozeHelperTest extends UiServiceTestCase { serializer.endDocument(); serializer.flush(); Thread.sleep(10); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), "utf-8"); mSnoozeHelper.readXml(parser, 2); @@ -227,7 +229,7 @@ public class SnoozeHelperTest extends UiServiceTestCase { + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" " + "pkg=\"pkg\" key=\"key2\" time=\"" + 15+ "\"/>" + "</snoozed-notifications>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml_string.getBytes())), null); mSnoozeHelper.readXml(parser, 4); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java index 013a99433041..5262465a399c 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java @@ -30,6 +30,8 @@ import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeConfig.EventInfo; import android.service.notification.ZenPolicy; import android.test.suitebuilder.annotation.SmallTest; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import androidx.test.runner.AndroidJUnit4; @@ -199,7 +201,7 @@ public class ZenModeConfigTest extends UiServiceTestCase { rule.name = "name"; rule.snoozing = true; - XmlSerializer out = new FastXmlSerializer(); + TypedXmlSerializer out = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); out.setOutput(new BufferedOutputStream(baos), "utf-8"); out.startDocument(null, true); @@ -208,7 +210,7 @@ public class ZenModeConfigTest extends UiServiceTestCase { out.endTag(null, tag); out.endDocument(); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 3430dbdce753..cfdd2464322d 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -95,6 +95,8 @@ import android.testing.TestableLooper; import android.util.ArrayMap; import android.util.Log; import android.util.StatsEvent; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.R; @@ -189,14 +191,14 @@ public class ZenModeHelperTest extends UiServiceTestCase { + "&end=7.0&exitAtAlarm=true\"/>" + "<disallow visualEffects=\"511\" />" + "</zen>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); return new XmlResourceParserImpl(parser); } private ByteArrayOutputStream writeXmlAndPurge(Integer version) throws Exception { - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); @@ -209,7 +211,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { private ByteArrayOutputStream writeXmlAndPurgeForUser(Integer version, int userId) throws Exception { - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); @@ -222,8 +224,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { return baos; } - private XmlPullParser getParserForByteStream(ByteArrayOutputStream baos) throws Exception { - XmlPullParser parser = Xml.newPullParser(); + private TypedXmlPullParser getParserForByteStream(ByteArrayOutputStream baos) throws Exception { + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput( new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); @@ -837,7 +839,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy(); ByteArrayOutputStream baos = writeXmlAndPurge(null); - XmlPullParser parser = getParserForByteStream(baos); + TypedXmlPullParser parser = getParserForByteStream(baos); mZenModeHelperSpy.readXml(parser, false, UserHandle.USER_ALL); assertEquals("Config mismatch: current vs expected: " @@ -962,7 +964,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelperSpy.mConfigs.put(11, newConfig11); // Parse backup data. - XmlPullParser parser = getParserForByteStream(baos); + TypedXmlPullParser parser = getParserForByteStream(baos); mZenModeHelperSpy.readXml(parser, true, 10); mZenModeHelperSpy.readXml(parser, true, 11); @@ -980,7 +982,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig original = mZenModeHelperSpy.mConfig.copy(); ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, UserHandle.USER_SYSTEM); - XmlPullParser parser = getParserForByteStream(baos); + TypedXmlPullParser parser = getParserForByteStream(baos); mZenModeHelperSpy.readXml(parser, true, UserHandle.USER_SYSTEM); assertEquals("Config mismatch: current vs original: " @@ -1000,7 +1002,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, UserHandle.USER_SYSTEM); // Restore data for user 10. - XmlPullParser parser = getParserForByteStream(baos); + TypedXmlPullParser parser = getParserForByteStream(baos); mZenModeHelperSpy.readXml(parser, true, 10); ZenModeConfig actual = mZenModeHelperSpy.mConfigs.get(10); @@ -1045,7 +1047,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy(); ByteArrayOutputStream baos = writeXmlAndPurge(null); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); @@ -1086,7 +1088,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy(); ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, UserHandle.USER_SYSTEM); - XmlPullParser parser = getParserForByteStream(baos); + TypedXmlPullParser parser = getParserForByteStream(baos); mZenModeHelperSpy.readXml(parser, true, UserHandle.USER_SYSTEM); ZenModeConfig.ZenRule original = expected.automaticRules.get(ruleId); @@ -1113,7 +1115,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // set previous version ByteArrayOutputStream baos = writeXmlAndPurge(5); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); @@ -1133,7 +1135,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { + "<disallow visualEffects=\"511\" />" + "</zen>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -1149,7 +1151,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { + "<disallow visualEffects=\"511\" />" + "</zen>"; - parser = Xml.newPullParser(); + parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -1168,7 +1170,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { + "<disallow visualEffects=\"511\" />" + "</zen>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -1187,7 +1189,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { + "<disallow visualEffects=\"511\" />" + "</zen>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -1206,7 +1208,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { + "<disallow visualEffects=\"511\" />" + "</zen>"; - parser = Xml.newPullParser(); + parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -1222,7 +1224,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { + "<disallow visualEffects=\"511\" />" + "</zen>"; - parser = Xml.newPullParser(); + parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -1242,7 +1244,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // set previous version ByteArrayOutputStream baos = writeXmlAndPurge(5); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); @@ -1278,7 +1280,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // set previous version ByteArrayOutputStream baos = writeXmlAndPurge(5); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); @@ -1330,7 +1332,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // set previous version ByteArrayOutputStream baos = writeXmlAndPurge(5); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); @@ -1399,7 +1401,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // set previous version ByteArrayOutputStream baos = writeXmlAndPurge(5); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); @@ -1628,12 +1630,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { } /** - * Wrapper to use XmlPullParser as XmlResourceParser for Resources.getXml() + * Wrapper to use TypedXmlPullParser as XmlResourceParser for Resources.getXml() */ final class XmlResourceParserImpl implements XmlResourceParser { - private XmlPullParser parser; + private TypedXmlPullParser parser; - public XmlResourceParserImpl(XmlPullParser parser) { + public XmlResourceParserImpl(TypedXmlPullParser parser) { this.parser = parser; } diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index 7f4f3dd58812..0ed037c7e70a 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -65,8 +65,6 @@ android:turnScreenOn="true" /> <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ResumeWhilePausingActivity" android:resumeWhilePausing="true"/> - <activity android:name="com.android.server.wm.ScreenDecorWindowTests$TestActivity" - android:showWhenLocked="true" android:allowEmbedded="true"/> <activity android:name="com.android.server.wm.ActivityLeakTests$DetectLeakActivity" /> </application> 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 53ade0ea64be..2304efcaef65 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -71,6 +71,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.never; @@ -376,6 +377,27 @@ public class ActivityRecordTests extends WindowTestsBase { } @Test + public void testDestroyedActivityNotScheduleConfigChanged() throws RemoteException { + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setCreateTask(true) + .setConfigChanges(CONFIG_ORIENTATION) + .build(); + final Task task = activity.getTask(); + activity.setState(DESTROYED, "Testing"); + + final Configuration newConfig = new Configuration(task.getConfiguration()); + newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT + ? ORIENTATION_LANDSCAPE + : ORIENTATION_PORTRAIT; + task.onRequestedOverrideConfigurationChanged(newConfig); + + ensureActivityConfiguration(activity); + + verify(mAtm.getLifecycleManager(), never()) + .scheduleTransaction(any(), any(), isA(ActivityConfigurationChangeItem.class)); + } + + @Test public void testSetRequestedOrientationUpdatesConfiguration() throws Exception { final ActivityRecord activity = new ActivityBuilder(mAtm) .setCreateTask(true) @@ -401,7 +423,7 @@ public class ActivityRecordTests extends WindowTestsBase { // Mimic the behavior that display doesn't handle app's requested orientation. final DisplayContent dc = activity.getTask().getDisplayContent(); - doReturn(false).when(dc).onDescendantOrientationChanged(any(), any()); + doReturn(false).when(dc).onDescendantOrientationChanged(any()); doReturn(false).when(dc).handlesOrientationChangeFromDescendant(); final int requestedOrientation; @@ -1658,7 +1680,7 @@ public class ActivityRecordTests extends WindowTestsBase { final int rotatedOrentation = r.getConfiguration().orientation == ORIENTATION_PORTRAIT ? SCREEN_ORIENTATION_LANDSCAPE : SCREEN_ORIENTATION_PORTRAIT; - doReturn(false).when(r).onDescendantOrientationChanged(any(), any()); + doReturn(false).when(r).onDescendantOrientationChanged(any()); r.setOrientation(rotatedOrentation); } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java index 6ca69bf974d5..8cc515e83342 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -196,7 +196,7 @@ public class AppTransitionTests extends WindowTestsBase { @Test public void testCancelRemoteAnimationWhenFreeze() { final DisplayContent dc = createNewDisplay(Display.STATE_ON); - doReturn(false).when(dc).onDescendantOrientationChanged(any(), any()); + doReturn(false).when(dc).onDescendantOrientationChanged(any()); final WindowState exitingAppWindow = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "exiting app"); final ActivityRecord exitingActivity= exitingAppWindow.mActivityRecord; diff --git a/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java index 5828d02948a1..59b12e406d70 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java @@ -366,6 +366,30 @@ public class ConfigurationContainerTests { assertTrue(child.getConfiguration().windowConfiguration.getMaxBounds().isEmpty()); } + @Test + public void testOnRequestedOverrideConfigurationChangedOverrideMaxBounds() { + final TestConfigurationContainer root = + new TestConfigurationContainer(true /* providesMaxBounds */); + final Rect bounds = new Rect(0, 0, 10, 10); + final TestConfigurationContainer child = new TestConfigurationContainer(); + root.addChild(child); + final Configuration configuration = new Configuration(); + configuration.windowConfiguration.setBounds(bounds); + + root.onRequestedOverrideConfigurationChanged(configuration); + + assertEquals(bounds, root.getBounds()); + assertEquals(bounds, root.getConfiguration().windowConfiguration.getBounds()); + assertEquals(bounds, child.getBounds()); + assertEquals(bounds, child.getConfiguration().windowConfiguration.getBounds()); + + assertEquals(bounds, root.getMaxBounds()); + assertEquals(bounds, root.getConfiguration().windowConfiguration.getMaxBounds()); + assertEquals(bounds, child.getMaxBounds()); + assertEquals(bounds, child.getConfiguration().windowConfiguration.getMaxBounds()); + } + + /** * Contains minimal implementation of {@link ConfigurationContainer}'s abstract behavior needed * for testing. diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java index 06a6882dc698..bc91c709aeb1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java @@ -87,7 +87,7 @@ public class DisplayAreaGroupTest extends WindowTestsBase { final Task task = new TaskBuilder(mSupervisor) .setTaskDisplayArea(mTaskDisplayArea).setCreateActivity(true).build(); final ActivityRecord activity = task.getTopNonFinishingActivity(); - doReturn(true).when(mDisplayContent).onDescendantOrientationChanged(any(), any()); + doReturn(true).when(mDisplayContent).onDescendantOrientationChanged(any()); activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); // Display is portrait, DisplayAreaGroup inherits that diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java index 59b2d4fc282b..025c5a6bb180 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java @@ -468,14 +468,14 @@ public class DisplayAreaTest extends WindowTestsBase { activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); - verify(tda).onDescendantOrientationChanged(any(), any()); - verify(mDisplayContent, never()).onDescendantOrientationChanged(any(), any()); + verify(tda).onDescendantOrientationChanged(any()); + verify(mDisplayContent, never()).onDescendantOrientationChanged(any()); tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); - verify(tda, times(2)).onDescendantOrientationChanged(any(), any()); - verify(mDisplayContent).onDescendantOrientationChanged(any(), any()); + verify(tda, times(2)).onDescendantOrientationChanged(any()); + verify(mDisplayContent).onDescendantOrientationChanged(any()); } private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> { 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 d9217188582f..64065e96b0f2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1202,6 +1202,17 @@ public class DisplayContentTests extends WindowTestsBase { assertTrue(mNavBarWindow.getParent().isAnimating(WindowContainer.AnimationFlags.PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM)); + // If the visibility of insets state is changed, the rotated state should be updated too. + final InsetsState rotatedState = app.getFixedRotationTransformInsetsState(); + final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState(); + assertEquals(state.getSource(ITYPE_STATUS_BAR).isVisible(), + rotatedState.getSource(ITYPE_STATUS_BAR).isVisible()); + state.getSource(ITYPE_STATUS_BAR).setVisible( + !rotatedState.getSource(ITYPE_STATUS_BAR).isVisible()); + mDisplayContent.getInsetsStateController().notifyInsetsChanged(); + assertEquals(state.getSource(ITYPE_STATUS_BAR).isVisible(), + rotatedState.getSource(ITYPE_STATUS_BAR).isVisible()); + final Rect outFrame = new Rect(); final Rect outInsets = new Rect(); final Rect outStableInsets = new Rect(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java index 21bdc9e7785e..79b2da187680 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -236,8 +236,7 @@ public class DisplayPolicyTests extends WindowTestsBase { final WindowState activity = createBaseApplicationWindow(); activity.mAttrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; - policy.adjustWindowParamsLw(activity, activity.mAttrs, 0 /* callingPid */, - 0 /* callingUid */); + policy.adjustWindowParamsLw(activity, activity.mAttrs); } private WindowState createApplicationWindow() { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java index 94ffcdab4fa7..20775e84fd8f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java @@ -107,11 +107,9 @@ public class DisplayPolicyTestsBase extends WindowTestsBase { } void addWindow(WindowState win) { - final int callingPid = Binder.getCallingPid(); - final int callingUid = Binder.getCallingUid(); - mDisplayPolicy.adjustWindowParamsLw(win, win.mAttrs, callingPid, callingUid); - assertEquals(WindowManagerGlobal.ADD_OKAY, - mDisplayPolicy.validateAddingWindowLw(win.mAttrs, callingPid, callingUid)); + mDisplayPolicy.adjustWindowParamsLw(win, win.mAttrs); + assertEquals(WindowManagerGlobal.ADD_OKAY, mDisplayPolicy.validateAddingWindowLw( + win.mAttrs, Binder.getCallingPid(), Binder.getCallingUid())); mDisplayPolicy.addWindowLw(win, win.mAttrs); win.mHasSurface = true; } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java index eaedd58001c1..554160ccbb82 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java @@ -27,6 +27,8 @@ import static org.junit.Assert.assertTrue; import android.annotation.Nullable; import android.platform.test.annotations.Presubmit; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import android.view.Display; import android.view.DisplayAddress; @@ -245,7 +247,7 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { private String getStoredDisplayAttributeValue(TestStorage storage, String attr) throws Exception { try (InputStream stream = storage.openRead()) { - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(stream, StandardCharsets.UTF_8.name()); int type; while ((type = parser.next()) != XmlPullParser.START_TAG diff --git a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java index c3e1922a09cc..dfc2e35ffedf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java @@ -31,7 +31,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.R; import com.android.internal.util.Preconditions; -import com.android.server.wm.utils.FakeDeviceConfigInterface; +import com.android.server.testutils.FakeDeviceConfigInterface; import org.junit.After; import org.junit.Test; diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index 40f73b12f805..5c39bd0a316b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -322,7 +322,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { assertEquals(landActivity.findMainWindow(), win1); // Ensure that the display is in Landscape - landActivity.onDescendantOrientationChanged(landActivity.token, landActivity); + landActivity.onDescendantOrientationChanged(landActivity); assertEquals(Configuration.ORIENTATION_LANDSCAPE, mDefaultDisplay.getConfiguration().orientation); diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java deleted file mode 100644 index 25ba6db38e05..000000000000 --- a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; -import static android.graphics.Color.RED; -import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; -import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION; -import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.Gravity.BOTTOM; -import static android.view.Gravity.LEFT; -import static android.view.Gravity.RIGHT; -import static android.view.Gravity.TOP; -import static android.view.InsetsState.ITYPE_CLIMATE_BAR; -import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; -import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -import static com.google.common.truth.Truth.assertThat; - -import static org.junit.Assert.assertEquals; - -import android.app.Activity; -import android.app.ActivityOptions; -import android.app.Instrumentation; -import android.content.Context; -import android.content.Intent; -import android.graphics.PixelFormat; -import android.graphics.Point; -import android.hardware.display.DisplayManager; -import android.hardware.display.VirtualDisplay; -import android.media.ImageReader; -import android.os.Handler; -import android.os.SystemClock; -import android.platform.test.annotations.Presubmit; -import android.util.Pair; -import android.view.Display; -import android.view.DisplayInfo; -import android.view.View; -import android.view.WindowInsets; -import android.view.WindowManager; -import android.widget.TextView; - -import androidx.test.filters.SmallTest; - -import com.android.compatibility.common.util.SystemUtil; - -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.function.BooleanSupplier; - -/** - * Tests for the {@link android.view.WindowManager.LayoutParams#PRIVATE_FLAG_IS_SCREEN_DECOR} flag. - * - * Build/Install/Run: - * atest WmTests:ScreenDecorWindowTests - */ -// TODO: Add test for FLAG_FULLSCREEN which hides the status bar and also other flags. -// TODO: Test non-Activity windows. -@SmallTest -@Presubmit -public class ScreenDecorWindowTests { - - private final Context mContext = getInstrumentation().getTargetContext(); - private final Instrumentation mInstrumentation = getInstrumentation(); - - private WindowManager mWm; - private ArrayList<View> mWindows = new ArrayList<>(); - - private Activity mTestActivity; - private VirtualDisplay mDisplay; - private ImageReader mImageReader; - - private int mDecorThickness; - private int mHalfDecorThickness; - - @Before - public void setUp() { - final Pair<VirtualDisplay, ImageReader> result = createDisplay(); - mDisplay = result.first; - mImageReader = result.second; - final Display display = mDisplay.getDisplay(); - final Context dContext = mContext.createDisplayContext(display); - mWm = dContext.getSystemService(WindowManager.class); - mTestActivity = startActivityOnDisplay(TestActivity.class, display.getDisplayId()); - final Point size = new Point(); - mDisplay.getDisplay().getRealSize(size); - mDecorThickness = Math.min(size.x, size.y) / 3; - mHalfDecorThickness = mDecorThickness / 2; - } - - @After - public void tearDown() { - while (!mWindows.isEmpty()) { - removeWindow(mWindows.get(0)); - } - finishActivity(mTestActivity); - mDisplay.release(); - mImageReader.close(); - } - - @Test - public void testScreenSides() { - // Decor on top - final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness); - assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness); - - // Decor at the bottom - updateWindow(decorWindow, BOTTOM, MATCH_PARENT, mDecorThickness, 0, 0); - assertInsetGreaterOrEqual(mTestActivity, BOTTOM, mDecorThickness); - - // Decor to the left - updateWindow(decorWindow, LEFT, mDecorThickness, MATCH_PARENT, 0, 0); - assertInsetGreaterOrEqual(mTestActivity, LEFT, mDecorThickness); - - // Decor to the right - updateWindow(decorWindow, RIGHT, mDecorThickness, MATCH_PARENT, 0, 0); - assertInsetGreaterOrEqual(mTestActivity, RIGHT, mDecorThickness); - } - - // Decor windows (i.e windows using PRIVATE_FLAG_IS_SCREEN_DECOR) are no longer supported. - // PRIVATE_FLAG_IS_SCREEN_DECOR and related code will be deprecated/removed soon. - @Ignore - @Test - public void testMultipleDecors() { - // Test 2 decor windows on-top. - createDecorWindow(TOP, MATCH_PARENT, mHalfDecorThickness); - assertInsetGreaterOrEqual(mTestActivity, TOP, mHalfDecorThickness); - createDecorWindow(TOP, MATCH_PARENT, mDecorThickness); - assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness); - - // And one at the bottom. - createDecorWindow(BOTTOM, MATCH_PARENT, mHalfDecorThickness); - assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness); - assertInsetGreaterOrEqual(mTestActivity, BOTTOM, mHalfDecorThickness); - } - - @Test - public void testFlagChange() { - WindowInsets initialInsets = getInsets(mTestActivity); - - final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness); - assertTopInsetEquals(mTestActivity, mDecorThickness); - - updateWindow(decorWindow, TOP, MATCH_PARENT, mDecorThickness, - 0, PRIVATE_FLAG_IS_SCREEN_DECOR); - - // TODO: fix test and re-enable assertion. - // initialInsets was not actually immutable and just updated to the current insets, - // meaning this assertion never actually tested anything. Now that WindowInsets actually is - // immutable, it turns out the test was broken. - // assertTopInsetEquals(mTestActivity, initialInsets.getSystemWindowInsetTop()); - - updateWindow(decorWindow, TOP, MATCH_PARENT, mDecorThickness, - PRIVATE_FLAG_IS_SCREEN_DECOR, PRIVATE_FLAG_IS_SCREEN_DECOR); - assertTopInsetEquals(mTestActivity, mDecorThickness); - } - - @Test - public void testRemoval() { - WindowInsets initialInsets = getInsets(mTestActivity); - - final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness); - assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness); - - removeWindow(decorWindow); - assertTopInsetEquals(mTestActivity, initialInsets.getSystemWindowInsetTop()); - } - - @Test - public void testProvidesInsetsTypes() { - int[] providesInsetsTypes = new int[]{ITYPE_CLIMATE_BAR}; - final View win = createWindow("StatusBarSubPanel", TOP, MATCH_PARENT, mDecorThickness, RED, - FLAG_LAYOUT_IN_SCREEN, 0, providesInsetsTypes); - - assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness); - } - - private View createDecorWindow(int gravity, int width, int height) { - int[] providesInsetsTypes = - new int[]{gravity == TOP ? ITYPE_CLIMATE_BAR : ITYPE_EXTRA_NAVIGATION_BAR}; - return createWindow("decorWindow", gravity, width, height, RED, - FLAG_LAYOUT_IN_SCREEN, PRIVATE_FLAG_IS_SCREEN_DECOR, providesInsetsTypes); - } - - private View createWindow(String name, int gravity, int width, int height, int color, int flags, - int privateFlags, int[] providesInsetsTypes) { - - final View[] viewHolder = new View[1]; - final int finalFlag = flags - | FLAG_NOT_FOCUSABLE | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_NOT_TOUCHABLE; - - // Needs to run on the UI thread. - Handler.getMain().runWithScissors(() -> { - final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - width, height, TYPE_APPLICATION_OVERLAY, finalFlag, PixelFormat.OPAQUE); - lp.gravity = gravity; - lp.privateFlags |= privateFlags; - lp.providesInsetsTypes = providesInsetsTypes; - - final TextView view = new TextView(mContext); - view.setText("ScreenDecorWindowTests - " + name); - view.setBackgroundColor(color); - mWm.addView(view, lp); - mWindows.add(view); - viewHolder[0] = view; - }, 0); - - waitForIdle(); - return viewHolder[0]; - } - - private void updateWindow(View v, int gravity, int width, int height, - int privateFlags, int privateFlagsMask) { - // Needs to run on the UI thread. - Handler.getMain().runWithScissors(() -> { - final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) v.getLayoutParams(); - lp.gravity = gravity; - lp.width = width; - lp.height = height; - setPrivateFlags(lp, privateFlags, privateFlagsMask); - - mWm.updateViewLayout(v, lp); - }, 0); - - waitForIdle(); - } - - private void removeWindow(View v) { - Handler.getMain().runWithScissors(() -> mWm.removeView(v), 0); - mWindows.remove(v); - waitForIdle(); - } - - private WindowInsets getInsets(Activity a) { - return new WindowInsets(a.getWindow().getDecorView().getRootWindowInsets()); - } - - /** - * Set the flags of the window, as per the - * {@link WindowManager.LayoutParams WindowManager.LayoutParams} - * flags. - * - * @param flags The new window flags (see WindowManager.LayoutParams). - * @param mask Which of the window flag bits to modify. - */ - public void setPrivateFlags(WindowManager.LayoutParams lp, int flags, int mask) { - lp.flags = (lp.flags & ~mask) | (flags & mask); - } - - /** - * Asserts the top inset of {@param activity} is equal to {@param expected} waiting as needed. - */ - private void assertTopInsetEquals(Activity activity, int expected) { - waitForTopInsetEqual(activity, expected); - assertEquals(expected, getInsets(activity).getSystemWindowInsetTop()); - } - - private void waitForTopInsetEqual(Activity activity, int expected) { - waitFor(() -> getInsets(activity).getSystemWindowInsetTop() == expected); - } - - /** - * Asserts the inset at {@param side} of {@param activity} is equal to {@param expected} - * waiting as needed. - */ - private void assertInsetGreaterOrEqual(Activity activity, int side, int expected) { - waitForInsetGreaterOrEqual(activity, side, expected); - - final WindowInsets insets = getInsets(activity); - switch (side) { - case TOP: - assertThat(insets.getSystemWindowInsetTop()).isAtLeast(expected); - break; - case BOTTOM: - assertThat(insets.getSystemWindowInsetBottom()).isAtLeast(expected); - break; - case LEFT: - assertThat(insets.getSystemWindowInsetLeft()).isAtLeast(expected); - break; - case RIGHT: - assertThat(insets.getSystemWindowInsetRight()).isAtLeast(expected); - break; - } - } - - private void waitForInsetGreaterOrEqual(Activity activity, int side, int expected) { - waitFor(() -> { - final WindowInsets insets = getInsets(activity); - switch (side) { - case TOP: return insets.getSystemWindowInsetTop() >= expected; - case BOTTOM: return insets.getSystemWindowInsetBottom() >= expected; - case LEFT: return insets.getSystemWindowInsetLeft() >= expected; - case RIGHT: return insets.getSystemWindowInsetRight() >= expected; - default: return true; - } - }); - } - - private void waitFor(BooleanSupplier waitCondition) { - int retriesLeft = 5; - do { - if (waitCondition.getAsBoolean()) { - break; - } - SystemClock.sleep(500); - } while (retriesLeft-- > 0); - } - - private void finishActivity(Activity a) { - if (a == null) { - return; - } - a.finish(); - waitForIdle(); - } - - private void waitForIdle() { - mInstrumentation.waitForIdleSync(); - } - - private Activity startActivityOnDisplay(Class<?> cls, int displayId) { - final Intent intent = new Intent(mContext, cls); - intent.addFlags(FLAG_ACTIVITY_NEW_TASK); - final ActivityOptions options = ActivityOptions.makeBasic(); - options.setLaunchDisplayId(displayId); - - final Activity activity = SystemUtil.runWithShellPermissionIdentity( - () -> mInstrumentation.startActivitySync(intent, options.toBundle()), - "android.permission.ACTIVITY_EMBEDDING"); - waitForIdle(); - - assertEquals(displayId, activity.getDisplayId()); - return activity; - } - - private Pair<VirtualDisplay, ImageReader> createDisplay() { - final DisplayManager dm = mContext.getSystemService(DisplayManager.class); - final DisplayInfo displayInfo = new DisplayInfo(); - final Display defaultDisplay = dm.getDisplay(DEFAULT_DISPLAY); - defaultDisplay.getDisplayInfo(displayInfo); - final String name = "ScreenDecorWindowTests"; - int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; - - final ImageReader imageReader = ImageReader.newInstance( - displayInfo.logicalWidth, displayInfo.logicalHeight, PixelFormat.RGBA_8888, 2); - - final VirtualDisplay display = dm.createVirtualDisplay(name, displayInfo.logicalWidth, - displayInfo.logicalHeight, displayInfo.logicalDensityDpi, imageReader.getSurface(), - flags); - - return Pair.create(display, imageReader); - } - - public static class TestActivity extends Activity { - } -} 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 a4bf5948c6a3..da00198030e4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -698,7 +698,7 @@ public class SizeCompatTests extends WindowTestsBase { // Update with new activity requested orientation and recompute bounds with no previous // size compat cache. - verify(mTask).onDescendantOrientationChanged(any(), same(newActivity)); + verify(mTask).onDescendantOrientationChanged(same(newActivity)); verify(mTask).computeFullscreenBounds(any(), any(), any(), anyInt()); final Rect displayBounds = display.getBounds(); @@ -739,7 +739,7 @@ public class SizeCompatTests extends WindowTestsBase { // Update with new activity requested orientation and recompute bounds with no previous // size compat cache. - verify(mTask).onDescendantOrientationChanged(any(), same(newActivity)); + verify(mTask).onDescendantOrientationChanged(same(newActivity)); verify(mTask).computeFullscreenBounds(any(), any(), any(), anyInt()); final Rect displayBounds = display.getBounds(); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index d080fa0445ef..61d4a473215b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -70,6 +70,8 @@ import android.graphics.Rect; import android.os.IBinder; import android.platform.test.annotations.Presubmit; import android.util.DisplayMetrics; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import android.view.DisplayInfo; @@ -1064,12 +1066,12 @@ public class TaskRecordTests extends WindowTestsBase { activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation()); - verify(display).onDescendantOrientationChanged(any(), same(task)); + verify(display).onDescendantOrientationChanged(same(task)); reset(display); display.setWindowingMode(WINDOWING_MODE_FULLSCREEN); assertEquals(SCREEN_ORIENTATION_LANDSCAPE, task.getOrientation()); - verify(display).onDescendantOrientationChanged(any(), same(task)); + verify(display).onDescendantOrientationChanged(same(task)); } private Task getTestTask() { @@ -1097,7 +1099,7 @@ public class TaskRecordTests extends WindowTestsBase { private byte[] serializeToBytes(Task r) throws Exception { try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { - final XmlSerializer serializer = Xml.newSerializer(); + final TypedXmlSerializer serializer = Xml.newFastSerializer(); serializer.setOutput(os, "UTF-8"); serializer.startDocument(null, true); serializer.startTag(null, TASK_TAG); @@ -1112,7 +1114,7 @@ public class TaskRecordTests extends WindowTestsBase { private Task restoreFromBytes(byte[] in) throws IOException, XmlPullParserException { try (Reader reader = new InputStreamReader(new ByteArrayInputStream(in))) { - final XmlPullParser parser = Xml.newPullParser(); + final TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(reader); assertEquals(XmlPullParser.START_TAG, parser.next()); assertEquals(TASK_TAG, parser.getName()); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index 3d8adbd215bd..573da896eff3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -56,7 +56,6 @@ import static org.junit.Assert.assertTrue; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; -import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.view.IRemoteAnimationFinishedCallback; @@ -787,12 +786,11 @@ public class WindowContainerTests extends WindowTestsBase { final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); final TestWindowContainer root = spy(builder.build()); - final IBinder binder = mock(IBinder.class); final ActivityRecord activityRecord = mock(ActivityRecord.class); final TestWindowContainer child = root.addChildWindow(); - child.setOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED, binder, activityRecord); - verify(root).onDescendantOrientationChanged(binder, activityRecord); + child.setOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED, activityRecord); + verify(root).onDescendantOrientationChanged(activityRecord); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java index 52100116df53..7a0ef0d7d7a9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java @@ -32,7 +32,7 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; -import com.android.server.wm.utils.FakeDeviceConfigInterface; +import com.android.server.testutils.FakeDeviceConfigInterface; import org.junit.After; import org.junit.Before; diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 3af88e1c6354..d585b2374783 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -1213,9 +1213,11 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser Slog.d(TAG, "Clear notification"); mUsbNotificationId = 0; } - // Not relevant for automotive. - if (mContext.getPackageManager().hasSystemFeature( + // Not relevant for automotive and watch. + if ((mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_AUTOMOTIVE) + || mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WATCH)) && id == SystemMessage.NOTE_USB_CHARGING) { mUsbNotificationId = 0; return; diff --git a/telephony/java/android/telephony/CallForwardingInfo.java b/telephony/java/android/telephony/CallForwardingInfo.java index 6ae6d002d990..aeac36e3a5f5 100644 --- a/telephony/java/android/telephony/CallForwardingInfo.java +++ b/telephony/java/android/telephony/CallForwardingInfo.java @@ -86,7 +86,7 @@ public final class CallForwardingInfo implements Parcelable { * Call forwarding reason types * @hide */ - @IntDef(flag = true, prefix = { "REASON_" }, value = { + @IntDef(prefix = { "REASON_" }, value = { REASON_UNCONDITIONAL, REASON_BUSY, REASON_NO_REPLY, diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 643d8c8ba2a5..9288f7925eab 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -9463,7 +9463,7 @@ public class TelephonyManager { } /** @hide */ - @IntDef(flag = true, prefix = { "CDMA_SUBSCRIPTION_" }, value = { + @IntDef(prefix = { "CDMA_SUBSCRIPTION_" }, value = { CDMA_SUBSCRIPTION_UNKNOWN, CDMA_SUBSCRIPTION_RUIM_SIM, CDMA_SUBSCRIPTION_NV diff --git a/telephony/java/android/telephony/ims/DelegateMessageCallback.java b/telephony/java/android/telephony/ims/DelegateMessageCallback.java index beec4a680d78..0d82a54a0f32 100644 --- a/telephony/java/android/telephony/ims/DelegateMessageCallback.java +++ b/telephony/java/android/telephony/ims/DelegateMessageCallback.java @@ -17,6 +17,7 @@ package android.telephony.ims; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.telephony.ims.stub.SipDelegate; /** @@ -30,6 +31,7 @@ import android.telephony.ims.stub.SipDelegate; * </ul> * @hide */ +@SystemApi public interface DelegateMessageCallback { /** diff --git a/telephony/java/android/telephony/ims/DelegateRegistrationState.java b/telephony/java/android/telephony/ims/DelegateRegistrationState.java index 4facfa77de21..3558a9b79ce0 100644 --- a/telephony/java/android/telephony/ims/DelegateRegistrationState.java +++ b/telephony/java/android/telephony/ims/DelegateRegistrationState.java @@ -18,14 +18,14 @@ package android.telephony.ims; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; 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.ArrayList; -import java.util.List; import java.util.Objects; import java.util.Set; @@ -34,6 +34,7 @@ import java.util.Set; * ImsService. * @hide */ +@SystemApi public final class DelegateRegistrationState implements Parcelable { /** @@ -114,14 +115,14 @@ public final class DelegateRegistrationState implements Parcelable { }) public @interface DeregisteringReason {} - private final ArrayList<String> mRegisteredTags = new ArrayList<>(); - private final ArrayList<FeatureTagState> mDeregisteringTags = new ArrayList<>(); - private final ArrayList<FeatureTagState> mDeregisteredTags = new ArrayList<>(); + private ArraySet<String> mRegisteredTags = new ArraySet<>(); + private final ArraySet<FeatureTagState> mDeregisteringTags = new ArraySet<>(); + private final ArraySet<FeatureTagState> mDeregisteredTags = new ArraySet<>(); /** * Builder used to create new instances of {@link DelegateRegistrationState}. */ - public static class Builder { + public static final class Builder { private final DelegateRegistrationState mState; @@ -135,10 +136,8 @@ public final class DelegateRegistrationState implements Parcelable { * @param featureTag The IMS media feature tag included in the current IMS registration. * @return The in-progress Builder instance for RegistrationState. */ - public Builder addRegisteredFeatureTag(@NonNull String featureTag) { - if (!mState.mRegisteredTags.contains(featureTag)) { - mState.mRegisteredTags.add(featureTag); - } + public @NonNull Builder addRegisteredFeatureTag(@NonNull String featureTag) { + mState.mRegisteredTags.add(featureTag); return this; } @@ -148,7 +147,8 @@ public final class DelegateRegistrationState implements Parcelable { * @param featureTags The IMS media feature tags included in the current IMS registration. * @return The in-progress Builder instance for RegistrationState. */ - public Builder addRegisteredFeatureTags(@NonNull Set<String> featureTags) { + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder addRegisteredFeatureTags(@NonNull Set<String> featureTags) { mState.mRegisteredTags.addAll(featureTags); return this; } @@ -167,13 +167,9 @@ public final class DelegateRegistrationState implements Parcelable { * The availability of the feature tag depends on the {@link DeregisteringReason}. * @return The in-progress Builder instance for RegistrationState. */ - public Builder addDeregisteringFeatureTag(@NonNull String featureTag, + public @NonNull Builder addDeregisteringFeatureTag(@NonNull String featureTag, @DeregisteringReason int reason) { - boolean ftExists = mState.mDeregisteringTags.stream().anyMatch( - f -> f.getFeatureTag().equals(featureTag)); - if (!ftExists) { - mState.mDeregisteringTags.add(new FeatureTagState(featureTag, reason)); - } + mState.mDeregisteringTags.add(new FeatureTagState(featureTag, reason)); return this; } @@ -185,20 +181,16 @@ public final class DelegateRegistrationState implements Parcelable { * @param reason The reason why the media feature tag has been deregistered. * @return The in-progress Builder instance for RegistrationState. */ - public Builder addDeregisteredFeatureTag(@NonNull String featureTag, + public @NonNull Builder addDeregisteredFeatureTag(@NonNull String featureTag, @DeregisteredReason int reason) { - boolean ftExists = mState.mDeregisteredTags.stream().anyMatch( - f -> f.getFeatureTag().equals(featureTag)); - if (!ftExists) { - mState.mDeregisteredTags.add(new FeatureTagState(featureTag, reason)); - } + mState.mDeregisteredTags.add(new FeatureTagState(featureTag, reason)); return this; } /** * @return the finalized instance. */ - public DelegateRegistrationState build() { + public @NonNull DelegateRegistrationState build() { return mState; } } @@ -212,7 +204,7 @@ public final class DelegateRegistrationState implements Parcelable { * Used for unparcelling only. */ private DelegateRegistrationState(Parcel source) { - source.readList(mRegisteredTags, null /*classloader*/); + mRegisteredTags = (ArraySet<String>) source.readArraySet(null); readStateFromParcel(source, mDeregisteringTags); readStateFromParcel(source, mDeregisteredTags); } @@ -268,7 +260,8 @@ public final class DelegateRegistrationState implements Parcelable { return new ArraySet<>(mDeregisteredTags); } - public static final Creator<DelegateRegistrationState> CREATOR = + + public static final @NonNull Creator<DelegateRegistrationState> CREATOR = new Creator<DelegateRegistrationState>() { @Override public DelegateRegistrationState createFromParcel(Parcel source) { @@ -287,13 +280,13 @@ public final class DelegateRegistrationState implements Parcelable { } @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeList(mRegisteredTags); + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeArraySet(mRegisteredTags); writeStateToParcel(dest, mDeregisteringTags); writeStateToParcel(dest, mDeregisteredTags); } - private void writeStateToParcel(Parcel dest, List<FeatureTagState> state) { + private void writeStateToParcel(Parcel dest, Set<FeatureTagState> state) { dest.writeInt(state.size()); for (FeatureTagState s : state) { dest.writeString(s.getFeatureTag()); @@ -301,11 +294,12 @@ public final class DelegateRegistrationState implements Parcelable { } } - private void readStateFromParcel(Parcel source, List<FeatureTagState> emptyState) { + private void readStateFromParcel(Parcel source, Set<FeatureTagState> emptyState) { int len = source.readInt(); for (int i = 0; i < len; i++) { String ft = source.readString(); int reason = source.readInt(); + emptyState.add(new FeatureTagState(ft, reason)); } } diff --git a/telephony/java/android/telephony/ims/DelegateRequest.java b/telephony/java/android/telephony/ims/DelegateRequest.java index 73d0840177dd..c322d924182a 100644 --- a/telephony/java/android/telephony/ims/DelegateRequest.java +++ b/telephony/java/android/telephony/ims/DelegateRequest.java @@ -17,6 +17,7 @@ package android.telephony.ims; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.telephony.ims.stub.SipDelegate; @@ -31,6 +32,7 @@ import java.util.Set; * SipDelegateConnection given back to the requesting application. * @hide */ +@SystemApi public final class DelegateRequest implements Parcelable { private final ArrayList<String> mFeatureTags; @@ -52,7 +54,7 @@ public final class DelegateRequest implements Parcelable { * @return the list of IMS feature tag associated with this DelegateRequest in the format * defined in RCC.07 section 2.6.1.3. */ - public Set<String> getFeatureTags() { + public @NonNull Set<String> getFeatureTags() { return new ArraySet<>(mFeatureTags); } @@ -70,7 +72,7 @@ public final class DelegateRequest implements Parcelable { } @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeList(mFeatureTags); } diff --git a/telephony/java/android/telephony/ims/DelegateStateCallback.java b/telephony/java/android/telephony/ims/DelegateStateCallback.java index 0f1afc42249e..fb659490d546 100644 --- a/telephony/java/android/telephony/ims/DelegateStateCallback.java +++ b/telephony/java/android/telephony/ims/DelegateStateCallback.java @@ -18,10 +18,11 @@ package android.telephony.ims; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.telephony.ims.stub.SipDelegate; import android.telephony.ims.stub.SipTransportImplBase; -import java.util.List; +import java.util.Set; /** * Callback interface to notify a remote application of the following: @@ -34,26 +35,24 @@ import java.util.List; * </ul> * @hide */ +@SystemApi public interface DelegateStateCallback { /** * This must be called by the ImsService after {@link SipTransportImplBase#createSipDelegate} is * called by the framework to notify the framework and remote application that the * {@link SipDelegate} has been successfully created. - * - * @param delegate The SipDelegate created to service the DelegateRequest. - * @param deniedTags A List of {@link FeatureTagState}, which contains the feature tags + * @param delegate The SipDelegate created to service the DelegateRequest. + * @param deniedTags A Set of {@link FeatureTagState}s, which contain the feature tags * associated with this {@link SipDelegate} that have no access to send/receive SIP messages * as well as a reason for why the feature tag is denied. For more information on the reason * why the feature tag was denied access, see the * {@link SipDelegateManager.DeniedReason} reasons. This is considered a permanent denial due * to this {@link SipDelegate} not supporting a feature or this ImsService already * implementing this feature elsewhere. If all features of this {@link SipDelegate} are - * denied, {@link #onCreated(SipDelegate, List)} should still be called as the framework will - * later call {@link SipTransportImplBase#destroySipDelegate(SipDelegate, int)} to clean the - * delegate up. + * denied, this method should still be called. */ - void onCreated(@NonNull SipDelegate delegate, @Nullable List<FeatureTagState> deniedTags); + void onCreated(@NonNull SipDelegate delegate, @Nullable Set<FeatureTagState> deniedTags); /** * This must be called by the ImsService after the framework calls diff --git a/telephony/java/android/telephony/ims/FeatureTagState.java b/telephony/java/android/telephony/ims/FeatureTagState.java index 060be6f2510d..3622065c5fe8 100644 --- a/telephony/java/android/telephony/ims/FeatureTagState.java +++ b/telephony/java/android/telephony/ims/FeatureTagState.java @@ -17,6 +17,7 @@ package android.telephony.ims; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.telephony.ims.stub.DelegateConnectionStateCallback; @@ -39,6 +40,7 @@ import java.util.Objects; * currently available. * @hide */ +@SystemApi public final class FeatureTagState implements Parcelable { private final String mFeatureTag; @@ -48,8 +50,8 @@ public final class FeatureTagState implements Parcelable { * Associate an IMS feature tag with its current state. See {@link DelegateRegistrationState} * and {@link DelegateConnectionStateCallback#onFeatureTagStatusChanged( * DelegateRegistrationState, List)} and - * {@link DelegateStateCallback#onCreated(SipDelegate, List)} for examples on how and when this - * is used. + * {@link DelegateStateCallback#onCreated(SipDelegate, java.util.Set)} for examples on how and + * when this is used. * * @param featureTag The IMS feature tag that is deregistered, in the process of * deregistering, or denied. @@ -93,12 +95,12 @@ public final class FeatureTagState implements Parcelable { } @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString(mFeatureTag); dest.writeInt(mState); } - public static final Creator<FeatureTagState> CREATOR = new Creator<FeatureTagState>() { + public static final @NonNull Creator<FeatureTagState> CREATOR = new Creator<FeatureTagState>() { @Override public FeatureTagState createFromParcel(Parcel source) { return new FeatureTagState(source); diff --git a/telephony/java/android/telephony/ims/ImsExternalCallState.java b/telephony/java/android/telephony/ims/ImsExternalCallState.java index fdf636c323b6..c663e393fe06 100644 --- a/telephony/java/android/telephony/ims/ImsExternalCallState.java +++ b/telephony/java/android/telephony/ims/ImsExternalCallState.java @@ -49,8 +49,7 @@ public final class ImsExternalCallState implements Parcelable { public static final int CALL_STATE_TERMINATED = 2; /**@hide*/ - @IntDef(flag = true, - value = { + @IntDef(value = { CALL_STATE_CONFIRMED, CALL_STATE_TERMINATED }, @@ -59,8 +58,7 @@ public final class ImsExternalCallState implements Parcelable { public @interface ExternalCallState {} /**@hide*/ - @IntDef(flag = true, - value = { + @IntDef(value = { ImsCallProfile.CALL_TYPE_VOICE, ImsCallProfile.CALL_TYPE_VT_TX, ImsCallProfile.CALL_TYPE_VT_RX, diff --git a/telephony/java/android/telephony/ims/ImsSsData.java b/telephony/java/android/telephony/ims/ImsSsData.java index fb8e5d37875b..868dea6a3121 100644 --- a/telephony/java/android/telephony/ims/ImsSsData.java +++ b/telephony/java/android/telephony/ims/ImsSsData.java @@ -72,7 +72,7 @@ public final class ImsSsData implements Parcelable { /**@hide*/ - @IntDef(flag = true, prefix = {"SS_"}, value = { + @IntDef(prefix = {"SS_"}, value = { SS_ACTIVATION, SS_DEACTIVATION, SS_INTERROGATION, @@ -89,7 +89,7 @@ public final class ImsSsData implements Parcelable { public static final int SS_ERASURE = 4; /**@hide*/ - @IntDef(flag = true, prefix = {"SS_"}, value = { + @IntDef(prefix = {"SS_"}, value = { SS_ALL_TELE_AND_BEARER_SERVICES, SS_ALL_TELESEVICES, SS_TELEPHONY, @@ -190,7 +190,7 @@ public final class ImsSsData implements Parcelable { public static final int RESULT_SUCCESS = 0; /** @hide */ - @IntDef(flag = true, prefix = { "SS_" }, value = { + @IntDef(prefix = { "SS_" }, value = { SS_CFU, SS_CF_BUSY, SS_CF_NO_REPLY, diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java index d12a6aef5186..5848be8b0bf2 100644 --- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java +++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java @@ -105,10 +105,17 @@ public final class RcsContactUceCapability implements Parcelable { public @interface RequestResult {} /** + * The base class of {@link OptionsBuilder} and {@link PresenceBuilder} + */ + public static abstract class RcsUcsCapabilityBuilder { + public abstract @NonNull RcsContactUceCapability build(); + } + + /** * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were * queried through SIP OPTIONS. */ - public static class OptionsBuilder { + public static class OptionsBuilder extends RcsUcsCapabilityBuilder { private final RcsContactUceCapability mCapabilities; @@ -155,6 +162,7 @@ public final class RcsContactUceCapability implements Parcelable { /** * @return the constructed instance. */ + @Override public @NonNull RcsContactUceCapability build() { return mCapabilities; } @@ -164,7 +172,7 @@ public final class RcsContactUceCapability implements Parcelable { * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were * queried through a presence server. */ - public static class PresenceBuilder { + public static class PresenceBuilder extends RcsUcsCapabilityBuilder { private final RcsContactUceCapability mCapabilities; @@ -205,6 +213,7 @@ public final class RcsContactUceCapability implements Parcelable { /** * @return the RcsContactUceCapability instance. */ + @Override public @NonNull RcsContactUceCapability build() { return mCapabilities; } diff --git a/telephony/java/android/telephony/ims/SipDelegateConnection.java b/telephony/java/android/telephony/ims/SipDelegateConnection.java index 6bfdc2c6d48a..c3cc1edf590b 100644 --- a/telephony/java/android/telephony/ims/SipDelegateConnection.java +++ b/telephony/java/android/telephony/ims/SipDelegateConnection.java @@ -17,6 +17,7 @@ package android.telephony.ims; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.telephony.ims.stub.SipDelegate; /** @@ -36,6 +37,7 @@ import android.telephony.ims.stub.SipDelegate; * @see SipDelegateManager#createSipDelegate * @hide */ +@SystemApi public interface SipDelegateConnection { /** @@ -47,9 +49,8 @@ public interface SipDelegateConnection { * @param sipMessage The SipMessage to be sent. * @param configVersion The SipDelegateImsConfiguration version used to construct the * SipMessage. See {@link SipDelegateImsConfiguration#getVersion} for more - * information on this parameter and why it is used. */ - void sendMessage(@NonNull SipMessage sipMessage, int configVersion); + void sendMessage(@NonNull SipMessage sipMessage, long configVersion); /** * Notify the {@link SipDelegate} that a SIP message received from diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java index 8abd0ee94865..eddbb1002f20 100644 --- a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java +++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java @@ -17,7 +17,10 @@ package android.telephony.ims; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.StringDef; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; @@ -47,7 +50,8 @@ import java.lang.annotation.RetentionPolicy; * update. * @hide */ -public class SipDelegateImsConfiguration implements Parcelable { +@SystemApi +public final class SipDelegateImsConfiguration implements Parcelable { /** * IPV4 Address type. @@ -354,7 +358,7 @@ public class SipDelegateImsConfiguration implements Parcelable { /** * Builder class to be used when constructing a new SipDelegateImsConfiguration. */ - public static class Builder { + public static final class Builder { private final long mVersion; private final PersistableBundle mBundle; @@ -381,7 +385,10 @@ public class SipDelegateImsConfiguration implements Parcelable { /** * Put a string value into this configuration bundle for the given key. */ - public Builder putString(@StringConfigKey String key, String value) { + // getString is available below. + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder addString(@NonNull @StringConfigKey String key, + @NonNull String value) { mBundle.putString(key, value); return this; } @@ -389,7 +396,9 @@ public class SipDelegateImsConfiguration implements Parcelable { /** * Replace the existing default value with a new value for a given key. */ - public Builder putInt(@IntConfigKey String key, int value) { + // getInt is available below. + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder addInt(@NonNull @IntConfigKey String key, int value) { mBundle.putInt(key, value); return this; } @@ -397,7 +406,9 @@ public class SipDelegateImsConfiguration implements Parcelable { /** * Replace the existing default value with a new value for a given key. */ - public Builder putBoolean(@BooleanConfigKey String key, boolean value) { + // getBoolean is available below. + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder addBoolean(@NonNull @BooleanConfigKey String key, boolean value) { mBundle.putBoolean(key, value); return this; } @@ -405,7 +416,7 @@ public class SipDelegateImsConfiguration implements Parcelable { /** * @return a new SipDelegateImsConfiguration from this Builder. */ - public SipDelegateImsConfiguration build() { + public @NonNull SipDelegateImsConfiguration build() { return new SipDelegateImsConfiguration(mVersion, mBundle); } } @@ -424,30 +435,38 @@ public class SipDelegateImsConfiguration implements Parcelable { } /** + * @return {@code true} if this configuration object has a an entry for the key specified, + * {@code false} if it does not. + */ + public boolean containsKey(@NonNull String key) { + return mBundle.containsKey(key); + } + + /** * @return the string value associated with a given key or {@code null} if it doesn't exist. */ - public @StringConfigKey String getString(String key) { + public @Nullable @StringConfigKey String getString(@NonNull String key) { return mBundle.getString(key); } /** - * @return the Integer value associated with a given key or {@code null} if the value doesn't - * exist. + * @return the integer value associated with a given key if it exists or the supplied default + * value if it does not. */ - public @IntConfigKey Integer getInt(String key) { + public @IntConfigKey int getInt(@NonNull String key, int defaultValue) { if (!mBundle.containsKey(key)) { - return null; + return defaultValue; } return mBundle.getInt(key); } /** - * @return the Integer value associated with a given key or {@code null} if the value doesn't - * exist. + * @return the boolean value associated with a given key or the supplied default value if the + * value doesn't exist in the bundle. */ - public @BooleanConfigKey Boolean getBoolen(String key) { + public @BooleanConfigKey boolean getBoolean(@NonNull String key, boolean defaultValue) { if (!mBundle.containsKey(key)) { - return null; + return defaultValue; } return mBundle.getBoolean(key); } @@ -455,7 +474,7 @@ public class SipDelegateImsConfiguration implements Parcelable { /** * @return a shallow copy of the full configuration. */ - public PersistableBundle copyBundle() { + public @NonNull PersistableBundle copyBundle() { return new PersistableBundle(mBundle); } @@ -479,12 +498,12 @@ public class SipDelegateImsConfiguration implements Parcelable { } @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeLong(mVersion); dest.writePersistableBundle(mBundle); } - public static final Creator<SipDelegateImsConfiguration> CREATOR = + public static final @NonNull Creator<SipDelegateImsConfiguration> CREATOR = new Creator<SipDelegateImsConfiguration>() { @Override public SipDelegateImsConfiguration createFromParcel(Parcel source) { diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java index 190a792b5a9a..2ec88ff27f93 100644 --- a/telephony/java/android/telephony/ims/SipDelegateManager.java +++ b/telephony/java/android/telephony/ims/SipDelegateManager.java @@ -54,7 +54,6 @@ public class SipDelegateManager { * The SIP message has failed being sent or received for an unknown reason. * <p> * The caller should retry a message that failed with this response. - * @hide */ public static final int MESSAGE_FAILURE_REASON_UNKNOWN = 0; @@ -64,47 +63,40 @@ public class SipDelegateManager { * <p> * This is considered a permanent error and the system will automatically begin the teardown and * destruction of the SipDelegate. No further messages should be sent on this transport. - * @hide */ public static final int MESSAGE_FAILURE_REASON_DELEGATE_DEAD = 1; /** * The message has not been sent/received because the delegate is in the process of closing and * has become unavailable. No further messages should be sent/received on this delegate. - * @hide */ public static final int MESSAGE_FAILURE_REASON_DELEGATE_CLOSED = 2; /** * The SIP message has an invalid start line and the message can not be sent. - * @hide */ public static final int MESSAGE_FAILURE_REASON_INVALID_START_LINE = 3; /** * One or more of the header fields in the header section of the outgoing SIP message is invalid * and the SIP message can not be sent. - * @hide */ public static final int MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS = 4; /** * The body content of the SIP message is invalid and the message can not be sent. - * @hide */ public static final int MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT = 5; /** * The feature tag associated with the outgoing message does not match any known feature tags * and this message can not be sent. - * @hide */ public static final int MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG = 6; /** * The feature tag associated with the outgoing message is not enabled for the associated * SipDelegateConnection and can not be sent. - * @hide */ public static final int MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE = 7; @@ -113,7 +105,6 @@ public class SipDelegateManager { * <p> * This message should be retried when connectivity to the network is re-established. See * {@link android.net.ConnectivityManager.NetworkCallback} for how this can be determined. - * @hide */ public static final int MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE = 8; @@ -124,7 +115,6 @@ public class SipDelegateManager { * This is considered a temporary failure, the message should not be retried until an IMS * registration change callback is received via * {@link DelegateConnectionStateCallback#onFeatureTagStatusChanged} - * @hide */ public static final int MESSAGE_FAILURE_REASON_NOT_REGISTERED = 9; @@ -135,7 +125,6 @@ public class SipDelegateManager { * <p> * The @link SipMessage} should be recreated using the newest * {@link SipDelegateImsConfiguration} and sent again. - * @hide */ public static final int MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION = 10; @@ -146,7 +135,6 @@ public class SipDelegateManager { * This is considered a temporary error and the {@link SipDelegateConnection} should resend the * message once {@link DelegateRegistrationState#DEREGISTERING_REASON_FEATURE_TAGS_CHANGING} is * no longer reported. - * @hide */ public static final int MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION = 11; @@ -171,7 +159,6 @@ public class SipDelegateManager { /** * Access to use this feature tag has been denied for an unknown reason. - * @hide */ public static final int DENIED_REASON_UNKNOWN = 0; @@ -179,14 +166,12 @@ public class SipDelegateManager { * This feature tag is allowed to be used by this SipDelegateConnection, but it is in use by * another SipDelegateConnection and can not be associated with this delegate. The feature tag * will stay in this state until the feature tag is release by the other application. - * @hide */ public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1; /** * Access to use this feature tag has been denied because this application does not have the * permissions required to access this feature tag. - * @hide */ public static final int DENIED_REASON_NOT_ALLOWED = 2; @@ -194,14 +179,12 @@ public class SipDelegateManager { * Access to use this feature tag has been denied because single registration is not allowed by * the carrier at this time. The application should fall back to dual registration if * applicable. - * @hide */ public static final int DENIED_REASON_SINGLE_REGISTRATION_NOT_ALLOWED = 3; /** * This feature tag is not recognized as a valid feature tag by the SipDelegate and has been * denied. - * @hide */ public static final int DENIED_REASON_INVALID = 4; @@ -218,33 +201,28 @@ public class SipDelegateManager { /** * The SipDelegate has closed due to an unknown reason. - * @hide */ public static final int SIP_DELEGATE_DESTROY_REASON_UNKNOWN = 0; /** * The SipDelegate has closed because the IMS service has died unexpectedly. - * @hide */ public static final int SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD = 1; /** * The SipDelegate has closed because the IMS application has requested that the connection be * destroyed. - * @hide */ public static final int SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP = 2; /** * The SipDelegate has been closed due to the user disabling RCS. - * @hide */ public static final int SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS = 3; /** * The SipDelegate has been closed due to the subscription associated with this delegate being * torn down. - * @hide */ public static final int SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN = 4; @@ -331,7 +309,6 @@ public class SipDelegateManager { * SipDelegateConnection. * @throws ImsException Thrown if there was a problem communicating with the ImsService * associated with this SipDelegateManager. See {@link ImsException#getCode()}. - * @hide */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void createSipDelegate(@NonNull DelegateRequest request, @NonNull Executor executor, @@ -366,7 +343,6 @@ public class SipDelegateManager { * This will also clean up all related callbacks in the associated ImsService. * @param delegateConnection The SipDelegateConnection to destroy. * @param reason The reason for why this SipDelegateConnection was destroyed. - * @hide */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void destroySipDelegate(@NonNull SipDelegateConnection delegateConnection, diff --git a/telephony/java/android/telephony/ims/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java index c3b1be2d7fc8..1539224dedcf 100644 --- a/telephony/java/android/telephony/ims/SipMessage.java +++ b/telephony/java/android/telephony/ims/SipMessage.java @@ -17,10 +17,14 @@ package android.telephony.ims; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import java.util.Arrays; +import java.util.Objects; + /** * Represents a partially encoded SIP message. See RFC 3261 for more information on how SIP * messages are structured and used. @@ -29,6 +33,7 @@ import android.os.Parcelable; * verification and should not be used as a generic SIP message container. * @hide */ +@SystemApi public final class SipMessage implements Parcelable { // Should not be set to true for production! private static final boolean IS_DEBUGGING = Build.IS_ENG; @@ -95,14 +100,14 @@ public final class SipMessage implements Parcelable { } @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString(mStartLine); dest.writeString(mHeaderSection); dest.writeInt(mContent.length); dest.writeByteArray(mContent); } - public static final Creator<SipMessage> CREATOR = new Creator<SipMessage>() { + public static final @NonNull Creator<SipMessage> CREATOR = new Creator<SipMessage>() { @Override public SipMessage createFromParcel(Parcel source) { return new SipMessage(source); @@ -152,4 +157,21 @@ public final class SipMessage implements Parcelable { } return startLine; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SipMessage that = (SipMessage) o; + return mStartLine.equals(that.mStartLine) + && mHeaderSection.equals(that.mHeaderSection) + && Arrays.equals(mContent, that.mContent); + } + + @Override + public int hashCode() { + int result = Objects.hash(mStartLine, mHeaderSection); + result = 31 * result + Arrays.hashCode(mContent); + return result; + } } diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl index 477ee958e1e8..5d6766a65155 100644 --- a/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl +++ b/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl @@ -23,7 +23,7 @@ import android.telephony.ims.SipMessage; * {@hide} */ oneway interface ISipDelegate { - void sendMessage(in SipMessage sipMessage, int configVersion); + void sendMessage(in SipMessage sipMessage, long configVersion); void notifyMessageReceived(in String viaTransactionId); void notifyMessageReceiveError(in String viaTransactionId, int reason); diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java index a7f62cc32be1..522ad8160870 100644 --- a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java +++ b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java @@ -29,12 +29,13 @@ import android.telephony.ims.SipDelegateManager; import android.telephony.ims.SipMessage; import android.telephony.ims.stub.SipDelegate; -import java.util.List; +import java.util.ArrayList; +import java.util.Set; import java.util.concurrent.Executor; /** * Implementation of callbacks by wrapping the internal AIDL from telephony. Also implements - * ISipDelegate internally when {@link DelegateStateCallback#onCreated(SipDelegate, List)} is called + * ISipDelegate internally when {@link DelegateStateCallback#onCreated(SipDelegate, Set)} is called * in order to trampoline events back to telephony. * @hide */ @@ -42,7 +43,7 @@ public class SipDelegateAidlWrapper implements DelegateStateCallback, DelegateMe private final ISipDelegate.Stub mDelegateBinder = new ISipDelegate.Stub() { @Override - public void sendMessage(SipMessage sipMessage, int configVersion) { + public void sendMessage(SipMessage sipMessage, long configVersion) { SipDelegate d = mDelegate; final long token = Binder.clearCallingIdentity(); try { @@ -136,10 +137,10 @@ public class SipDelegateAidlWrapper implements DelegateStateCallback, DelegateMe @Override public void onCreated(@NonNull SipDelegate delegate, - @Nullable List<FeatureTagState> deniedTags) { + @Nullable Set<FeatureTagState> deniedTags) { mDelegate = delegate; try { - mStateBinder.onCreated(mDelegateBinder, deniedTags); + mStateBinder.onCreated(mDelegateBinder, new ArrayList<>(deniedTags)); } catch (RemoteException e) { // BinderDied will trigger destroySipDelegate, so just ignore this locally. } diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java index 3bd1a462b31a..29ba8e2d50c4 100644 --- a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java +++ b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java @@ -158,7 +158,7 @@ public class SipDelegateConnectionAidlWrapper implements SipDelegateConnection, } @Override - public void sendMessage(SipMessage sipMessage, int configVersion) { + public void sendMessage(SipMessage sipMessage, long configVersion) { try { ISipDelegate conn = getSipDelegateBinder(); if (conn == null) { diff --git a/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java b/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java index 59f9601299b2..eefe8493aef1 100644 --- a/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java +++ b/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java @@ -17,6 +17,7 @@ package android.telephony.ims.stub; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.telephony.ims.SipDelegateConnection; import android.telephony.ims.SipDelegateManager; import android.telephony.ims.SipMessage; @@ -26,6 +27,7 @@ import android.telephony.ims.SipMessage; * messages as well as the result of sending a SIP message. * @hide */ +@SystemApi public interface DelegateConnectionMessageCallback { /** @@ -49,6 +51,6 @@ public interface DelegateConnectionMessageCallback { * previously sent {@link SipMessage}. * @param reason The reason for the failure. */ - void onMessageSendFailure(String viaTransactionId, + void onMessageSendFailure(@NonNull String viaTransactionId, @SipDelegateManager.MessageFailureReason int reason); } diff --git a/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java index 976180538b18..02218ead0ad7 100644 --- a/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java +++ b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java @@ -17,6 +17,7 @@ package android.telephony.ims.stub; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.telephony.ims.DelegateRegistrationState; import android.telephony.ims.DelegateRequest; import android.telephony.ims.FeatureTagState; @@ -58,6 +59,7 @@ import java.util.Set; * * @hide */ +@SystemApi public interface DelegateConnectionStateCallback { /** diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java index a6f5c45445f5..153d687dd84f 100644 --- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java @@ -49,8 +49,7 @@ public class ImsRegistrationImplBase { * @hide */ // Defines the underlying radio technology type that we have registered for IMS over. - @IntDef(flag = true, - value = { + @IntDef(value = { REGISTRATION_TECH_NONE, REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN diff --git a/telephony/java/android/telephony/ims/stub/SipDelegate.java b/telephony/java/android/telephony/ims/stub/SipDelegate.java index 3ec97095eb00..d7e7b62dd550 100644 --- a/telephony/java/android/telephony/ims/stub/SipDelegate.java +++ b/telephony/java/android/telephony/ims/stub/SipDelegate.java @@ -17,6 +17,7 @@ package android.telephony.ims.stub; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.telephony.ims.DelegateMessageCallback; import android.telephony.ims.ImsService; import android.telephony.ims.SipDelegateImsConfiguration; @@ -40,6 +41,7 @@ import android.telephony.ims.SipMessage; * {@link android.telephony.ims.DelegateStateCallback} for more information. * @hide */ +@SystemApi public interface SipDelegate { /** @@ -57,7 +59,7 @@ public interface SipDelegate { * {@link DelegateMessageCallback#onMessageSendFailure} should be called with code * {@link SipDelegateManager#MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION}. */ - void sendMessage(@NonNull SipMessage message, int configVersion); + void sendMessage(@NonNull SipMessage message, long configVersion); /** * The framework is requesting that routing resources associated with the SIP dialog using the diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java index 93d438cf7f4d..1f74c09af0f6 100644 --- a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java +++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java @@ -17,6 +17,7 @@ package android.telephony.ims.stub; import android.annotation.NonNull; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.os.Binder; import android.os.IBinder; @@ -32,7 +33,6 @@ import android.telephony.ims.aidl.SipDelegateAidlWrapper; import android.util.Log; import java.util.ArrayList; -import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; @@ -99,7 +99,8 @@ public class SipTransportImplBase { /** * Called by the Telephony framework to request the creation of a new {@link SipDelegate}. * <p> - * The implementation must call {@link DelegateStateCallback#onCreated(SipDelegate, List)} with + * The implementation must call + * {@link DelegateStateCallback#onCreated(SipDelegate, java.util.Set)} with * the {@link SipDelegate} that is associated with the {@link DelegateRequest}. * <p> * This method will be called on the Executor specified in @@ -112,8 +113,9 @@ public class SipTransportImplBase { * for the SipDelegate. * @param mc A callback back to the remote application to be used to send SIP messages to the * remote application and acknowledge the sending of outgoing SIP messages. - * @hide */ + // executor used is defined in the constructor. + @SuppressLint("ExecutorRegistration") public void createSipDelegate(int subscriptionId, @NonNull DelegateRequest request, @NonNull DelegateStateCallback dc, @NonNull DelegateMessageCallback mc) { throw new UnsupportedOperationException("createSipDelegate not implemented!"); @@ -130,7 +132,6 @@ public class SipTransportImplBase { * @param delegate The delegate to be destroyed. * @param reason The reason the remote connection to this {@link SipDelegate} is being * destroyed. - * @hide */ public void destroySipDelegate(@NonNull SipDelegate delegate, @SipDelegateManager.SipDelegateDestroyReason int reason) { diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index b524549440da..5d4fdd0ff556 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -857,6 +857,11 @@ interface ITelephony { in int[] featureTypes, in String packageName); /** + * @return true if the ImsService cleared any carrier ImsService overrides, false otherwise. + */ + boolean clearCarrierImsServiceOverride(int slotIndex); + + /** * @return the package name of the carrier/device ImsService associated with this slot. */ String getBoundImsServicePackage(int slotIndex, boolean isCarrierImsService, int featureType); diff --git a/tests/BootImageProfileTest/TEST_MAPPING b/tests/BootImageProfileTest/TEST_MAPPING new file mode 100644 index 000000000000..1b569f9455bf --- /dev/null +++ b/tests/BootImageProfileTest/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "BootImageProfileTest" + } + ] +} 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 1f03c4dc056d..686ddcbd66bd 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 @@ -105,8 +105,7 @@ class OpenAppColdTest( configuration.endRotation) navBarLayerIsAlwaysVisible(enabled = false) statusBarLayerIsAlwaysVisible(enabled = false) - visibleLayersShownMoreThanOneConsecutiveEntry( - enabled = Surface.ROTATION_0 == configuration.endRotation) + visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 174541970) appLayerReplacesWallpaperLayer(testApp) } diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java index 52718bec9148..3d8deb5cfc8d 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java @@ -34,7 +34,6 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.cts.install.lib.Install; import com.android.cts.install.lib.InstallUtils; -import com.android.cts.install.lib.LocalIntentSender; import com.android.cts.install.lib.TestApp; import com.android.cts.install.lib.Uninstall; import com.android.cts.rollback.lib.Rollback; @@ -258,10 +257,6 @@ public class StagedRollbackTest { .getPackageManager().getPackageInstaller(); pi.abandonSession(sessionId); - // Remove the first intent sender result, so that the next staged install session does not - // erroneously think that it has itself been abandoned. - // TODO(b/136260017): Restructure LocalIntentSender to negate the need for this step. - LocalIntentSender.getIntentSenderResult(); Install.single(TestApp.A2).setStaged().setEnableRollback().commit(); } diff --git a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java index b77ed6ab5a29..cade5ba3771f 100644 --- a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java +++ b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java @@ -22,10 +22,13 @@ import static com.android.testutils.ParcelUtils.assertParcelSane; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import android.os.Build; import android.util.SparseArray; import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; + +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; +import com.android.testutils.DevSdkIgnoreRunner; import org.junit.Before; import org.junit.Test; @@ -34,7 +37,8 @@ import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.List; -@RunWith(AndroidJUnit4.class) +@IgnoreUpTo(Build.VERSION_CODES.R) +@RunWith(DevSdkIgnoreRunner.class) @SmallTest public class OemNetworkPreferencesTest { diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 3619937d76ed..ba87dc50e246 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -1058,7 +1058,9 @@ public class ConnectivityServiceTest { public void setUids(Set<UidRange> uids) { mNetworkCapabilities.setUids(uids); - updateCapabilitiesInternal(null /* defaultNetwork */, true); + if (mAgentRegistered) { + mMockNetworkAgent.setNetworkCapabilities(mNetworkCapabilities, true); + } } public void setVpnType(int vpnType) { @@ -1089,6 +1091,10 @@ public class ConnectivityServiceTest { mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp, mNetworkCapabilities); mMockNetworkAgent.waitForIdle(TIMEOUT_MS); + verify(mNetworkManagementService, times(1)) + .addVpnUidRanges(eq(mMockVpn.getNetId()), eq(uids.toArray(new UidRange[0]))); + verify(mNetworkManagementService, never()) + .removeVpnUidRanges(eq(mMockVpn.getNetId()), any()); mAgentRegistered = true; mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities()); mNetworkAgent = mMockNetworkAgent.getNetworkAgent(); @@ -1143,28 +1149,6 @@ public class ConnectivityServiceTest { mMockNetworkAgent.sendLinkProperties(lp); } - private NetworkCapabilities updateCapabilitiesInternal(Network defaultNetwork, - boolean sendToConnectivityService) { - if (!mAgentRegistered) return null; - super.updateCapabilities(defaultNetwork); - // Because super.updateCapabilities will update the capabilities of the agent but - // not the mock agent, the mock agent needs to know about them. - copyCapabilitiesToNetworkAgent(sendToConnectivityService); - return new NetworkCapabilities(mNetworkCapabilities); - } - - private void copyCapabilitiesToNetworkAgent(boolean sendToConnectivityService) { - if (null != mMockNetworkAgent) { - mMockNetworkAgent.setNetworkCapabilities(mNetworkCapabilities, - sendToConnectivityService); - } - } - - @Override - public NetworkCapabilities updateCapabilities(Network defaultNetwork) { - return updateCapabilitiesInternal(defaultNetwork, false); - } - public void disconnect() { if (mMockNetworkAgent != null) mMockNetworkAgent.disconnect(); mAgentRegistered = false; @@ -6922,8 +6906,8 @@ public class ConnectivityServiceTest { final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); mMockVpn.establish(lp, VPN_UID, vpnRange); - // Connected VPN should have interface rules set up. There are two expected invocations, - // one during VPN uid update, one during VPN LinkProperties update + // A connected VPN should have interface rules set up. There are two expected invocations, + // one during the VPN initial connection, one during the VPN LinkProperties update. ArgumentCaptor<int[]> uidCaptor = ArgumentCaptor.forClass(int[].class); verify(mMockNetd, times(2)).firewallAddUidInterfaceRules(eq("tun0"), uidCaptor.capture()); assertContainsExactly(uidCaptor.getAllValues().get(0), APP1_UID, APP2_UID); @@ -7438,20 +7422,14 @@ public class ConnectivityServiceTest { setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION); - // setUp() calls mockVpn() which adds a VPN with the Test Runner's uid. Configure it to be - // active - final VpnInfo info = new VpnInfo(); - info.ownerUid = Process.myUid(); - info.vpnIface = VPN_IFNAME; - mMockVpn.setVpnInfo(info); - mMockVpn.establishForMyUid(); - waitForIdle(); + // Wait for networks to connect and broadcasts to be sent before removing permissions. + waitForIdle(); mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); - assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {network})); + waitForIdle(); assertTrue( "Active VPN permission not applied", mService.checkConnectivityDiagnosticsPermissions( @@ -7459,6 +7437,7 @@ public class ConnectivityServiceTest { mContext.getOpPackageName())); assertTrue(mService.setUnderlyingNetworksForVpn(null)); + waitForIdle(); assertFalse( "VPN shouldn't receive callback on non-underlying network", mService.checkConnectivityDiagnosticsPermissions( diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index 1dcc07c6db81..d0db55f2bb13 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -41,6 +41,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; @@ -86,10 +87,10 @@ import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.ConditionVariable; import android.os.INetworkManagementService; -import android.os.Looper; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; +import android.os.test.TestLooper; import android.provider.Settings; import android.security.Credentials; import android.security.KeyStore; @@ -100,6 +101,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.R; +import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnProfile; import com.android.server.IpSecService; @@ -223,6 +225,8 @@ public class VpnTest { .thenReturn(mNotificationManager); when(mContext.getSystemService(eq(Context.CONNECTIVITY_SERVICE))) .thenReturn(mConnectivityManager); + when(mContext.getSystemServiceName(eq(ConnectivityManager.class))) + .thenReturn(Context.CONNECTIVITY_SERVICE); when(mContext.getSystemService(eq(Context.IPSEC_SERVICE))).thenReturn(mIpSecManager); when(mContext.getString(R.string.config_customVpnAlwaysOnDisconnectedDialogComponent)) .thenReturn(Resources.getSystem().getString( @@ -589,7 +593,7 @@ public class VpnTest { } @Test - public void testNotificationShownForAlwaysOnApp() { + public void testNotificationShownForAlwaysOnApp() throws Exception { final UserHandle userHandle = UserHandle.of(primaryUser.id); final Vpn vpn = createVpn(primaryUser.id); setMockedUsers(primaryUser); @@ -619,7 +623,6 @@ public class VpnTest { @Test public void testCapabilities() { - final Vpn vpn = createVpn(primaryUser.id); setMockedUsers(primaryUser); final Network mobile = new Network(1); @@ -1037,7 +1040,7 @@ public class VpnTest { when(exception.getErrorType()) .thenReturn(IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED); - final Vpn vpn = startLegacyVpn(mVpnProfile); + final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), (mVpnProfile)); final NetworkCallback cb = triggerOnAvailableAndGetCallback(); // Wait for createIkeSession() to be called before proceeding in order to ensure consistent @@ -1048,20 +1051,20 @@ public class VpnTest { ikeCb.onClosedExceptionally(exception); verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb)); - assertEquals(DetailedState.FAILED, vpn.getNetworkInfo().getDetailedState()); + assertEquals(LegacyVpnInfo.STATE_FAILED, vpn.getLegacyVpnInfo().state); } @Test public void testStartPlatformVpnIllegalArgumentExceptionInSetup() throws Exception { when(mIkev2SessionCreator.createIkeSession(any(), any(), any(), any(), any(), any())) .thenThrow(new IllegalArgumentException()); - final Vpn vpn = startLegacyVpn(mVpnProfile); + final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), mVpnProfile); final NetworkCallback cb = triggerOnAvailableAndGetCallback(); // Wait for createIkeSession() to be called before proceeding in order to ensure consistent // state verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb)); - assertEquals(DetailedState.FAILED, vpn.getNetworkInfo().getDetailedState()); + assertEquals(LegacyVpnInfo.STATE_FAILED, vpn.getLegacyVpnInfo().state); } private void setAndVerifyAlwaysOnPackage(Vpn vpn, int uid, boolean lockdownEnabled) { @@ -1100,8 +1103,7 @@ public class VpnTest { // a subsequent CL. } - public Vpn startLegacyVpn(final VpnProfile vpnProfile) throws Exception { - final Vpn vpn = createVpn(primaryUser.id); + private Vpn startLegacyVpn(final Vpn vpn, final VpnProfile vpnProfile) throws Exception { setMockedUsers(primaryUser); // Dummy egress interface @@ -1118,7 +1120,7 @@ public class VpnTest { @Test public void testStartPlatformVpn() throws Exception { - startLegacyVpn(mVpnProfile); + startLegacyVpn(createVpn(primaryUser.id), mVpnProfile); // TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in // a subsequent patch. } @@ -1153,7 +1155,7 @@ public class VpnTest { legacyRunnerReady.open(); return new Network(102); }); - final Vpn vpn = startLegacyVpn(profile); + final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), profile); final TestDeps deps = (TestDeps) vpn.mDeps; try { // udppsk and 1701 are the values for TYPE_L2TP_IPSEC_PSK @@ -1287,8 +1289,13 @@ public class VpnTest { doReturn(UserHandle.of(userId)).when(asUserContext).getUser(); when(mContext.createContextAsUser(eq(UserHandle.of(userId)), anyInt())) .thenReturn(asUserContext); - return new Vpn(Looper.myLooper(), mContext, new TestDeps(), mNetService, + final TestLooper testLooper = new TestLooper(); + final Vpn vpn = new Vpn(testLooper.getLooper(), mContext, new TestDeps(), mNetService, userId, mKeyStore, mSystemServices, mIkev2SessionCreator); + verify(mConnectivityManager, times(1)).registerNetworkProvider(argThat( + provider -> provider.getName().contains("VpnNetworkProvider") + )); + return vpn; } private static void assertBlocked(Vpn vpn, int... uids) { diff --git a/tools/codegen/src/com/android/codegen/ClassPrinter.kt b/tools/codegen/src/com/android/codegen/ClassPrinter.kt index b90e1bb3e7e7..8cae14a2d040 100644 --- a/tools/codegen/src/com/android/codegen/ClassPrinter.kt +++ b/tools/codegen/src/com/android/codegen/ClassPrinter.kt @@ -145,7 +145,6 @@ class ClassPrinter( } return when { cliArgs.contains("--hidden-$kebabCase") -> true - this == FeatureFlag.BUILD_UPON -> FeatureFlag.BUILDER.hidden else -> false } } diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt index 6a635d0e6181..d9ad649782bb 100644 --- a/tools/codegen/src/com/android/codegen/SharedConstants.kt +++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt @@ -1,7 +1,7 @@ package com.android.codegen const val CODEGEN_NAME = "codegen" -const val CODEGEN_VERSION = "1.0.21" +const val CODEGEN_VERSION = "1.0.22" const val CANONICAL_BUILDER_CLASS = "Builder" const val BASE_BUILDER_CLASS = "BaseBuilder" diff --git a/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt b/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt index 84faeea36eea..4c1fa6ec40b3 100644 --- a/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt +++ b/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt @@ -33,13 +33,12 @@ import javax.tools.Diagnostic.Kind import javax.tools.StandardLocation.CLASS_OUTPUT import kotlin.collections.set - /** * The IntDefProcessor is intended to generate a mapping from ints to their respective string * identifier for each IntDef for use by Winscope or any other tool which requires such a mapping. * - * The processor will run when building :frameworks-all and dump all the IntDef mappings found the - * files the make up :frameworks-all as json to outputPath. + * The processor will run when building :framework-minus-apex-intdefs and dump all the IntDef + * mappings found in the files that make up the build target as json to outputPath. */ class IntDefProcessor : AbstractProcessor() { private val outputName = "intDefMapping.json" @@ -72,8 +71,8 @@ class IntDefProcessor : AbstractProcessor() { } private fun generateIntDefMapping( - annotatedElement: TypeElement, - annotationType: TypeElement + annotatedElement: TypeElement, + annotationType: TypeElement ): Map<Int, String> { // LinkedHashMap makes sure ordering is the same as in the code val mapping = LinkedHashMap<Int, String>() @@ -151,8 +150,8 @@ class IntDefProcessor : AbstractProcessor() { companion object { fun serializeTo( - annotationTypeToIntDefMapping: Map<String, IntDefMapping>, - writer: Writer + annotationTypeToIntDefMapping: Map<String, IntDefMapping>, + writer: Writer ) { val indent = " " diff --git a/wifi/api/current.txt b/wifi/api/current.txt index 911f2fbc6afa..ce2b8ca4f2cd 100644 --- a/wifi/api/current.txt +++ b/wifi/api/current.txt @@ -14,6 +14,7 @@ package android.net.wifi { field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE = -3; // 0xfffffffd field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED = -8; // 0xfffffff8 field public static final int EASY_CONNECT_EVENT_FAILURE_TIMEOUT = -6; // 0xfffffffa + field public static final int EASY_CONNECT_EVENT_FAILURE_URI_GENERATION = -13; // 0xfffffff3 } public final class ScanResult implements android.os.Parcelable { diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt index c4a1766f982c..5e8fc0774cce 100644 --- a/wifi/api/system-current.txt +++ b/wifi/api/system-current.txt @@ -3,6 +3,7 @@ package android.net.wifi { public abstract class EasyConnectStatusCallback { ctor public EasyConnectStatusCallback(); + method public void onBootstrapUriGenerated(@NonNull String); method public abstract void onConfiguratorSuccess(int); method public abstract void onEnrolleeSuccess(int); method public void onFailure(int); @@ -497,6 +498,7 @@ package android.net.wifi { method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public boolean setWifiConnectedNetworkScorer(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.WifiConnectedNetworkScorer); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsConfiguratorInitiator(@NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsEnrolleeInitiator(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsEnrolleeResponder(@Nullable String, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startLocalOnlyHotspot(@NonNull android.net.wifi.SoftApConfiguration, @Nullable java.util.concurrent.Executor, @Nullable android.net.wifi.WifiManager.LocalOnlyHotspotCallback); method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public boolean startScan(android.os.WorkSource); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startSubscriptionProvisioning(@NonNull android.net.wifi.hotspot2.OsuProvider, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.hotspot2.ProvisioningCallback); @@ -523,6 +525,14 @@ package android.net.wifi { field public static final int DEVICE_MOBILITY_STATE_LOW_MVMT = 2; // 0x2 field public static final int DEVICE_MOBILITY_STATE_STATIONARY = 3; // 0x3 field public static final int DEVICE_MOBILITY_STATE_UNKNOWN = 0; // 0x0 + field public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP256R1 = 3; // 0x3 + field public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP384R1 = 4; // 0x4 + field public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP512R1 = 5; // 0x5 + field public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_DEFAULT = 0; // 0x0 + field public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_PRIME256V1 = 0; // 0x0 + field public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP384R1 = 1; // 0x1 + field public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP521R1 = 2; // 0x2 + field public static final int EASY_CONNECT_DEVICE_INFO_MAXIMUM_LENGTH = 40; // 0x28 field public static final int EASY_CONNECT_NETWORK_ROLE_AP = 1; // 0x1 field public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; // 0x0 field public static final String EXTRA_CHANGE_REASON = "changeReason"; diff --git a/wifi/java/android/net/wifi/EasyConnectStatusCallback.java b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java index 6c2e6ddf5dd2..ee7025594bc6 100644 --- a/wifi/java/android/net/wifi/EasyConnectStatusCallback.java +++ b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java @@ -161,6 +161,11 @@ public abstract class EasyConnectStatusCallback { */ public static final int EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION = -12; + /** + * Easy Connect Failure event: System failed to generate DPP URI. + */ + public static final int EASY_CONNECT_EVENT_FAILURE_URI_GENERATION = -13; + /** @hide */ @IntDef(prefix = {"EASY_CONNECT_EVENT_FAILURE_"}, value = { EASY_CONNECT_EVENT_FAILURE_INVALID_URI, @@ -175,6 +180,7 @@ public abstract class EasyConnectStatusCallback { EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK, EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION, EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION, + EASY_CONNECT_EVENT_FAILURE_URI_GENERATION, }) @Retention(RetentionPolicy.SOURCE) public @interface EasyConnectFailureStatusCode { @@ -264,4 +270,17 @@ public abstract class EasyConnectStatusCallback { */ @SystemApi public abstract void onProgress(@EasyConnectProgressStatusCode int code); + + /** + * Called when local Easy Connect Responder successfully generates a DPP URI from + * the supplicant. This callback is the first successful outcome + * of a Easy Connect Responder flow starting with + * {@link WifiManager#startEasyConnectAsEnrolleeResponder(String, int, Executor, + * EasyConnectStatusCallback)} . + * + * @param uri DPP URI from the supplicant. + * @hide + */ + @SystemApi + public void onBootstrapUriGenerated(@NonNull String uri) {}; } diff --git a/wifi/java/android/net/wifi/IDppCallback.aidl b/wifi/java/android/net/wifi/IDppCallback.aidl index d7a958a5b4b1..dcbe8468de9e 100644 --- a/wifi/java/android/net/wifi/IDppCallback.aidl +++ b/wifi/java/android/net/wifi/IDppCallback.aidl @@ -45,4 +45,10 @@ oneway interface IDppCallback * to show progress. */ void onProgress(int status); + + /** + * Called when local DPP Responder successfully generates a URI. + */ + void onBootstrapUriGenerated(String uri); + } diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index cc864eafcff1..0b3c0575dabd 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -235,6 +235,9 @@ interface IWifiManager void startDppAsEnrolleeInitiator(in IBinder binder, in String configuratorUri, in IDppCallback callback); + void startDppAsEnrolleeResponder(in IBinder binder, in String deviceInfo, int curve, + in IDppCallback callback); + void stopDppSession(); void updateWifiUsabilityScore(int seqNum, int score, int predictionHorizonSec); diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index da7c9c055d65..d089e1a86a68 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -5551,6 +5551,89 @@ public class WifiManager { } /** + * Easy Connect Device information maximum allowed length. + * + * @hide + */ + @SystemApi + public static final int EASY_CONNECT_DEVICE_INFO_MAXIMUM_LENGTH = 40; + + /** + * Easy Connect Cryptography Curve name: prime256v1 + * + * @hide + */ + @SystemApi + public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_PRIME256V1 = 0; + + /** + * Easy Connect Cryptography Curve name: secp384r1 + * + * @hide + */ + @SystemApi + public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP384R1 = 1; + + /** + * Easy Connect Cryptography Curve name: secp521r1 + * + * @hide + */ + @SystemApi + public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP521R1 = 2; + + + /** + * Easy Connect Cryptography Curve name: brainpoolP256r1 + * + * @hide + */ + @SystemApi + public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP256R1 = 3; + + + /** + * Easy Connect Cryptography Curve name: brainpoolP384r1 + * + * @hide + */ + @SystemApi + public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP384R1 = 4; + + + /** + * Easy Connect Cryptography Curve name: brainpoolP512r1 + * + * @hide + */ + @SystemApi + public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP512R1 = 5; + + /** + * Easy Connect Cryptography Curve name: default + * This allows framework to choose manadatory curve prime256v1. + * + * @hide + */ + @SystemApi + public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_DEFAULT = + EASY_CONNECT_CRYPTOGRAPHY_CURVE_PRIME256V1; + + /** @hide */ + @IntDef(prefix = {"EASY_CONNECT_CRYPTOGRAPHY_CURVE_"}, value = { + EASY_CONNECT_CRYPTOGRAPHY_CURVE_DEFAULT, + EASY_CONNECT_CRYPTOGRAPHY_CURVE_PRIME256V1, + EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP384R1, + EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP521R1, + EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP256R1, + EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP384R1, + EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP512R1, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EasyConnectCryptographyCurve { + } + + /** * Start Easy Connect (DPP) in Configurator-Initiator role. The current device will initiate * Easy Connect bootstrapping with a peer, and configure the peer with the SSID and password of * the specified network using the Easy Connect protocol on an encrypted link. @@ -5606,6 +5689,52 @@ public class WifiManager { } /** + * Start Easy Connect (DPP) in Enrollee-Responder role. + * The device will: + * 1. Generate a DPP bootstrap URI and return it using the + * {@link EasyConnectStatusCallback#onBootstrapUriGenerated(String)} method. + * 2. Start DPP as a Responder, waiting for an Initiator device to start the DPP + * authentication process. + * The caller should use the URI provided in step #1, for instance display it as a QR code + * or communicate it in some other way to the initiator device. + * + * @param deviceInfo Device specific information to add to the DPP URI. This field allows + * the users of the configurators to identify the device. + * Optional - if not provided or in case of an empty string, + * Info field (I:) will be skipped in the generated DPP URI. + * Allowed Range of ASCII characters in deviceInfo - %x20-7E. + * semicolon and space are not allowed. + * Due to the limitation of maximum allowed characters in QR code, + * framework limits to a max of + * {@link #EASY_CONNECT_DEVICE_INFO_MAXIMUM_LENGTH} characters in + * deviceInfo. + * Violation of these rules will result in an exception. + * @param curve Elliptic curve cryptography used to generate DPP + * public/private key pair. If application is not interested in a + * specific curve, choose default curve + * {@link #EASY_CONNECT_CRYPTOGRAPHY_CURVE_DEFAULT}. + * @param callback Callback for status updates + * @param executor The Executor on which to run the callback. + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) + public void startEasyConnectAsEnrolleeResponder(@Nullable String deviceInfo, + @EasyConnectCryptographyCurve int curve, + @NonNull @CallbackExecutor Executor executor, + @NonNull EasyConnectStatusCallback callback) { + Binder binder = new Binder(); + try { + mService.startDppAsEnrolleeResponder(binder, deviceInfo, curve, + new EasyConnectCallbackProxy(executor, callback)); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Stop or abort a current Easy Connect (DPP) session. This call, once processed, will * terminate any ongoing transaction, and clean up all associated resources. Caller should not * expect any callbacks once this call is made. However, due to the asynchronous nature of @@ -5679,6 +5808,15 @@ public class WifiManager { mEasyConnectStatusCallback.onProgress(status); }); } + + @Override + public void onBootstrapUriGenerated(String uri) { + Log.d(TAG, "Easy Connect onBootstrapUriGenerated callback"); + Binder.clearCallingIdentity(); + mExecutor.execute(() -> { + mEasyConnectStatusCallback.onBootstrapUriGenerated(uri); + }); + } } /** diff --git a/wifi/tests/src/android/net/wifi/EasyConnectStatusCallbackTest.java b/wifi/tests/src/android/net/wifi/EasyConnectStatusCallbackTest.java index b10141434b0b..cf37b78b889a 100644 --- a/wifi/tests/src/android/net/wifi/EasyConnectStatusCallbackTest.java +++ b/wifi/tests/src/android/net/wifi/EasyConnectStatusCallbackTest.java @@ -19,6 +19,7 @@ package android.net.wifi; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import android.annotation.NonNull; import android.util.SparseArray; import androidx.test.filters.SmallTest; @@ -52,6 +53,11 @@ public class EasyConnectStatusCallbackTest { mOnFailureR1EventReceived = true; mLastCode = code; } + + @Override + public void onBootstrapUriGenerated(@NonNull String uri) { + + } }; private boolean mOnFailureR1EventReceived; private int mLastCode; |