diff options
196 files changed, 7581 insertions, 1990 deletions
diff --git a/Android.bp b/Android.bp index 412a8ec79437..9426a9c503ed 100644 --- a/Android.bp +++ b/Android.bp @@ -803,10 +803,9 @@ filegroup { } filegroup { - name: "incremental_data_loader_aidl", + name: "dataloader_aidl", srcs: [ - "core/java/android/service/incremental/IIncrementalDataLoaderStatusListener.aidl", - "core/java/android/service/incremental/IIncrementalDataLoaderService.aidl", + "core/java/android/content/pm/IDataLoaderStatusListener.aidl", ], path: "core/java", } @@ -815,7 +814,27 @@ aidl_interface { name: "libincremental_aidl", srcs: [ ":incremental_aidl", - ":incremental_data_loader_aidl", + ], + imports: [ + "libdataloader_aidl", + ], + backend: { + java: { + sdk_version: "28", + }, + cpp: { + enabled: true, + }, + ndk: { + enabled: true, + }, + }, +} + +aidl_interface { + name: "libdataloader_aidl", + srcs: [ + ":dataloader_aidl", ], backend: { java: { diff --git a/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java b/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java index 5ac922c4a9b6..3dbb5cffe908 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java @@ -24,10 +24,8 @@ import android.util.SparseArray; import com.google.android.icing.proto.DocumentProto; import com.google.android.icing.proto.PropertyProto; +import com.google.android.icing.proto.SearchResultProto; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; @@ -94,23 +92,25 @@ public class FakeIcing { * Returns documents containing the given term. * * @param term A single exact term to look up in the index. - * @return The matching documents, or an empty {@code List} if no documents match. + * @return A {@link SearchResultProto} containing the matching documents, which may have no + * results if no documents match. */ @NonNull - public List<DocumentProto> query(@NonNull String term) { + public SearchResultProto query(@NonNull String term) { String normTerm = normalizeString(term); Set<Integer> docIds = mIndex.get(normTerm); if (docIds == null || docIds.isEmpty()) { - return Collections.emptyList(); + return SearchResultProto.getDefaultInstance(); } - List<DocumentProto> matches = new ArrayList<>(docIds.size()); + SearchResultProto.Builder results = SearchResultProto.newBuilder(); for (int docId : docIds) { DocumentProto document = mDocStore.get(docId); if (document != null) { - matches.add(document); + results.addResults( + SearchResultProto.ResultProto.newBuilder().setDocument(document)); } } - return matches; + return results.build(); } /** diff --git a/api/current.txt b/api/current.txt index 6ad8ddd4097e..8533ab197148 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11365,6 +11365,15 @@ package android.content.pm { field public int version; } + public final class InstallSourceInfo implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public String getInitiatingPackageName(); + method @Nullable public String getInstallingPackageName(); + method @Nullable public String getOriginatingPackageName(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.InstallSourceInfo> CREATOR; + } + public class InstrumentationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { ctor public InstrumentationInfo(); ctor public InstrumentationInfo(android.content.pm.InstrumentationInfo); @@ -11729,10 +11738,11 @@ package android.content.pm { method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName); method @NonNull public abstract android.graphics.drawable.Drawable getDefaultActivityIcon(); method @Nullable public abstract android.graphics.drawable.Drawable getDrawable(@NonNull String, @DrawableRes int, @Nullable android.content.pm.ApplicationInfo); + method @NonNull public android.content.pm.InstallSourceInfo getInstallSourceInfo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; method @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int); method @NonNull public java.util.List<android.content.pm.ModuleInfo> getInstalledModules(int); method @NonNull public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int); - method @Nullable public abstract String getInstallerPackageName(@NonNull String); + method @Deprecated @Nullable public abstract String getInstallerPackageName(@NonNull String); method @NonNull public abstract byte[] getInstantAppCookie(); method public abstract int getInstantAppCookieMaxBytes(); method @NonNull public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; @@ -25605,6 +25615,50 @@ package android.media { field public static final int MUXER_OUTPUT_WEBM = 1; // 0x1 } + public final class MediaParser { + method public boolean advance(@NonNull android.media.MediaParser.SeekableInputReader) throws java.io.IOException, java.lang.InterruptedException; + method @NonNull public static android.media.MediaParser create(@NonNull android.media.MediaParser.OutputConsumer, @NonNull java.lang.String...); + method @NonNull public static android.media.MediaParser createByName(@NonNull String, @NonNull android.media.MediaParser.OutputConsumer); + method @Nullable public String getExtractorName(); + method @NonNull public static java.util.List<java.lang.String> getExtractorNames(@NonNull android.media.MediaFormat); + method public void release(); + method public void seek(@NonNull android.media.MediaParser.SeekPoint); + } + + public static interface MediaParser.InputReader { + method public long getLength(); + method public long getPosition(); + method public int read(@NonNull byte[], int, int) throws java.io.IOException, java.lang.InterruptedException; + } + + public static interface MediaParser.OutputConsumer { + method public void onFormat(int, @NonNull android.media.MediaFormat); + method public void onSampleCompleted(int, long, int, int, int, @Nullable android.media.MediaCodec.CryptoInfo); + method public void onSampleData(int, @NonNull android.media.MediaParser.InputReader) throws java.io.IOException, java.lang.InterruptedException; + method public void onSeekMap(@NonNull android.media.MediaParser.SeekMap); + method public void onTracksFound(int); + } + + public static interface MediaParser.SeekMap { + method public long getDurationUs(); + method @NonNull public android.util.Pair<android.media.MediaParser.SeekPoint,android.media.MediaParser.SeekPoint> getSeekPoints(long); + method public boolean isSeekable(); + field public static final int UNKNOWN_DURATION = -2147483648; // 0x80000000 + } + + public static final class MediaParser.SeekPoint { + field @NonNull public static final android.media.MediaParser.SeekPoint START; + field public final long position; + field public final long timeUs; + } + + public static interface MediaParser.SeekableInputReader extends android.media.MediaParser.InputReader { + method public void seekToPosition(long); + } + + public static final class MediaParser.UnrecognizedInputFormatException extends java.io.IOException { + } + public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation { ctor public MediaPlayer(); method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler); @@ -38618,15 +38672,13 @@ package android.provider { ctor public MediaStore(); method @Nullable public static android.net.Uri getDocumentUri(@NonNull android.content.Context, @NonNull android.net.Uri); method @NonNull public static java.util.Set<java.lang.String> getExternalVolumeNames(@NonNull android.content.Context); - method public static boolean getIncludePending(@NonNull android.net.Uri); method public static android.net.Uri getMediaScannerUri(); method @Nullable public static android.net.Uri getMediaUri(@NonNull android.content.Context, @NonNull android.net.Uri); method public static boolean getRequireOriginal(@NonNull android.net.Uri); method @NonNull public static String getVersion(@NonNull android.content.Context); method @NonNull public static String getVersion(@NonNull android.content.Context, @NonNull String); method @NonNull public static String getVolumeName(@NonNull android.net.Uri); - method @NonNull public static android.net.Uri setIncludePending(@NonNull android.net.Uri); - method @NonNull public static android.net.Uri setIncludeTrashed(@NonNull android.net.Uri); + method @Deprecated @NonNull public static android.net.Uri setIncludePending(@NonNull android.net.Uri); method @NonNull public static android.net.Uri setRequireOriginal(@NonNull android.net.Uri); method public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri); method public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri, long); @@ -38662,9 +38714,17 @@ package android.provider { field public static final String INTENT_ACTION_TEXT_OPEN_FROM_SEARCH = "android.media.action.TEXT_OPEN_FROM_SEARCH"; field public static final String INTENT_ACTION_VIDEO_CAMERA = "android.media.action.VIDEO_CAMERA"; field public static final String INTENT_ACTION_VIDEO_PLAY_FROM_SEARCH = "android.media.action.VIDEO_PLAY_FROM_SEARCH"; + field public static final int MATCH_DEFAULT = 0; // 0x0 + field public static final int MATCH_EXCLUDE = 2; // 0x2 + field public static final int MATCH_INCLUDE = 1; // 0x1 + field public static final int MATCH_ONLY = 3; // 0x3 field public static final String MEDIA_IGNORE_FILENAME = ".nomedia"; field public static final String MEDIA_SCANNER_VOLUME = "volume"; field public static final String META_DATA_STILL_IMAGE_CAMERA_PREWARM_SERVICE = "android.media.still_image_camera_preview_service"; + field public static final String QUERY_ARG_MATCH_FAVORITE = "android:query-arg-match-favorite"; + field public static final String QUERY_ARG_MATCH_PENDING = "android:query-arg-match-pending"; + field public static final String QUERY_ARG_MATCH_TRASHED = "android:query-arg-match-trashed"; + field public static final String QUERY_ARG_RELATED_URI = "android:query-arg-related-uri"; field public static final String UNKNOWN_STRING = "<unknown>"; field public static final String VOLUME_EXTERNAL = "external"; field public static final String VOLUME_EXTERNAL_PRIMARY = "external_primary"; @@ -43487,6 +43547,7 @@ package android.telecom { method public int getCallProperties(); method public String getCallerDisplayName(); method public int getCallerDisplayNamePresentation(); + method public int getCallerNumberVerificationStatus(); method public final long getConnectTimeMillis(); method public long getCreationTimeMillis(); method public android.telecom.DisconnectCause getDisconnectCause(); @@ -43668,6 +43729,7 @@ package android.telecom { method public final android.telecom.CallAudioState getCallAudioState(); method public final String getCallerDisplayName(); method public final int getCallerDisplayNamePresentation(); + method public int getCallerNumberVerificationStatus(); method public final android.telecom.Conference getConference(); method public final java.util.List<android.telecom.Conferenceable> getConferenceables(); method public final int getConnectionCapabilities(); @@ -43719,6 +43781,7 @@ package android.telecom { method public final void setAudioModeIsVoip(boolean); method public final void setAudioRoute(int); method public final void setCallerDisplayName(String, int); + method public void setCallerNumberVerificationStatus(int); method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>); method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>); method public final void setConnectionCapabilities(int); @@ -43814,6 +43877,9 @@ package android.telecom { field public static final int STATE_NEW = 1; // 0x1 field public static final int STATE_PULLING_CALL = 7; // 0x7 field public static final int STATE_RINGING = 2; // 0x2 + field public static final int VERIFICATION_STATUS_FAILED = 2; // 0x2 + field public static final int VERIFICATION_STATUS_NOT_VERIFIED = 0; // 0x0 + field public static final int VERIFICATION_STATUS_PASSED = 1; // 0x1 } public static final class Connection.RttModifyStatus { diff --git a/api/removed.txt b/api/removed.txt index e0e26f7d65a7..1a2f43450d7b 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -438,7 +438,9 @@ package android.provider { public final class MediaStore { method @Deprecated @NonNull public static android.net.Uri createPending(@NonNull android.content.Context, @NonNull android.provider.MediaStore.PendingParams); method @Deprecated @NonNull public static java.util.Set<java.lang.String> getAllVolumeNames(@NonNull android.content.Context); + method @Deprecated public static boolean getIncludePending(@NonNull android.net.Uri); method @Deprecated @NonNull public static android.provider.MediaStore.PendingSession openPending(@NonNull android.content.Context, @NonNull android.net.Uri); + method @Deprecated @NonNull public static android.net.Uri setIncludeTrashed(@NonNull android.net.Uri); } public static interface MediaStore.Audio.AudioColumns extends android.provider.MediaStore.MediaColumns { diff --git a/api/system-current.txt b/api/system-current.txt index 7fdf9edec911..fe9c0d1a7ff2 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1399,6 +1399,7 @@ package android.bluetooth { public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile { method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean connect(android.bluetooth.BluetoothDevice); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean disconnect(android.bluetooth.BluetoothDevice); + method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothDevice getActiveDevice(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setPriority(android.bluetooth.BluetoothDevice, int); @@ -1433,6 +1434,37 @@ package android.bluetooth { field @Deprecated public static final int PRIORITY_ON = 100; // 0x64 } + public final class BluetoothUuid { + method public static boolean containsAnyUuid(@Nullable android.os.ParcelUuid[], @Nullable android.os.ParcelUuid[]); + method @NonNull public static android.os.ParcelUuid parseUuidFrom(@Nullable byte[]); + field @NonNull public static final android.os.ParcelUuid A2DP_SINK; + field @NonNull public static final android.os.ParcelUuid A2DP_SOURCE; + field @NonNull public static final android.os.ParcelUuid ADV_AUDIO_DIST; + field @NonNull public static final android.os.ParcelUuid AVRCP_CONTROLLER; + field @NonNull public static final android.os.ParcelUuid AVRCP_TARGET; + field @NonNull public static final android.os.ParcelUuid BASE_UUID; + field @NonNull public static final android.os.ParcelUuid BNEP; + field @NonNull public static final android.os.ParcelUuid HEARING_AID; + field @NonNull public static final android.os.ParcelUuid HFP; + field @NonNull public static final android.os.ParcelUuid HFP_AG; + field @NonNull public static final android.os.ParcelUuid HID; + field @NonNull public static final android.os.ParcelUuid HOGP; + field @NonNull public static final android.os.ParcelUuid HSP; + field @NonNull public static final android.os.ParcelUuid HSP_AG; + field @NonNull public static final android.os.ParcelUuid MAP; + field @NonNull public static final android.os.ParcelUuid MAS; + field @NonNull public static final android.os.ParcelUuid MNS; + field @NonNull public static final android.os.ParcelUuid NAP; + field @NonNull public static final android.os.ParcelUuid OBEX_OBJECT_PUSH; + field @NonNull public static final android.os.ParcelUuid PANU; + field @NonNull public static final android.os.ParcelUuid PBAP_PCE; + field @NonNull public static final android.os.ParcelUuid PBAP_PSE; + field @NonNull public static final android.os.ParcelUuid SAP; + field public static final int UUID_BYTES_128_BIT = 16; // 0x10 + field public static final int UUID_BYTES_16_BIT = 2; // 0x2 + field public static final int UUID_BYTES_32_BIT = 4; // 0x4 + } + } package android.bluetooth.le { @@ -4733,7 +4765,7 @@ package android.net.ipsec.ike { method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getOutboundTrafficSelectors(); } - public abstract class ChildSessionOptions { + public abstract class ChildSessionParams { } public class IkeFqdnIdentification extends android.net.ipsec.ike.IkeIdentification { @@ -4778,11 +4810,11 @@ package android.net.ipsec.ike { } public final class IkeSession implements java.lang.AutoCloseable { - ctor public IkeSession(@NonNull android.content.Context, @NonNull android.net.ipsec.ike.IkeSessionOptions, @NonNull android.net.ipsec.ike.ChildSessionOptions, @NonNull java.util.concurrent.Executor, @NonNull android.net.ipsec.ike.IkeSessionCallback, @NonNull android.net.ipsec.ike.ChildSessionCallback); + ctor public IkeSession(@NonNull android.content.Context, @NonNull android.net.ipsec.ike.IkeSessionParams, @NonNull android.net.ipsec.ike.ChildSessionParams, @NonNull java.util.concurrent.Executor, @NonNull android.net.ipsec.ike.IkeSessionCallback, @NonNull android.net.ipsec.ike.ChildSessionCallback); method public void close(); method public void closeChildSession(@NonNull android.net.ipsec.ike.ChildSessionCallback); method public void kill(); - method public void openChildSession(@NonNull android.net.ipsec.ike.ChildSessionOptions, @NonNull android.net.ipsec.ike.ChildSessionCallback); + method public void openChildSession(@NonNull android.net.ipsec.ike.ChildSessionParams, @NonNull android.net.ipsec.ike.ChildSessionCallback); } public interface IkeSessionCallback { @@ -4800,21 +4832,21 @@ package android.net.ipsec.ike { field public static final int EXTENSION_TYPE_MOBIKE = 2; // 0x2 } - public final class IkeSessionOptions { + public final class IkeSessionParams { } - public static final class IkeSessionOptions.Builder { - ctor public IkeSessionOptions.Builder(); - method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder addSaProposal(@NonNull android.net.ipsec.ike.IkeSaProposal); - method @NonNull public android.net.ipsec.ike.IkeSessionOptions build(); - method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setAuthDigitalSignature(@NonNull java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.security.PrivateKey); - method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setAuthDigitalSignature(@NonNull java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.util.List<java.security.cert.X509Certificate>, @NonNull java.security.PrivateKey); - method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setAuthEap(@NonNull java.security.cert.X509Certificate, @NonNull android.net.eap.EapSessionConfig); - method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setAuthPsk(@NonNull byte[]); - method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setLocalIdentification(@NonNull android.net.ipsec.ike.IkeIdentification); - method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setRemoteIdentification(@NonNull android.net.ipsec.ike.IkeIdentification); - method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setServerAddress(@NonNull java.net.InetAddress); - method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setUdpEncapsulationSocket(@NonNull android.net.IpSecManager.UdpEncapsulationSocket); + public static final class IkeSessionParams.Builder { + ctor public IkeSessionParams.Builder(); + method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.IkeSaProposal); + method @NonNull public android.net.ipsec.ike.IkeSessionParams build(); + method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthDigitalSignature(@NonNull java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.security.PrivateKey); + method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthDigitalSignature(@NonNull java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.util.List<java.security.cert.X509Certificate>, @NonNull java.security.PrivateKey); + method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthEap(@NonNull java.security.cert.X509Certificate, @NonNull android.net.eap.EapSessionConfig); + method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthPsk(@NonNull byte[]); + method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setLocalIdentification(@NonNull android.net.ipsec.ike.IkeIdentification); + method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setRemoteIdentification(@NonNull android.net.ipsec.ike.IkeIdentification); + method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setServerAddress(@NonNull java.net.InetAddress); + method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setUdpEncapsulationSocket(@NonNull android.net.IpSecManager.UdpEncapsulationSocket); } public final class IkeTrafficSelector { @@ -4851,33 +4883,33 @@ package android.net.ipsec.ike { field public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = 2; // 0x2 } - public final class TransportModeChildSessionOptions extends android.net.ipsec.ike.ChildSessionOptions { + public final class TransportModeChildSessionParams extends android.net.ipsec.ike.ChildSessionParams { } - public static final class TransportModeChildSessionOptions.Builder { - ctor public TransportModeChildSessionOptions.Builder(); - method @NonNull public android.net.ipsec.ike.TransportModeChildSessionOptions.Builder addInboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector); - method @NonNull public android.net.ipsec.ike.TransportModeChildSessionOptions.Builder addOutboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector); - method @NonNull public android.net.ipsec.ike.TransportModeChildSessionOptions.Builder addSaProposal(@NonNull android.net.ipsec.ike.ChildSaProposal); - method @NonNull public android.net.ipsec.ike.TransportModeChildSessionOptions build(); + public static final class TransportModeChildSessionParams.Builder { + ctor public TransportModeChildSessionParams.Builder(); + method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder addInboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector); + method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder addOutboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector); + method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.ChildSaProposal); + method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams build(); } - public final class TunnelModeChildSessionOptions extends android.net.ipsec.ike.ChildSessionOptions { + public final class TunnelModeChildSessionParams extends android.net.ipsec.ike.ChildSessionParams { } - public static final class TunnelModeChildSessionOptions.Builder { - ctor public TunnelModeChildSessionOptions.Builder(); - method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector); - method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInternalAddressRequest(int); - method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInternalAddressRequest(@NonNull java.net.InetAddress, int); - method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInternalDhcpServerRequest(int); - method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInternalDhcpServerRequest(@NonNull java.net.InetAddress); - method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInternalDnsServerRequest(int); - method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInternalDnsServerRequest(@NonNull java.net.InetAddress); - method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInternalSubnetRequest(int); - method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addOutboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector); - method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addSaProposal(@NonNull android.net.ipsec.ike.ChildSaProposal); - method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions build(); + public static final class TunnelModeChildSessionParams.Builder { + ctor public TunnelModeChildSessionParams.Builder(); + method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector); + method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalAddressRequest(int); + method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalAddressRequest(@NonNull java.net.InetAddress, int); + method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalDhcpServerRequest(int); + method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalDhcpServerRequest(@NonNull java.net.InetAddress); + method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalDnsServerRequest(int); + method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalDnsServerRequest(@NonNull java.net.InetAddress); + method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalSubnetRequest(int); + method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addOutboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector); + method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.ChildSaProposal); + method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams build(); } } @@ -6718,6 +6750,8 @@ package android.permission { public final class PermissionManager { method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public int getRuntimePermissionsVersion(); method @NonNull public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions(); + method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToLuiApp(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); + method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void revokeDefaultPermissionsFromLuiApps(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public void setRuntimePermissionsVersion(@IntRange(from=0) int); } @@ -7078,6 +7112,8 @@ package android.provider { field public static final String DEVICE_DEMO_MODE = "device_demo_mode"; field public static final String DEVICE_PROVISIONING_MOBILE_DATA_ENABLED = "device_provisioning_mobile_data"; field public static final String EUICC_PROVISIONED = "euicc_provisioned"; + field public static final String EUICC_SUPPORTED_COUNTRIES = "euicc_supported_countries"; + field public static final String EUICC_UNSUPPORTED_COUNTRIES = "euicc_unsupported_countries"; field public static final String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT = "install_carrier_app_notification_persistent"; field public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = "install_carrier_app_notification_sleep_millis"; field public static final String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update"; diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt index 3aa8187c0a79..a8c4db38c841 100644 --- a/api/test-lint-baseline.txt +++ b/api/test-lint-baseline.txt @@ -8,35 +8,35 @@ AcronymName: android.app.NotificationChannel#setImportanceLockedByOEM(boolean): ActionValue: android.location.Location#EXTRA_NO_GPS_LOCATION: ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_ADDITIONAL_CALL_INFO: - Inconsistent extra value; expected `android.telephony.ims.extra.ADDITIONAL_CALL_INFO`, was `AdditionalCallInfo` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CALL_RAT_TYPE: - Inconsistent extra value; expected `android.telephony.ims.extra.CALL_RAT_TYPE`, was `CallRadioTech` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CHILD_NUMBER: - Inconsistent extra value; expected `android.telephony.ims.extra.CHILD_NUMBER`, was `ChildNum` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CNA: - Inconsistent extra value; expected `android.telephony.ims.extra.CNA`, was `cna` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CNAP: - Inconsistent extra value; expected `android.telephony.ims.extra.CNAP`, was `cnap` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CODEC: - Inconsistent extra value; expected `android.telephony.ims.extra.CODEC`, was `Codec` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_DIALSTRING: - Inconsistent extra value; expected `android.telephony.ims.extra.DIALSTRING`, was `dialstring` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_DISPLAY_TEXT: - Inconsistent extra value; expected `android.telephony.ims.extra.DISPLAY_TEXT`, was `DisplayText` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_EMERGENCY_CALL: - Inconsistent extra value; expected `android.telephony.ims.extra.EMERGENCY_CALL`, was `e_call` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_IS_CALL_PULL: - Inconsistent extra value; expected `android.telephony.ims.extra.IS_CALL_PULL`, was `CallPull` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_OI: - Inconsistent extra value; expected `android.telephony.ims.extra.OI`, was `oi` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_OIR: - Inconsistent extra value; expected `android.telephony.ims.extra.OIR`, was `oir` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_REMOTE_URI: - Inconsistent extra value; expected `android.telephony.ims.extra.REMOTE_URI`, was `remote_uri` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_USSD: - Inconsistent extra value; expected `android.telephony.ims.extra.USSD`, was `ussd` + ActionValue: android.telephony.ims.ImsReasonInfo#EXTRA_MSG_SERVICE_NOT_AUTHORIZED: - Inconsistent extra value; expected `android.telephony.ims.extra.MSG_SERVICE_NOT_AUTHORIZED`, was `Forbidden. Not Authorized for Service` + ActionValue: android.telephony.mbms.vendor.VendorUtils#ACTION_CLEANUP: ActionValue: android.telephony.mbms.vendor.VendorUtils#ACTION_DOWNLOAD_RESULT_INTERNAL: @@ -100,13 +100,13 @@ ArrayReturn: android.os.NativeHandle#getFileDescriptors(): ArrayReturn: android.security.keystore.AttestationUtils#attestDeviceIds(android.content.Context, int[], byte[]): ArrayReturn: android.telephony.ims.ImsUtListener#onUtConfigurationCallBarringQueried(int, android.telephony.ims.ImsSsInfo[]) parameter #1: - Method parameter should be Collection<ImsSsInfo> (or subclass) instead of raw array; was `android.telephony.ims.ImsSsInfo[]` + ArrayReturn: android.telephony.ims.ImsUtListener#onUtConfigurationCallForwardQueried(int, android.telephony.ims.ImsCallForwardInfo[]) parameter #1: - Method parameter should be Collection<ImsCallForwardInfo> (or subclass) instead of raw array; was `android.telephony.ims.ImsCallForwardInfo[]` + ArrayReturn: android.telephony.ims.ImsUtListener#onUtConfigurationCallWaitingQueried(int, android.telephony.ims.ImsSsInfo[]) parameter #1: - Method parameter should be Collection<ImsSsInfo> (or subclass) instead of raw array; was `android.telephony.ims.ImsSsInfo[]` + ArrayReturn: android.telephony.ims.stub.ImsRegistrationImplBase#onSubscriberAssociatedUriChanged(android.net.Uri[]) parameter #0: - Method parameter should be Collection<Uri> (or subclass) instead of raw array; was `android.net.Uri[]` + ArrayReturn: android.view.FocusFinder#sort(android.view.View[], int, int, android.view.ViewGroup, boolean) parameter #0: ArrayReturn: android.view.contentcapture.ViewNode#getAutofillOptions(): @@ -268,7 +268,7 @@ ConcreteCollection: android.service.autofill.InternalTransformation#batchApply(a ConcreteCollection: android.service.autofill.UserData#getFieldClassificationAlgorithms(): ConcreteCollection: android.telephony.ims.ImsConferenceState#mParticipants: - Field type is concrete collection (`java.util.HashMap`); must be higher-level interface + ContextFirst: android.os.VibrationEffect#get(android.net.Uri, android.content.Context) parameter #1: @@ -338,9 +338,9 @@ ExecutorRegistration: android.os.RemoteCallback#RemoteCallback(android.os.Remote ExecutorRegistration: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler): ExecutorRegistration: android.telephony.ims.stub.ImsCallSessionImplBase#setListener(android.telephony.ims.ImsCallSessionListener): - Registration methods should have overload that accepts delivery Executor: `setListener` + ExecutorRegistration: android.telephony.ims.stub.ImsUtImplBase#setListener(android.telephony.ims.ImsUtListener): - Registration methods should have overload that accepts delivery Executor: `setListener` + ExecutorRegistration: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener): ExecutorRegistration: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener): @@ -460,11 +460,13 @@ InterfaceConstant: android.telecom.PhoneAccountSuggestionService#SERVICE_INTERFA InternalField: android.telephony.ims.ImsConferenceState#mParticipants: - Internal field mParticipants must not be exposed + KotlinOperator: android.os.WorkSource#get(int): +KotlinOperator: android.util.SparseArrayMap#get(int, String): + Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object) ListenerInterface: android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener: @@ -474,9 +476,9 @@ ListenerInterface: android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListen ListenerInterface: android.os.IncidentManager.AuthListener: ListenerInterface: android.telephony.ims.ImsCallSessionListener: - Listeners should be an interface, or otherwise renamed Callback: ImsCallSessionListener + ListenerInterface: android.telephony.ims.ImsUtListener: - Listeners should be an interface, or otherwise renamed Callback: ImsUtListener + ListenerLast: android.hardware.camera2.CameraDevice#createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) parameter #4: @@ -496,17 +498,17 @@ ManagerConstructor: android.content.pm.ShortcutManager#ShortcutManager(android.c ManagerLookup: android.telephony.ims.ImsMmTelManager#createForSubscriptionId(int): - Managers must always be obtained from Context (`createForSubscriptionId`) + ManagerLookup: android.telephony.ims.ProvisioningManager#createForSubscriptionId(int): - Managers must always be obtained from Context (`createForSubscriptionId`) + MethodNameTense: android.telephony.ims.feature.CapabilityChangeRequest#getCapabilitiesToEnable(): - Unexpected tense; probably meant `enabled`, was `getCapabilitiesToEnable` + MethodNameUnits: android.telephony.ims.ImsCallForwardInfo#getTimeSeconds(): - Returned time values must be in milliseconds, was `getTimeSeconds` + MinMaxConstant: android.os.UserHandle#MIN_SECONDARY_USER_ID: @@ -1460,7 +1462,7 @@ MissingNullability: android.telecom.PhoneAccountSuggestionService#onBind(android MissingNullability: android.telecom.PhoneAccountSuggestionService#onBind(android.content.Intent) parameter #0: MissingNullability: android.telephony.CallQuality#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `dest` in method `writeToParcel` + MissingNullability: android.telephony.DataSpecificRegistrationInfo#writeToParcel(android.os.Parcel, int) parameter #0: MissingNullability: android.telephony.LteVopsSupportInfo#writeToParcel(android.os.Parcel, int) parameter #0: @@ -1478,9 +1480,9 @@ MissingNullability: android.telephony.SmsManager#checkSmsShortCodeDestination(St MissingNullability: android.telephony.TelephonyManager#checkCarrierPrivilegesForPackage(String) parameter #0: MissingNullability: android.telephony.TelephonyManager#getCarrierPackageNamesForIntent(android.content.Intent): - Missing nullability on method `getCarrierPackageNamesForIntent` return + MissingNullability: android.telephony.TelephonyManager#getCarrierPackageNamesForIntent(android.content.Intent) parameter #0: - Missing nullability on parameter `intent` in method `getCarrierPackageNamesForIntent` + MissingNullability: android.telephony.TelephonyManager#getLine1AlphaTag(): MissingNullability: android.telephony.TelephonyManager#getRadioHalVersion(): @@ -1518,315 +1520,315 @@ MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(St MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #8: MissingNullability: android.telephony.ims.ImsCallForwardInfo#getNumber(): - Missing nullability on method `getNumber` return + MissingNullability: android.telephony.ims.ImsCallForwardInfo#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `out` in method `writeToParcel` + MissingNullability: android.telephony.ims.ImsCallProfile#ImsCallProfile(int, int, android.os.Bundle, android.telephony.ims.ImsStreamMediaProfile) parameter #2: - Missing nullability on parameter `callExtras` in method `ImsCallProfile` + MissingNullability: android.telephony.ims.ImsCallProfile#ImsCallProfile(int, int, android.os.Bundle, android.telephony.ims.ImsStreamMediaProfile) parameter #3: - Missing nullability on parameter `mediaProfile` in method `ImsCallProfile` + MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String): - Missing nullability on method `getCallExtra` return + MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String) parameter #0: - Missing nullability on parameter `name` in method `getCallExtra` + MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String, String): - Missing nullability on method `getCallExtra` return + MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String, String) parameter #0: - Missing nullability on parameter `name` in method `getCallExtra` + MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String, String) parameter #1: - Missing nullability on parameter `defaultValue` in method `getCallExtra` + MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtraBoolean(String) parameter #0: - Missing nullability on parameter `name` in method `getCallExtraBoolean` + MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtraBoolean(String, boolean) parameter #0: - Missing nullability on parameter `name` in method `getCallExtraBoolean` + MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtraInt(String) parameter #0: - Missing nullability on parameter `name` in method `getCallExtraInt` + MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtraInt(String, int) parameter #0: - Missing nullability on parameter `name` in method `getCallExtraInt` + MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtras(): - Missing nullability on method `getCallExtras` return + MissingNullability: android.telephony.ims.ImsCallProfile#getMediaProfile(): - Missing nullability on method `getMediaProfile` return + MissingNullability: android.telephony.ims.ImsCallProfile#getVideoStateFromImsCallProfile(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `callProfile` in method `getVideoStateFromImsCallProfile` + MissingNullability: android.telephony.ims.ImsCallProfile#setCallExtra(String, String) parameter #0: - Missing nullability on parameter `name` in method `setCallExtra` + MissingNullability: android.telephony.ims.ImsCallProfile#setCallExtra(String, String) parameter #1: - Missing nullability on parameter `value` in method `setCallExtra` + MissingNullability: android.telephony.ims.ImsCallProfile#setCallExtraBoolean(String, boolean) parameter #0: - Missing nullability on parameter `name` in method `setCallExtraBoolean` + MissingNullability: android.telephony.ims.ImsCallProfile#setCallExtraInt(String, int) parameter #0: - Missing nullability on parameter `name` in method `setCallExtraInt` + MissingNullability: android.telephony.ims.ImsCallProfile#updateCallExtras(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `profile` in method `updateCallExtras` + MissingNullability: android.telephony.ims.ImsCallProfile#updateCallType(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `profile` in method `updateCallType` + MissingNullability: android.telephony.ims.ImsCallProfile#updateMediaProfile(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `profile` in method `updateMediaProfile` + MissingNullability: android.telephony.ims.ImsCallProfile#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `out` in method `writeToParcel` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtendFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - Missing nullability on parameter `reasonInfo` in method `callSessionConferenceExtendFailed` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `newSession` in method `callSessionConferenceExtendReceived` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #1: - Missing nullability on parameter `profile` in method `callSessionConferenceExtendReceived` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `newSession` in method `callSessionConferenceExtended` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #1: - Missing nullability on parameter `profile` in method `callSessionConferenceExtended` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceStateUpdated(android.telephony.ims.ImsConferenceState) parameter #0: - Missing nullability on parameter `state` in method `callSessionConferenceStateUpdated` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHandover(int, int, android.telephony.ims.ImsReasonInfo) parameter #2: - Missing nullability on parameter `reasonInfo` in method `callSessionHandover` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHandoverFailed(int, int, android.telephony.ims.ImsReasonInfo) parameter #2: - Missing nullability on parameter `reasonInfo` in method `callSessionHandoverFailed` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHeld(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `profile` in method `callSessionHeld` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHoldFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - Missing nullability on parameter `reasonInfo` in method `callSessionHoldFailed` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHoldReceived(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `profile` in method `callSessionHoldReceived` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionInitiated(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `profile` in method `callSessionInitiated` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionInitiatedFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - Missing nullability on parameter `reasonInfo` in method `callSessionInitiatedFailed` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionInviteParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - Missing nullability on parameter `reasonInfo` in method `callSessionInviteParticipantsRequestFailed` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionMergeComplete(android.telephony.ims.stub.ImsCallSessionImplBase) parameter #0: - Missing nullability on parameter `newSession` in method `callSessionMergeComplete` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionMergeFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - Missing nullability on parameter `reasonInfo` in method `callSessionMergeFailed` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `newSession` in method `callSessionMergeStarted` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #1: - Missing nullability on parameter `profile` in method `callSessionMergeStarted` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionProgressing(android.telephony.ims.ImsStreamMediaProfile) parameter #0: - Missing nullability on parameter `profile` in method `callSessionProgressing` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionRemoveParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - Missing nullability on parameter `reasonInfo` in method `callSessionRemoveParticipantsRequestFailed` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionResumeFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - Missing nullability on parameter `reasonInfo` in method `callSessionResumeFailed` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionResumeReceived(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `profile` in method `callSessionResumeReceived` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionResumed(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `profile` in method `callSessionResumed` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionRttMessageReceived(String) parameter #0: - Missing nullability on parameter `rttMessage` in method `callSessionRttMessageReceived` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionRttModifyRequestReceived(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `callProfile` in method `callSessionRttModifyRequestReceived` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionSuppServiceReceived(android.telephony.ims.ImsSuppServiceNotification) parameter #0: - Missing nullability on parameter `suppSrvNotification` in method `callSessionSuppServiceReceived` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionTerminated(android.telephony.ims.ImsReasonInfo) parameter #0: - Missing nullability on parameter `reasonInfo` in method `callSessionTerminated` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionUpdateFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - Missing nullability on parameter `reasonInfo` in method `callSessionUpdateFailed` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionUpdateReceived(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `profile` in method `callSessionUpdateReceived` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionUpdated(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `profile` in method `callSessionUpdated` + MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionUssdMessageReceived(int, String) parameter #1: - Missing nullability on parameter `ussdMessage` in method `callSessionUssdMessageReceived` + MissingNullability: android.telephony.ims.ImsConferenceState#getConnectionStateForStatus(String) parameter #0: - Missing nullability on parameter `status` in method `getConnectionStateForStatus` + MissingNullability: android.telephony.ims.ImsConferenceState#mParticipants: - Missing nullability on field `mParticipants` in class `class android.telephony.ims.ImsConferenceState` + MissingNullability: android.telephony.ims.ImsConferenceState#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `out` in method `writeToParcel` + MissingNullability: android.telephony.ims.ImsExternalCallState#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `out` in method `writeToParcel` + MissingNullability: android.telephony.ims.ImsReasonInfo#ImsReasonInfo(int, int, String) parameter #2: - Missing nullability on parameter `extraMessage` in method `ImsReasonInfo` + MissingNullability: android.telephony.ims.ImsReasonInfo#getExtraMessage(): - Missing nullability on method `getExtraMessage` return + MissingNullability: android.telephony.ims.ImsReasonInfo#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `out` in method `writeToParcel` + MissingNullability: android.telephony.ims.ImsService#createMmTelFeature(int): - Missing nullability on method `createMmTelFeature` return + MissingNullability: android.telephony.ims.ImsService#createRcsFeature(int): - Missing nullability on method `createRcsFeature` return + MissingNullability: android.telephony.ims.ImsService#getConfig(int): - Missing nullability on method `getConfig` return + MissingNullability: android.telephony.ims.ImsService#getRegistration(int): - Missing nullability on method `getRegistration` return + MissingNullability: android.telephony.ims.ImsService#onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) parameter #0: - Missing nullability on parameter `c` in method `onUpdateSupportedImsFeatures` + MissingNullability: android.telephony.ims.ImsService#querySupportedImsFeatures(): - Missing nullability on method `querySupportedImsFeatures` return + MissingNullability: android.telephony.ims.ImsSsData#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `out` in method `writeToParcel` + MissingNullability: android.telephony.ims.ImsSsInfo#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `out` in method `writeToParcel` + MissingNullability: android.telephony.ims.ImsStreamMediaProfile#copyFrom(android.telephony.ims.ImsStreamMediaProfile) parameter #0: - Missing nullability on parameter `profile` in method `copyFrom` + MissingNullability: android.telephony.ims.ImsStreamMediaProfile#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `out` in method `writeToParcel` + MissingNullability: android.telephony.ims.ImsSuppServiceNotification#ImsSuppServiceNotification(int, int, int, int, String, String[]) parameter #4: - Missing nullability on parameter `number` in method `ImsSuppServiceNotification` + MissingNullability: android.telephony.ims.ImsSuppServiceNotification#ImsSuppServiceNotification(int, int, int, int, String, String[]) parameter #5: - Missing nullability on parameter `history` in method `ImsSuppServiceNotification` + MissingNullability: android.telephony.ims.ImsSuppServiceNotification#history: - Missing nullability on field `history` in class `class android.telephony.ims.ImsSuppServiceNotification` + MissingNullability: android.telephony.ims.ImsSuppServiceNotification#number: - Missing nullability on field `number` in class `class android.telephony.ims.ImsSuppServiceNotification` + MissingNullability: android.telephony.ims.ImsSuppServiceNotification#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `out` in method `writeToParcel` + MissingNullability: android.telephony.ims.ImsUtListener#onSupplementaryServiceIndication(android.telephony.ims.ImsSsData) parameter #0: - Missing nullability on parameter `ssData` in method `onSupplementaryServiceIndication` + MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationCallBarringQueried(int, android.telephony.ims.ImsSsInfo[]) parameter #1: - Missing nullability on parameter `cbInfo` in method `onUtConfigurationCallBarringQueried` + MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationCallForwardQueried(int, android.telephony.ims.ImsCallForwardInfo[]) parameter #1: - Missing nullability on parameter `cfInfo` in method `onUtConfigurationCallForwardQueried` + MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationCallWaitingQueried(int, android.telephony.ims.ImsSsInfo[]) parameter #1: - Missing nullability on parameter `cwInfo` in method `onUtConfigurationCallWaitingQueried` + MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationQueried(int, android.os.Bundle) parameter #1: - Missing nullability on parameter `configuration` in method `onUtConfigurationQueried` + MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationQueryFailed(int, android.telephony.ims.ImsReasonInfo) parameter #1: - Missing nullability on parameter `error` in method `onUtConfigurationQueryFailed` + MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationUpdateFailed(int, android.telephony.ims.ImsReasonInfo) parameter #1: - Missing nullability on parameter `error` in method `onUtConfigurationUpdateFailed` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#changeCameraCapabilities(android.telecom.VideoProfile.CameraCapabilities) parameter #0: - Missing nullability on parameter `CameraCapabilities` in method `changeCameraCapabilities` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSendSessionModifyRequest(android.telecom.VideoProfile, android.telecom.VideoProfile) parameter #0: - Missing nullability on parameter `fromProfile` in method `onSendSessionModifyRequest` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSendSessionModifyRequest(android.telecom.VideoProfile, android.telecom.VideoProfile) parameter #1: - Missing nullability on parameter `toProfile` in method `onSendSessionModifyRequest` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSendSessionModifyResponse(android.telecom.VideoProfile) parameter #0: - Missing nullability on parameter `responseProfile` in method `onSendSessionModifyResponse` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetCamera(String) parameter #0: - Missing nullability on parameter `cameraId` in method `onSetCamera` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetCamera(String, int) parameter #0: - Missing nullability on parameter `cameraId` in method `onSetCamera` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetDisplaySurface(android.view.Surface) parameter #0: - Missing nullability on parameter `surface` in method `onSetDisplaySurface` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetPauseImage(android.net.Uri) parameter #0: - Missing nullability on parameter `uri` in method `onSetPauseImage` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetPreviewSurface(android.view.Surface) parameter #0: - Missing nullability on parameter `surface` in method `onSetPreviewSurface` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#receiveSessionModifyRequest(android.telecom.VideoProfile) parameter #0: - Missing nullability on parameter `VideoProfile` in method `receiveSessionModifyRequest` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile) parameter #1: - Missing nullability on parameter `requestedProfile` in method `receiveSessionModifyResponse` + MissingNullability: android.telephony.ims.ImsVideoCallProvider#receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile) parameter #2: - Missing nullability on parameter `responseProfile` in method `receiveSessionModifyResponse` + MissingNullability: android.telephony.ims.feature.CapabilityChangeRequest#getCapabilitiesToDisable(): - Missing nullability on method `getCapabilitiesToDisable` return + MissingNullability: android.telephony.ims.feature.CapabilityChangeRequest#getCapabilitiesToEnable(): - Missing nullability on method `getCapabilitiesToEnable` return + MissingNullability: android.telephony.ims.feature.CapabilityChangeRequest#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `dest` in method `writeToParcel` + MissingNullability: android.telephony.ims.feature.ImsFeature#changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy) parameter #0: - Missing nullability on parameter `request` in method `changeEnabledCapabilities` + MissingNullability: android.telephony.ims.feature.ImsFeature#changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy) parameter #1: - Missing nullability on parameter `c` in method `changeEnabledCapabilities` + MissingNullability: android.telephony.ims.feature.MmTelFeature#queryCapabilityStatus(): - Missing nullability on method `queryCapabilityStatus` return + MissingNullability: android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities) parameter #0: - Missing nullability on parameter `c` in method `MmTelCapabilities` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#accept(int, android.telephony.ims.ImsStreamMediaProfile) parameter #1: - Missing nullability on parameter `profile` in method `accept` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#deflect(String) parameter #0: - Missing nullability on parameter `deflectNumber` in method `deflect` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#extendToConference(String[]) parameter #0: - Missing nullability on parameter `participants` in method `extendToConference` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getCallId(): - Missing nullability on method `getCallId` return + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getCallProfile(): - Missing nullability on method `getCallProfile` return + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getImsVideoCallProvider(): - Missing nullability on method `getImsVideoCallProvider` return + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getLocalCallProfile(): - Missing nullability on method `getLocalCallProfile` return + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getProperty(String): - Missing nullability on method `getProperty` return + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getProperty(String) parameter #0: - Missing nullability on parameter `name` in method `getProperty` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getRemoteCallProfile(): - Missing nullability on method `getRemoteCallProfile` return + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#hold(android.telephony.ims.ImsStreamMediaProfile) parameter #0: - Missing nullability on parameter `profile` in method `hold` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#inviteParticipants(String[]) parameter #0: - Missing nullability on parameter `participants` in method `inviteParticipants` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#removeParticipants(String[]) parameter #0: - Missing nullability on parameter `participants` in method `removeParticipants` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#resume(android.telephony.ims.ImsStreamMediaProfile) parameter #0: - Missing nullability on parameter `profile` in method `resume` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#sendDtmf(char, android.os.Message) parameter #1: - Missing nullability on parameter `result` in method `sendDtmf` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#sendRttMessage(String) parameter #0: - Missing nullability on parameter `rttMessage` in method `sendRttMessage` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#sendRttModifyRequest(android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `toProfile` in method `sendRttModifyRequest` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#sendUssd(String) parameter #0: - Missing nullability on parameter `ussdMessage` in method `sendUssd` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#setListener(android.telephony.ims.ImsCallSessionListener) parameter #0: - Missing nullability on parameter `listener` in method `setListener` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#start(String, android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `callee` in method `start` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#start(String, android.telephony.ims.ImsCallProfile) parameter #1: - Missing nullability on parameter `profile` in method `start` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#startConference(String[], android.telephony.ims.ImsCallProfile) parameter #0: - Missing nullability on parameter `participants` in method `startConference` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#startConference(String[], android.telephony.ims.ImsCallProfile) parameter #1: - Missing nullability on parameter `profile` in method `startConference` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#update(int, android.telephony.ims.ImsStreamMediaProfile) parameter #1: - Missing nullability on parameter `profile` in method `update` + MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase.State#toString(int): - Missing nullability on method `toString` return + MissingNullability: android.telephony.ims.stub.ImsConfigImplBase#getConfigString(int): - Missing nullability on method `getConfigString` return + MissingNullability: android.telephony.ims.stub.ImsConfigImplBase#notifyProvisionedValueChanged(int, String) parameter #1: - Missing nullability on parameter `value` in method `notifyProvisionedValueChanged` + MissingNullability: android.telephony.ims.stub.ImsConfigImplBase#setConfig(int, String) parameter #1: - Missing nullability on parameter `value` in method `setConfig` + MissingNullability: android.telephony.ims.stub.ImsFeatureConfiguration#getServiceFeatures(): - Missing nullability on method `getServiceFeatures` return + MissingNullability: android.telephony.ims.stub.ImsFeatureConfiguration#writeToParcel(android.os.Parcel, int) parameter #0: - Missing nullability on parameter `dest` in method `writeToParcel` + MissingNullability: android.telephony.ims.stub.ImsFeatureConfiguration.Builder#addFeature(int, int): - Missing nullability on method `addFeature` return + MissingNullability: android.telephony.ims.stub.ImsFeatureConfiguration.Builder#build(): - Missing nullability on method `build` return + MissingNullability: android.telephony.ims.stub.ImsMultiEndpointImplBase#onImsExternalCallStateUpdate(java.util.List<android.telephony.ims.ImsExternalCallState>) parameter #0: - Missing nullability on parameter `externalCallDialogs` in method `onImsExternalCallStateUpdate` + MissingNullability: android.telephony.ims.stub.ImsRegistrationImplBase#onDeregistered(android.telephony.ims.ImsReasonInfo) parameter #0: - Missing nullability on parameter `info` in method `onDeregistered` + MissingNullability: android.telephony.ims.stub.ImsRegistrationImplBase#onSubscriberAssociatedUriChanged(android.net.Uri[]) parameter #0: - Missing nullability on parameter `uris` in method `onSubscriberAssociatedUriChanged` + MissingNullability: android.telephony.ims.stub.ImsRegistrationImplBase#onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo) parameter #1: - Missing nullability on parameter `info` in method `onTechnologyChangeFailed` + MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#getSmsFormat(): - Missing nullability on method `getSmsFormat` return + MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsReceived(int, String, byte[]) parameter #1: - Missing nullability on parameter `format` in method `onSmsReceived` + MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsReceived(int, String, byte[]) parameter #2: - Missing nullability on parameter `pdu` in method `onSmsReceived` + MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsStatusReportReceived(int, String, byte[]) parameter #1: - Missing nullability on parameter `format` in method `onSmsStatusReportReceived` + MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsStatusReportReceived(int, String, byte[]) parameter #2: - Missing nullability on parameter `pdu` in method `onSmsStatusReportReceived` + MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsStatusReportReceived(int, int, String, byte[]) parameter #2: - Missing nullability on parameter `format` in method `onSmsStatusReportReceived` + MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsStatusReportReceived(int, int, String, byte[]) parameter #3: - Missing nullability on parameter `pdu` in method `onSmsStatusReportReceived` + MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#sendSms(int, int, String, String, boolean, byte[]) parameter #2: - Missing nullability on parameter `format` in method `sendSms` + MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#sendSms(int, int, String, String, boolean, byte[]) parameter #3: - Missing nullability on parameter `smsc` in method `sendSms` + MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#sendSms(int, int, String, String, boolean, byte[]) parameter #5: - Missing nullability on parameter `pdu` in method `sendSms` + MissingNullability: android.telephony.ims.stub.ImsUtImplBase#queryCallForward(int, String) parameter #1: - Missing nullability on parameter `number` in method `queryCallForward` + MissingNullability: android.telephony.ims.stub.ImsUtImplBase#setListener(android.telephony.ims.ImsUtListener) parameter #0: - Missing nullability on parameter `listener` in method `setListener` + MissingNullability: android.telephony.ims.stub.ImsUtImplBase#transact(android.os.Bundle) parameter #0: - Missing nullability on parameter `ssInfo` in method `transact` + MissingNullability: android.telephony.ims.stub.ImsUtImplBase#updateCallBarring(int, int, String[]) parameter #2: - Missing nullability on parameter `barrList` in method `updateCallBarring` + MissingNullability: android.telephony.ims.stub.ImsUtImplBase#updateCallBarringForServiceClass(int, int, String[], int) parameter #2: - Missing nullability on parameter `barrList` in method `updateCallBarringForServiceClass` + MissingNullability: android.telephony.ims.stub.ImsUtImplBase#updateCallForward(int, int, String, int, int) parameter #2: - Missing nullability on parameter `number` in method `updateCallForward` + MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String): MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String) parameter #0: @@ -2278,7 +2280,7 @@ NotCloseable: android.app.prediction.AppPredictor: NotCloseable: android.os.HwParcel: NotCloseable: android.telephony.ims.stub.ImsUtImplBase: - Classes that release resources (close()) should implement AutoClosable and CloseGuard: class android.telephony.ims.stub.ImsUtImplBase + OnNameExpected: android.service.autofill.augmented.AugmentedAutofillService#dump(java.io.PrintWriter, String[]): @@ -2292,21 +2294,21 @@ OnNameExpected: android.service.notification.NotificationAssistantService#attach OnNameExpected: android.service.quicksettings.TileService#isQuickSettingsSupported(): OnNameExpected: android.telephony.ims.ImsService#createMmTelFeature(int): - If implemented by developer, should follow the on<Something> style; otherwise consider marking final + OnNameExpected: android.telephony.ims.ImsService#createRcsFeature(int): - If implemented by developer, should follow the on<Something> style; otherwise consider marking final + OnNameExpected: android.telephony.ims.ImsService#disableIms(int): - If implemented by developer, should follow the on<Something> style; otherwise consider marking final + OnNameExpected: android.telephony.ims.ImsService#enableIms(int): - If implemented by developer, should follow the on<Something> style; otherwise consider marking final + OnNameExpected: android.telephony.ims.ImsService#getConfig(int): - If implemented by developer, should follow the on<Something> style; otherwise consider marking final + OnNameExpected: android.telephony.ims.ImsService#getRegistration(int): - If implemented by developer, should follow the on<Something> style; otherwise consider marking final + OnNameExpected: android.telephony.ims.ImsService#querySupportedImsFeatures(): - If implemented by developer, should follow the on<Something> style; otherwise consider marking final + OnNameExpected: android.telephony.ims.ImsService#readyForFeatureCreation(): - If implemented by developer, should follow the on<Something> style; otherwise consider marking final + OnNameExpected: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#dispose(int): OnNameExpected: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int): @@ -2446,7 +2448,7 @@ RethrowRemoteException: android.os.HwBinder#transact(int, android.os.HwParcel, a RethrowRemoteException: android.os.IHwBinder#transact(int, android.os.HwParcel, android.os.HwParcel, int): RethrowRemoteException: android.telephony.ims.ImsService#onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration): - Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause) + RethrowRemoteException: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener): RethrowRemoteException: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener): @@ -2526,7 +2528,7 @@ SamShouldBeLast: android.service.autofill.ImageTransformation#apply(android.serv SamShouldBeLast: android.service.autofill.InternalTransformation#batchApply(android.service.autofill.ValueFinder, android.widget.RemoteViews, java.util.ArrayList<android.util.Pair<java.lang.Integer,android.service.autofill.InternalTransformation>>): SamShouldBeLast: android.telephony.ims.ImsMmTelManager#getFeatureState(java.util.function.Consumer<java.lang.Integer>, java.util.concurrent.Executor): - SAM-compatible parameters (such as parameter 1, "callback", in android.telephony.ims.ImsMmTelManager.getFeatureState) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions + SamShouldBeLast: android.view.Choreographer#postCallback(int, Runnable, Object): SamShouldBeLast: android.view.Choreographer#postCallbackDelayed(int, Runnable, Object, long): @@ -2599,6 +2601,8 @@ UserHandle: android.app.role.RoleManager#removeOnRoleHoldersChangedListenerAsUse UserHandle: android.app.role.RoleManager#removeRoleHolderAsUser(String, String, int, android.os.UserHandle, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Boolean>): +UserHandle: android.companion.CompanionDeviceManager#isDeviceAssociated(String, android.net.MacAddress, android.os.UserHandle): + When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added UserHandle: android.content.pm.PackageManager#getInstallReason(String, android.os.UserHandle): UserHandle: android.content.pm.PackageManager#getPermissionFlags(String, String, android.os.UserHandle): diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 1fd3abfc5adc..e8c0e1540e9c 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -1275,7 +1275,7 @@ Status StatsService::sendAppBreadcrumbAtom(int32_t label, int32_t state) { // Permission check not necessary as it's meant for applications to write to // statsd. android::util::stats_write(util::APP_BREADCRUMB_REPORTED, - IPCThreadState::self()->getCallingUid(), label, + (int32_t) IPCThreadState::self()->getCallingUid(), label, state); return Status::ok(); } diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index b9051daab6ae..2efb78943812 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -50,6 +50,7 @@ import "frameworks/base/core/proto/android/stats/intelligence/enums.proto"; import "frameworks/base/core/proto/android/stats/launcher/launcher.proto"; import "frameworks/base/core/proto/android/stats/location/location_enums.proto"; import "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.proto"; +import "frameworks/base/core/proto/android/stats/mediaprovider/mediaprovider_enums.proto"; import "frameworks/base/core/proto/android/stats/storage/storage_enums.proto"; import "frameworks/base/core/proto/android/stats/style/style_enums.proto"; import "frameworks/base/core/proto/android/telecomm/enums.proto"; @@ -342,6 +343,16 @@ message Atom { VmsClientConnectionStateChanged vms_client_connection_state_changed = 230; GpsLocationStatusReported gps_location_status_reported = 231; GpsTimeToFirstFixReported gps_time_to_first_fix_reported = 232; + MediaProviderScanEvent media_provider_scan_event = + 233 [(log_from_module) = "mediaprovider"]; + MediaProviderDeletionEvent media_provider_deletion_event = + 234 [(log_from_module) = "mediaprovider"]; + MediaProviderPermissionEvent media_provider_permission_event = + 235 [(log_from_module) = "mediaprovider"]; + MediaProviderSchemaChange media_provider_schema_change = + 236 [(log_from_module) = "mediaprovider"]; + MediaProviderIdleMaintenance media_provider_idle_maintenance = + 237 [(log_from_module) = "mediaprovider"]; } // Pulled events will start at field 10000. @@ -3799,6 +3810,124 @@ message VmsClientConnectionStateChanged { optional State state = 2; } +/** + * Logs when MediaProvider has successfully finished scanning a storage volume. + * + * Logged from: + * packages/providers/MediaProvider/src/com/android/providers/media/scan/ModernMediaScanner.java + */ +message MediaProviderScanEvent { + enum Reason { + // Scan triggered due to unknown reason + UNKNOWN = 0; + // Scan triggered due to storage volume being mounted + MOUNTED = 1; + // Scan triggered due to explicit user action or app request + DEMAND = 2; + // Scan triggered due to idle maintenance + IDLE = 3; + } + + // Volume type that this event pertains to + optional android.stats.mediaprovider.VolumeType volume_type = 1; + // Reason why this scan was triggered + optional Reason reason = 2; + // Total number of files scanned + optional int64 item_count = 3; + // Duration of scan, normalized per file + optional float normalized_duration_millis = 4; + // Number of database inserts, normalized per file + optional float normalized_insert_count = 5; + // Number of database updates, normalized per file + optional float normalized_update_count = 6; + // Number of database deletes, normalized per file + optional float normalized_delete_count = 7; +} + +/** + * Logs when an app has asked MediaProvider to delete media belonging to the user. + * + * Logged from: + * packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java + */ +message MediaProviderDeletionEvent { + // Volume type that this event pertains to + optional android.stats.mediaprovider.VolumeType volume_type = 1; + // Device timestamp when this deletion event occurred + optional int64 timestamp_millis = 2; + // App that requested deletion + optional string package_name = 3; + // Number of items that were deleted + optional int32 item_count = 4; +} + +/** + * Logs when an app has asked MediaProvider to grant them access to media belonging to the user. + * + * Logged from: + * packages/providers/MediaProvider/src/com/android/providers/media/PermissionActivity.java + */ +message MediaProviderPermissionEvent { + enum Result { + UNKNOWN = 0; + USER_GRANTED = 1; + AUTO_GRANTED = 2; + USER_DENIED = 3; + USER_DENIED_WITH_PREJUDICE = 4; + AUTO_DENIED = 5; + } + + // Volume type that this event pertains to + optional android.stats.mediaprovider.VolumeType volume_type = 1; + // Device timestamp when this permission event occurred + optional int64 timestamp_millis = 2; + // App that requested permission + optional string package_name = 3; + // Number of items that were requested + optional int32 item_count = 4; + // Result of this request + optional Result result = 5; +} + +/** + * Logs when MediaProvider has finished upgrading or downgrading its database schema. + * + * Logged from: + * packages/providers/MediaProvider/src/com/android/providers/media/DatabaseHelper.java + */ +message MediaProviderSchemaChange { + // Volume type that this event pertains to + optional android.stats.mediaprovider.VolumeType volume_type = 1; + // Old database version code + optional int32 version_from = 2; + // New database version code + optional int32 version_to = 3; + // Total number of files in database + optional int64 item_count = 4; + // Duration of schema change, normalized per file + optional float normalized_duration_millis = 5; +} + +/** + * Logs when MediaProvider has finished an idle maintenance job. + * + * Logged from: + * packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java + */ +message MediaProviderIdleMaintenance { + // Volume type that this event pertains to + optional android.stats.mediaprovider.VolumeType volume_type = 1; + + // Total number of files in database + optional int64 item_count = 2; + // Duration of idle maintenance, normalized per file + optional float normalized_duration_millis = 3; + // Number of thumbnails found to be stale, normalized per file + optional float normalized_stale_thumbnails = 4; + // Number of items found to be expired, normalized per file + optional float normalized_expired_media = 5; +} + ////////////////////////////////////////////////////////////////////// // Pulled atoms below this line // ////////////////////////////////////////////////////////////////////// @@ -5798,10 +5927,10 @@ message PermissionGrantRequestResultReported { optional int64 request_id = 1; // UID of package requesting the permission grant - optional int32 requesting_uid = 2 [(is_uid) = true]; + optional int32 uid = 2 [(is_uid) = true]; // Name of package requesting the permission grant - optional string requesting_package_name = 3; + optional string package_name = 3; // The permission to be granted optional string permission_name = 4; diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java index 03b08f785f74..1987440106f5 100644 --- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java +++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java @@ -16,10 +16,12 @@ package com.android.commands.telecom; +import android.app.ActivityThread; import android.content.ComponentName; import android.content.Context; import android.net.Uri; import android.os.IUserManager; +import android.os.Looper; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; @@ -33,7 +35,6 @@ import android.text.TextUtils; import com.android.internal.os.BaseCommand; import com.android.internal.telecom.ITelecomService; -import com.android.internal.telephony.ITelephony; import java.io.PrintStream; @@ -82,7 +83,7 @@ public final class Telecom extends BaseCommand { private ComponentName mComponent; private String mAccountId; private ITelecomService mTelecomService; - private ITelephony mTelephonyService; + private TelephonyManager mTelephonyManager; private IUserManager mUserManager; @Override @@ -153,9 +154,10 @@ public final class Telecom extends BaseCommand { return; } - mTelephonyService = ITelephony.Stub.asInterface( - ServiceManager.getService(Context.TELEPHONY_SERVICE)); - if (mTelephonyService == null) { + Looper.prepareMainLooper(); + Context context = ActivityThread.systemMain().getSystemContext(); + mTelephonyManager = context.getSystemService(TelephonyManager.class); + if (mTelephonyManager == null) { Log.w(this, "onRun: Can't access telephony service."); showError("Error: Could not access the Telephony Service. Is the system running?"); return; @@ -351,7 +353,7 @@ public final class Telecom extends BaseCommand { } int numSims = Integer.parseInt(nextArgRequired()); System.out.println("Setting sim count to " + numSims + ". Device may reboot"); - mTelephonyService.switchMultiSimConfig(numSims); + mTelephonyManager.switchMultiSimConfig(numSims); } /** @@ -365,8 +367,7 @@ public final class Telecom extends BaseCommand { private void runGetMaxPhones() throws RemoteException { // This assumes the max number of SIMs is 2, which it currently is - if (TelephonyManager.MULTISIM_ALLOWED - == mTelephonyService.isMultiSimSupported("com.android.commands.telecom", null)) { + if (TelephonyManager.MULTISIM_ALLOWED == mTelephonyManager.isMultiSimSupported()) { System.out.println("2"); } else { System.out.println("1"); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 0113f6912183..034826a8d5fa 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -39,6 +39,7 @@ import android.content.pm.IPackageDeleteObserver; import android.content.pm.IPackageManager; import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageStatsObserver; +import android.content.pm.InstallSourceInfo; import android.content.pm.InstantAppInfo; import android.content.pm.InstrumentationInfo; import android.content.pm.IntentFilterVerificationInfo; @@ -2085,6 +2086,21 @@ public class ApplicationPackageManager extends PackageManager { } @Override + @NonNull + public InstallSourceInfo getInstallSourceInfo(String packageName) throws NameNotFoundException { + final InstallSourceInfo installSourceInfo; + try { + installSourceInfo = mPM.getInstallSourceInfo(packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + if (installSourceInfo == null) { + throw new NameNotFoundException(packageName); + } + return installSourceInfo; + } + + @Override public int getMoveStatus(int moveId) { try { return mPM.getMoveStatus(moveId); @@ -2782,7 +2798,7 @@ public class ApplicationPackageManager extends PackageManager { public Drawable loadUnbadgedItemIcon(@NonNull PackageItemInfo itemInfo, @Nullable ApplicationInfo appInfo) { if (itemInfo.showUserIcon != UserHandle.USER_NULL) { - // Indicates itemInfo is for a different user (e.g. a profile's parent), so use a + // Indicates itemInfo is for a different user (e.g. a profile's parent), so use a // generic user icon (users generally lack permission to view each other's actual icons) int targetUserId = itemInfo.showUserIcon; return UserIcons.getDefaultUserIcon( diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 50d0dab62ea1..aa8a3023a40f 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -387,6 +387,7 @@ interface IActivityManager { void requestInteractiveBugReport(); void requestFullBugReport(); void requestRemoteBugReport(); + boolean launchBugReportHandlerApp(); @UnsupportedAppUsage Intent getIntentForIntentSender(in IIntentSender sender); diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java index 11d6528ab6af..2ef05105825a 100644 --- a/core/java/android/app/StatsManager.java +++ b/core/java/android/app/StatsManager.java @@ -51,12 +51,13 @@ public final class StatsManager { private static final String TAG = "StatsManager"; private static final boolean DEBUG = false; + private static final Object sLock = new Object(); private final Context mContext; - @GuardedBy("this") + @GuardedBy("sLock") private IStatsManager mService; - @GuardedBy("this") + @GuardedBy("sLock") private IStatsCompanionService mStatsCompanion; /** @@ -125,7 +126,7 @@ public final class StatsManager { */ @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) public void addConfig(long configKey, byte[] config) throws StatsUnavailableException { - synchronized (this) { + synchronized (sLock) { try { IStatsManager service = getIStatsManagerLocked(); // can throw IllegalArgumentException @@ -162,7 +163,7 @@ public final class StatsManager { */ @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) public void removeConfig(long configKey) throws StatsUnavailableException { - synchronized (this) { + synchronized (sLock) { try { IStatsManager service = getIStatsManagerLocked(); service.removeConfiguration(configKey, mContext.getOpPackageName()); @@ -223,7 +224,7 @@ public final class StatsManager { public void setBroadcastSubscriber( PendingIntent pendingIntent, long configKey, long subscriberId) throws StatsUnavailableException { - synchronized (this) { + synchronized (sLock) { try { IStatsManager service = getIStatsManagerLocked(); if (pendingIntent != null) { @@ -277,7 +278,7 @@ public final class StatsManager { @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) public void setFetchReportsOperation(PendingIntent pendingIntent, long configKey) throws StatsUnavailableException { - synchronized (this) { + synchronized (sLock) { try { IStatsManager service = getIStatsManagerLocked(); if (pendingIntent == null) { @@ -315,7 +316,7 @@ public final class StatsManager { @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) public @NonNull long[] setActiveConfigsChangedOperation(@Nullable PendingIntent pendingIntent) throws StatsUnavailableException { - synchronized (this) { + synchronized (sLock) { try { IStatsManager service = getIStatsManagerLocked(); if (pendingIntent == null) { @@ -363,7 +364,7 @@ public final class StatsManager { */ @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) public byte[] getReports(long configKey) throws StatsUnavailableException { - synchronized (this) { + synchronized (sLock) { try { IStatsManager service = getIStatsManagerLocked(); return service.getData(configKey, mContext.getOpPackageName()); @@ -400,7 +401,7 @@ public final class StatsManager { */ @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) public byte[] getStatsMetadata() throws StatsUnavailableException { - synchronized (this) { + synchronized (sLock) { try { IStatsManager service = getIStatsManagerLocked(); return service.getMetadata(mContext.getOpPackageName()); @@ -435,7 +436,7 @@ public final class StatsManager { @RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS}) public long[] getRegisteredExperimentIds() throws StatsUnavailableException { - synchronized (this) { + synchronized (sLock) { try { IStatsManager service = getIStatsManagerLocked(); if (service == null) { @@ -472,7 +473,7 @@ public final class StatsManager { @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) public void setPullerCallback(int atomTag, IStatsPullerCallback callback) throws StatsUnavailableException { - synchronized (this) { + synchronized (sLock) { try { IStatsManager service = getIStatsManagerLocked(); if (callback == null) { @@ -515,7 +516,7 @@ public final class StatsManager { if (additiveFields == null) { additiveFields = new int[0]; } - synchronized (this) { + synchronized (sLock) { IStatsCompanionService service = getIStatsCompanionServiceLocked(); PullAtomCallbackInternal rec = new PullAtomCallbackInternal(atomTag, callback, executor); @@ -649,13 +650,13 @@ public final class StatsManager { private class StatsdDeathRecipient implements IBinder.DeathRecipient { @Override public void binderDied() { - synchronized (this) { + synchronized (sLock) { mService = null; } } } - @GuardedBy("this") + @GuardedBy("sLock") private IStatsManager getIStatsManagerLocked() throws StatsUnavailableException { if (mService != null) { return mService; @@ -672,7 +673,7 @@ public final class StatsManager { return mService; } - @GuardedBy("this") + @GuardedBy("sLock") private IStatsCompanionService getIStatsCompanionServiceLocked() { if (mStatsCompanion != null) { return mStatsCompanion; diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 1829f74700fd..601b65863db1 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -1137,7 +1137,8 @@ public final class SystemServiceRegistry { registerService(Context.PERMISSION_SERVICE, PermissionManager.class, new CachedServiceFetcher<PermissionManager>() { @Override - public PermissionManager createService(ContextImpl ctx) { + public PermissionManager createService(ContextImpl ctx) + throws ServiceNotFoundException { IPackageManager packageManager = AppGlobals.getPackageManager(); return new PermissionManager(ctx.getOuterContext(), packageManager); }}); diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java index 00903c43b291..63bc40b86aa7 100644 --- a/core/java/android/app/admin/DeviceAdminInfo.java +++ b/core/java/android/app/admin/DeviceAdminInfo.java @@ -55,6 +55,14 @@ public final class DeviceAdminInfo implements Parcelable { static final String TAG = "DeviceAdminInfo"; /** + * A type of policy that this device admin can use: profile owner on an organization-owned + * device. + * + * @hide + */ + public static final int USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER = -3; + + /** * A type of policy that this device admin can use: device owner meta-policy * for an admin that is designated as owner of the device. * diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 39dc51e307f1..34ceb08f39bf 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -9135,7 +9135,14 @@ public class DevicePolicyManager { } /** - * Called by device owner to get the MAC address of the Wi-Fi device. + * Called by device owner, or profile owner on organization-owned device, to get the MAC + * address of the Wi-Fi device. + * + * NOTE: The MAC address returned here should only be used for inventory management and is + * not likely to be the MAC address used by the device to connect to Wi-Fi networks: MAC + * addresses used for scanning and connecting to Wi-Fi networks are randomized by default. + * To get the randomized MAC address used, call + * {@link android.net.wifi.WifiConfiguration#getRandomizedMacAddress}. * * @param admin Which device owner this request is associated with. * @return the MAC address of the Wi-Fi device, or null when the information is not available. diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java index 909cbc2ccdf7..387a36bba608 100644 --- a/core/java/android/app/timezonedetector/TimeZoneDetector.java +++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java @@ -17,6 +17,7 @@ package android.app.timezonedetector; import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.content.Context; import android.os.RemoteException; @@ -26,10 +27,11 @@ import android.util.Log; /** * The interface through which system components can send signals to the TimeZoneDetectorService. + * * @hide */ @SystemService(Context.TIME_ZONE_DETECTOR_SERVICE) -public final class TimeZoneDetector { +public class TimeZoneDetector { private static final String TAG = "timezonedetector.TimeZoneDetector"; private static final boolean DEBUG = false; @@ -41,10 +43,11 @@ public final class TimeZoneDetector { } /** - * Suggests the current time zone to the detector. The detector may ignore the signal if better - * signals are available such as those that come from more reliable sources or were - * determined more recently. + * Suggests the current time zone, determined using telephony signals, to the detector. The + * detector may ignore the signal based on system settings, whether better information is + * available, and so on. */ + @RequiresPermission(android.Manifest.permission.SET_TIME_ZONE) public void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion timeZoneSuggestion) { if (DEBUG) { Log.d(TAG, "suggestPhoneTimeZone called: " + timeZoneSuggestion); @@ -56,4 +59,28 @@ public final class TimeZoneDetector { } } + /** + * Suggests the current time zone, determined for the user's manually information, to the + * detector. The detector may ignore the signal based on system settings. + */ + @RequiresPermission(android.Manifest.permission.SET_TIME_ZONE) + public void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion) { + if (DEBUG) { + Log.d(TAG, "suggestManualTimeZone called: " + timeZoneSuggestion); + } + try { + mITimeZoneDetectorService.suggestManualTimeZone(timeZoneSuggestion); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * A shared utility method to create a {@link ManualTimeZoneSuggestion}. + */ + public static ManualTimeZoneSuggestion createManualTimeZoneSuggestion(String tzId, String why) { + ManualTimeZoneSuggestion suggestion = new ManualTimeZoneSuggestion(tzId); + suggestion.addDebugInfo(why); + return suggestion; + } } diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java index accdd8dc64bd..8ed61b6c87e3 100644 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ b/core/java/android/bluetooth/BluetoothA2dp.java @@ -610,7 +610,7 @@ public final class BluetoothA2dp implements BluetoothProfile { if (uuids == null) return false; for (ParcelUuid uuid : uuids) { - if (BluetoothUuid.isAvrcpTarget(uuid)) { + if (uuid.equals(BluetoothUuid.AVRCP_TARGET)) { return true; } } diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index ea3831a869fc..0955b103a8e8 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -559,7 +559,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * * <p> The device should already be paired. * Priority can be one of {@link BluetoothProfile#PRIORITY_ON} or - * {@link BluetoothProfile#PRIORITY_OFF}, + * {@link BluetoothProfile#PRIORITY_OFF} * * @param device Paired bluetooth device * @param priority @@ -1133,8 +1133,9 @@ public final class BluetoothHeadset implements BluetoothProfile { * is active. * @hide */ + @SystemApi + @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH) - @UnsupportedAppUsage public BluetoothDevice getActiveDevice() { if (VDBG) { Log.d(TAG, "getActiveDevice"); diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java index a8e1fd26ad44..7ee29ff1c50b 100644 --- a/core/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java @@ -557,7 +557,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * Set priority of the profile * * <p> The device should already be paired. - * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF}, + * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF} * * @param device Paired bluetooth device * @param priority diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java index bc3c9a9ebf4b..7e96c23af4b9 100644 --- a/core/java/android/bluetooth/BluetoothUuid.java +++ b/core/java/android/bluetooth/BluetoothUuid.java @@ -16,8 +16,10 @@ package android.bluetooth; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; -import android.os.Build; import android.os.ParcelUuid; import java.nio.ByteBuffer; @@ -31,6 +33,7 @@ import java.util.UUID; * * @hide */ +@SystemApi public final class BluetoothUuid { /* See Bluetooth Assigned Numbers document - SDP section, to get the values of UUIDs @@ -39,167 +42,157 @@ public final class BluetoothUuid { * The following 128 bit values are calculated as: * uuid * 2^96 + BASE_UUID */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public static final ParcelUuid AudioSink = + + /** @hide */ + @NonNull + @SystemApi + public static final ParcelUuid A2DP_SINK = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); - public static final ParcelUuid AudioSource = + /** @hide */ + @NonNull + @SystemApi + public static final ParcelUuid A2DP_SOURCE = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public static final ParcelUuid AdvAudioDist = + /** @hide */ + @NonNull + @SystemApi + public static final ParcelUuid ADV_AUDIO_DIST = ParcelUuid.fromString("0000110D-0000-1000-8000-00805F9B34FB"); - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid HSP = ParcelUuid.fromString("00001108-0000-1000-8000-00805F9B34FB"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid HSP_AG = ParcelUuid.fromString("00001112-0000-1000-8000-00805F9B34FB"); - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public static final ParcelUuid Handsfree = + /** @hide */ + @NonNull + @SystemApi + public static final ParcelUuid HFP = ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB"); - public static final ParcelUuid Handsfree_AG = + /** @hide */ + @NonNull + @SystemApi + public static final ParcelUuid HFP_AG = ParcelUuid.fromString("0000111F-0000-1000-8000-00805F9B34FB"); - public static final ParcelUuid AvrcpController = + /** @hide */ + @NonNull + @SystemApi + public static final ParcelUuid AVRCP_CONTROLLER = ParcelUuid.fromString("0000110E-0000-1000-8000-00805F9B34FB"); - public static final ParcelUuid AvrcpTarget = + /** @hide */ + @NonNull + @SystemApi + public static final ParcelUuid AVRCP_TARGET = ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"); - @UnsupportedAppUsage - public static final ParcelUuid ObexObjectPush = + /** @hide */ + @NonNull + @SystemApi + public static final ParcelUuid OBEX_OBJECT_PUSH = ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb"); - public static final ParcelUuid Hid = + /** @hide */ + @NonNull + @SystemApi + public static final ParcelUuid HID = ParcelUuid.fromString("00001124-0000-1000-8000-00805f9b34fb"); - @UnsupportedAppUsage - public static final ParcelUuid Hogp = + /** @hide */ + @NonNull + @SystemApi + public static final ParcelUuid HOGP = ParcelUuid.fromString("00001812-0000-1000-8000-00805f9b34fb"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid PANU = ParcelUuid.fromString("00001115-0000-1000-8000-00805F9B34FB"); - @UnsupportedAppUsage + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid NAP = ParcelUuid.fromString("00001116-0000-1000-8000-00805F9B34FB"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid BNEP = ParcelUuid.fromString("0000000f-0000-1000-8000-00805F9B34FB"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid PBAP_PCE = ParcelUuid.fromString("0000112e-0000-1000-8000-00805F9B34FB"); - @UnsupportedAppUsage + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid PBAP_PSE = ParcelUuid.fromString("0000112f-0000-1000-8000-00805F9B34FB"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid MAP = ParcelUuid.fromString("00001134-0000-1000-8000-00805F9B34FB"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid MNS = ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid MAS = ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid SAP = ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB"); - public static final ParcelUuid HearingAid = + /** @hide */ + @NonNull + @SystemApi + public static final ParcelUuid HEARING_AID = ParcelUuid.fromString("0000FDF0-0000-1000-8000-00805f9b34fb"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid BASE_UUID = ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); - /** Length of bytes for 16 bit UUID */ + /** + * Length of bytes for 16 bit UUID + * + * @hide + */ + @SystemApi public static final int UUID_BYTES_16_BIT = 2; - /** Length of bytes for 32 bit UUID */ + /** + * Length of bytes for 32 bit UUID + * + * @hide + */ + @SystemApi public static final int UUID_BYTES_32_BIT = 4; - /** Length of bytes for 128 bit UUID */ - public static final int UUID_BYTES_128_BIT = 16; - - @UnsupportedAppUsage - public static final ParcelUuid[] RESERVED_UUIDS = { - AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, - ObexObjectPush, PANU, NAP, MAP, MNS, MAS, SAP}; - - @UnsupportedAppUsage - public static boolean isAudioSource(ParcelUuid uuid) { - return uuid.equals(AudioSource); - } - - public static boolean isAudioSink(ParcelUuid uuid) { - return uuid.equals(AudioSink); - } - - @UnsupportedAppUsage - public static boolean isAdvAudioDist(ParcelUuid uuid) { - return uuid.equals(AdvAudioDist); - } - - public static boolean isHandsfree(ParcelUuid uuid) { - return uuid.equals(Handsfree); - } - - public static boolean isHeadset(ParcelUuid uuid) { - return uuid.equals(HSP); - } - - public static boolean isAvrcpController(ParcelUuid uuid) { - return uuid.equals(AvrcpController); - } - - @UnsupportedAppUsage - public static boolean isAvrcpTarget(ParcelUuid uuid) { - return uuid.equals(AvrcpTarget); - } - - public static boolean isInputDevice(ParcelUuid uuid) { - return uuid.equals(Hid); - } - - public static boolean isPanu(ParcelUuid uuid) { - return uuid.equals(PANU); - } - - public static boolean isNap(ParcelUuid uuid) { - return uuid.equals(NAP); - } - - public static boolean isBnep(ParcelUuid uuid) { - return uuid.equals(BNEP); - } - - public static boolean isMap(ParcelUuid uuid) { - return uuid.equals(MAP); - } - - public static boolean isMns(ParcelUuid uuid) { - return uuid.equals(MNS); - } - - public static boolean isMas(ParcelUuid uuid) { - return uuid.equals(MAS); - } - - public static boolean isSap(ParcelUuid uuid) { - return uuid.equals(SAP); - } - /** - * Returns true if ParcelUuid is present in uuidArray + * Length of bytes for 128 bit UUID * - * @param uuidArray - Array of ParcelUuids - * @param uuid + * @hide */ - @UnsupportedAppUsage - public static boolean isUuidPresent(ParcelUuid[] uuidArray, ParcelUuid uuid) { - if ((uuidArray == null || uuidArray.length == 0) && uuid == null) { - return true; - } - - if (uuidArray == null) { - return false; - } - - for (ParcelUuid element : uuidArray) { - if (element.equals(uuid)) return true; - } - return false; - } + @SystemApi + public static final int UUID_BYTES_128_BIT = 16; /** * Returns true if there any common ParcelUuids in uuidA and uuidB. * * @param uuidA - List of ParcelUuids * @param uuidB - List of ParcelUuids + * + * @hide */ - @UnsupportedAppUsage - public static boolean containsAnyUuid(ParcelUuid[] uuidA, ParcelUuid[] uuidB) { + @SystemApi + public static boolean containsAnyUuid(@Nullable ParcelUuid[] uuidA, + @Nullable ParcelUuid[] uuidB) { if (uuidA == null && uuidB == null) return true; if (uuidA == null) { @@ -218,29 +211,6 @@ public final class BluetoothUuid { } /** - * Returns true if all the ParcelUuids in ParcelUuidB are present in - * ParcelUuidA - * - * @param uuidA - Array of ParcelUuidsA - * @param uuidB - Array of ParcelUuidsB - */ - public static boolean containsAllUuids(ParcelUuid[] uuidA, ParcelUuid[] uuidB) { - if (uuidA == null && uuidB == null) return true; - - if (uuidA == null) { - return uuidB.length == 0; - } - - if (uuidB == null) return true; - - HashSet<ParcelUuid> uuidSet = new HashSet<ParcelUuid>(Arrays.asList(uuidA)); - for (ParcelUuid uuid : uuidB) { - if (!uuidSet.contains(uuid)) return false; - } - return true; - } - - /** * Extract the Service Identifier or the actual uuid from the Parcel Uuid. * For example, if 0000110B-0000-1000-8000-00805F9B34FB is the parcel Uuid, * this function will return 110B @@ -248,7 +218,7 @@ public final class BluetoothUuid { * @param parcelUuid * @return the service identifier. */ - public static int getServiceIdentifierFromParcelUuid(ParcelUuid parcelUuid) { + private static int getServiceIdentifierFromParcelUuid(ParcelUuid parcelUuid) { UUID uuid = parcelUuid.getUuid(); long value = (uuid.getMostSignificantBits() & 0xFFFFFFFF00000000L) >>> 32; return (int) value; @@ -262,8 +232,12 @@ public final class BluetoothUuid { * @param uuidBytes Byte representation of uuid. * @return {@link ParcelUuid} parsed from bytes. * @throws IllegalArgumentException If the {@code uuidBytes} cannot be parsed. + * + * @hide */ - public static ParcelUuid parseUuidFrom(byte[] uuidBytes) { + @NonNull + @SystemApi + public static ParcelUuid parseUuidFrom(@Nullable byte[] uuidBytes) { if (uuidBytes == null) { throw new IllegalArgumentException("uuidBytes cannot be null"); } @@ -305,6 +279,8 @@ public final class BluetoothUuid { * @param uuid uuid to parse. * @return shortest representation of {@code uuid} as bytes. * @throws IllegalArgumentException If the {@code uuid} is null. + * + * @hide */ public static byte[] uuidToBytes(ParcelUuid uuid) { if (uuid == null) { @@ -345,6 +321,8 @@ public final class BluetoothUuid { * * @param parcelUuid * @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise. + * + * @hide */ @UnsupportedAppUsage public static boolean is16BitUuid(ParcelUuid parcelUuid) { @@ -361,6 +339,8 @@ public final class BluetoothUuid { * * @param parcelUuid * @return true if the parcelUuid can be converted to 32 bit uuid, false otherwise. + * + * @hide */ @UnsupportedAppUsage public static boolean is32BitUuid(ParcelUuid parcelUuid) { @@ -373,4 +353,6 @@ public final class BluetoothUuid { } return ((uuid.getMostSignificantBits() & 0xFFFFFFFFL) == 0x1000L); } + + private BluetoothUuid() {} } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 341b5206ba90..d370a380bc3b 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3427,7 +3427,6 @@ public abstract class Context { //@hide: TIME_DETECTOR_SERVICE, //@hide: TIME_ZONE_DETECTOR_SERVICE, PERMISSION_SERVICE, - INCREMENTAL_SERVICE, }) @Retention(RetentionPolicy.SOURCE) public @interface ServiceName {} @@ -4971,6 +4970,13 @@ public abstract class Context { /** * Use with {@link #getSystemService(String)} to retrieve an + * {@link android.content.pm.DataLoaderManager}. + * @hide + */ + public static final String DATA_LOADER_MANAGER_SERVICE = "dataloadermanager"; + + /** + * Use with {@link #getSystemService(String)} to retrieve an * {@link android.os.incremental.IncrementalManager}. * @hide */ diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 40aca0ef2033..af7b98683219 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -4053,6 +4053,13 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_SERVICE_STATE = "android.intent.action.SERVICE_STATE"; /** + * Used for looking up a Data Loader Service providers. + * @hide + */ + @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) + public static final String ACTION_LOAD_DATA = "android.intent.action.LOAD_DATA"; + + /** * An int extra used with {@link #ACTION_SERVICE_STATE} which indicates voice registration * state. * @see android.telephony.ServiceState#STATE_EMERGENCY_ONLY diff --git a/core/java/android/content/pm/DataLoaderManager.java b/core/java/android/content/pm/DataLoaderManager.java new file mode 100644 index 000000000000..26880384e502 --- /dev/null +++ b/core/java/android/content/pm/DataLoaderManager.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Bundle; +import android.os.RemoteException; + +/** + * Data loader manager takes care of data loaders of different packages. It provides methods to + * initialize a data loader binder service (binding and creating it), to return a binder of the data + * loader binder service and to destroy a data loader binder service. + * @see com.android.server.pm.DataLoaderManagerService + * @hide + */ +public class DataLoaderManager { + private static final String TAG = "DataLoaderManager"; + private final IDataLoaderManager mService; + + public DataLoaderManager(IDataLoaderManager service) { + mService = service; + } + + /** + * Finds a data loader binder service and binds to it. This requires PackageManager. + * + * @param dataLoaderId ID for the new data loader binder service. + * @param params Bundle that contains parameters to configure the data loader service. + * Must contain: + * key: "packageName", value: String, package name of data loader service + * package; + * key: "extras", value: Bundle, client-specific data structures + * + * @param listener Callback for the data loader service to report status back to the + * caller. + * @return false if 1) target ID collides with a data loader that is already bound to data + * loader manager; 2) package name is not specified; 3) fails to find data loader package; + * or 4) fails to bind to the specified data loader service, otherwise return true. + */ + public boolean initializeDataLoader(int dataLoaderId, @NonNull Bundle params, + @NonNull IDataLoaderStatusListener listener) { + try { + return mService.initializeDataLoader(dataLoaderId, params, listener); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns a binder interface of the data loader binder service, given its ID. + */ + @Nullable + public IDataLoader getDataLoader(int dataLoaderId) { + try { + return mService.getDataLoader(dataLoaderId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Destroys the data loader binder service and removes it from data loader manager service. + */ + @Nullable + public void destroyDataLoader(int dataLoaderId) { + try { + mService.destroyDataLoader(dataLoaderId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } +} diff --git a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SourcePosition.java b/core/java/android/content/pm/IDataLoader.aidl index 4ae093c3c2e4..60cc9ba9e141 100644 --- a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SourcePosition.java +++ b/core/java/android/content/pm/IDataLoader.aidl @@ -14,23 +14,21 @@ * limitations under the License. */ -package android.processor.unsupportedappusage; +package android.content.pm; + +import android.os.Bundle; +import android.content.pm.IDataLoaderStatusListener; +import android.content.pm.InstallationFile; +import java.util.List; /** - * Represents a source position within a source file + * TODO: update with new APIs + * @hide */ -public class SourcePosition { - public final String filename; - public final int startLine; - public final int startCol; - public final int endLine; - public final int endCol; - - public SourcePosition(String filename, int startLine, int startCol, int endLine, int endCol) { - this.filename = filename; - this.startLine = startLine; - this.startCol = startCol; - this.endLine = endLine; - this.endCol = endCol; - } +oneway interface IDataLoader { + void create(int id, in Bundle params, IDataLoaderStatusListener listener); + void start(in List<InstallationFile> fileInfos); + void stop(); + void destroy(); + void onFileCreated(long inode, in byte[] metadata); } diff --git a/core/java/android/content/pm/IDataLoaderManager.aidl b/core/java/android/content/pm/IDataLoaderManager.aidl new file mode 100644 index 000000000000..f453c9b6c45f --- /dev/null +++ b/core/java/android/content/pm/IDataLoaderManager.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import android.os.Bundle; +import android.content.pm.IDataLoader; +import android.content.pm.IDataLoaderStatusListener; +import java.util.List; + +/** @hide */ +interface IDataLoaderManager { + boolean initializeDataLoader(int id, in Bundle params, IDataLoaderStatusListener listener); + IDataLoader getDataLoader(int dataLoaderId); + void destroyDataLoader(int dataLoaderId); +}
\ No newline at end of file diff --git a/core/java/android/service/incremental/IIncrementalDataLoaderStatusListener.aidl b/core/java/android/content/pm/IDataLoaderStatusListener.aidl index f04242dc6c02..a60d6ee2d28a 100644 --- a/core/java/android/service/incremental/IIncrementalDataLoaderStatusListener.aidl +++ b/core/java/android/content/pm/IDataLoaderStatusListener.aidl @@ -14,13 +14,13 @@ * limitations under the License. */ -package android.service.incremental; +package android.content.pm; /** - * Callbacks from DataLoaderService to IncrementalService to report data loader status. + * Callbacks from a data loader binder service to report data loader status. * @hide */ -oneway interface IIncrementalDataLoaderStatusListener { +oneway interface IDataLoaderStatusListener { /** Data loader status */ const int DATA_LOADER_READY = 0; const int DATA_LOADER_NOT_READY = 1; @@ -31,6 +31,6 @@ oneway interface IIncrementalDataLoaderStatusListener { const int DATA_LOADER_CONNECTION_OK = 6; /** Data loader status callback */ - void onStatusChanged(in int storageId, in int status); + void onStatusChanged(in int dataLoaderId, in int status); } diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl index b74061793f9b..0b3c7654f4fb 100644 --- a/core/java/android/content/pm/IPackageInstallerSession.aidl +++ b/core/java/android/content/pm/IPackageInstallerSession.aidl @@ -46,4 +46,5 @@ interface IPackageInstallerSession { int getParentSessionId(); boolean isStaged(); + void addFile(in String name, long size, in byte[] metadata); } diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index a71367d562f7..b3d8eb51e324 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -26,6 +26,7 @@ import android.content.pm.ChangedPackages; import android.content.pm.InstantAppInfo; import android.content.pm.FeatureInfo; import android.content.pm.IDexModuleRegisterCallback; +import android.content.pm.InstallSourceInfo; import android.content.pm.IPackageInstaller; import android.content.pm.IPackageDeleteObserver; import android.content.pm.IPackageDeleteObserver2; @@ -237,6 +238,8 @@ interface IPackageManager { @UnsupportedAppUsage String getInstallerPackageName(in String packageName); + InstallSourceInfo getInstallSourceInfo(in String packageName); + void resetApplicationPreferences(int userId); @UnsupportedAppUsage diff --git a/core/java/android/content/pm/InstallSourceInfo.aidl b/core/java/android/content/pm/InstallSourceInfo.aidl new file mode 100644 index 000000000000..21ee4c35b57c --- /dev/null +++ b/core/java/android/content/pm/InstallSourceInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +parcelable InstallSourceInfo; diff --git a/core/java/android/content/pm/InstallSourceInfo.java b/core/java/android/content/pm/InstallSourceInfo.java new file mode 100644 index 000000000000..4d235f1af2f7 --- /dev/null +++ b/core/java/android/content/pm/InstallSourceInfo.java @@ -0,0 +1,110 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Information about how an app was installed. + * @see PackageManager#getInstallSourceInfo(String) + */ +public final class InstallSourceInfo implements Parcelable { + + @Nullable private final String mInitiatingPackageName; + + @Nullable private final String mOriginatingPackageName; + + @Nullable private final String mInstallingPackageName; + + /** @hide */ + public InstallSourceInfo(@Nullable String initiatingPackageName, + @Nullable String originatingPackageName, @Nullable String installingPackageName) { + this.mInitiatingPackageName = initiatingPackageName; + this.mOriginatingPackageName = originatingPackageName; + this.mInstallingPackageName = installingPackageName; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(mInitiatingPackageName); + dest.writeString(mOriginatingPackageName); + dest.writeString(mInstallingPackageName); + } + + private InstallSourceInfo(Parcel source) { + mInitiatingPackageName = source.readString(); + mOriginatingPackageName = source.readString(); + mInstallingPackageName = source.readString(); + } + + /** The name of the package that requested the installation, or null if not available. */ + @Nullable + public String getInitiatingPackageName() { + return mInitiatingPackageName; + } + + /** + * The name of the package on behalf of which the initiating package requested the installation, + * or null if not available. + * <p> + * For example if a downloaded APK is installed via the Package Installer this could be the + * app that performed the download. This value is provided by the initiating package and not + * verified by the framework. + * <p> + * Note that the {@code InstallSourceInfo} returned by + * {@link PackageManager#getInstallSourceInfo(String)} will not have this information + * available unless the calling application holds the INSTALL_PACKAGES permission. + */ + @Nullable + public String getOriginatingPackageName() { + return mOriginatingPackageName; + } + + /** + * The name of the package responsible for the installation (the installer of record), or null + * if not available. + * Note that this may differ from the initiating package name and can be modified. + * + * @see PackageManager#setInstallerPackageName(String, String) + */ + @Nullable + public String getInstallingPackageName() { + return mInstallingPackageName; + } + + @NonNull + public static final Parcelable.Creator<InstallSourceInfo> CREATOR = + new Creator<InstallSourceInfo>() { + @Override + public InstallSourceInfo createFromParcel(Parcel source) { + return new InstallSourceInfo(source); + } + + @Override + public InstallSourceInfo[] newArray(int size) { + return new InstallSourceInfo[size]; + } + }; +} diff --git a/core/java/android/content/pm/InstallationFile.aidl b/core/java/android/content/pm/InstallationFile.aidl new file mode 100644 index 000000000000..1edff9d6c7aa --- /dev/null +++ b/core/java/android/content/pm/InstallationFile.aidl @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +/** + * Describes a file which is part of a package installation. + */ +parcelable InstallationFile; + diff --git a/core/java/android/content/pm/InstallationFile.java b/core/java/android/content/pm/InstallationFile.java new file mode 100644 index 000000000000..ac5fd1e41075 --- /dev/null +++ b/core/java/android/content/pm/InstallationFile.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Defines the properties of a file in an installation session. + * TODO(b/136132412): update with new APIs. + * + * @hide + */ +public final class InstallationFile implements Parcelable { + public static final int FILE_TYPE_UNKNOWN = -1; + public static final int FILE_TYPE_APK = 0; + public static final int FILE_TYPE_LIB = 1; + public static final int FILE_TYPE_OBB = 2; + + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"FILE_TYPE_"}, value = { + FILE_TYPE_APK, + FILE_TYPE_LIB, + FILE_TYPE_OBB, + }) + public @interface FileType { + } + + private String mFileName; + private @FileType int mFileType; + private long mFileSize; + private byte[] mMetadata; + + public InstallationFile(@NonNull String fileName, long fileSize, + @Nullable byte[] metadata) { + mFileName = fileName; + mFileSize = fileSize; + mMetadata = metadata; + if (fileName.toLowerCase().endsWith(".apk")) { + mFileType = FILE_TYPE_APK; + } else if (fileName.toLowerCase().endsWith(".obb")) { + mFileType = FILE_TYPE_OBB; + } else if (fileName.toLowerCase().endsWith(".so") && fileName.toLowerCase().startsWith( + "lib/")) { + mFileType = FILE_TYPE_LIB; + } else { + mFileType = FILE_TYPE_UNKNOWN; + } + } + + public @FileType int getFileType() { + return mFileType; + } + + public @NonNull String getName() { + return mFileName; + } + + public long getSize() { + return mFileSize; + } + + public @Nullable byte[] getMetadata() { + return mMetadata; + } + + private InstallationFile(Parcel source) { + mFileName = source.readString(); + mFileType = source.readInt(); + mFileSize = source.readLong(); + mMetadata = source.createByteArray(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(mFileName); + dest.writeInt(mFileType); + dest.writeLong(mFileSize); + dest.writeByteArray(mMetadata); + } + + public static final @NonNull Creator<InstallationFile> CREATOR = + new Creator<InstallationFile>() { + public InstallationFile createFromParcel(Parcel source) { + return new InstallationFile(source); + } + + public InstallationFile[] newArray(int size) { + return new InstallationFile[size]; + } + }; + +} diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 4ab6f3cd92b4..e9fc8f6acfc6 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -50,6 +50,8 @@ import android.os.ParcelableException; import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserHandle; +import android.os.incremental.IncrementalDataLoaderParams; +import android.os.incremental.IncrementalDataLoaderParamsParcel; import android.system.ErrnoException; import android.system.Os; import android.util.ArraySet; @@ -1212,6 +1214,27 @@ public class PackageInstaller { } /** + * Configure files for an installation session. + * + * Currently only for Incremental installation session. Once this method is called, + * the files and their paths, as specified in the parameters, will be created and properly + * configured in the Incremental File System. + * + * TODO(b/136132412): update this and InstallationFile class with latest API design. + * + * @throws IllegalStateException if {@link SessionParams#incrementalParams} is null. + * + * @hide + */ + public void addFile(@NonNull String name, long size, @NonNull byte[] metadata) { + try { + mSession.addFile(name, size, metadata); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Release this session object. You can open the session again if it * hasn't been finalized. */ @@ -1398,6 +1421,8 @@ public class PackageInstaller { public boolean isStaged; /** {@hide} */ public long requiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST; + /** {@hide} */ + public IncrementalDataLoaderParams incrementalParams; /** * Construct parameters for a new package install session. @@ -1431,6 +1456,12 @@ public class PackageInstaller { isMultiPackage = source.readBoolean(); isStaged = source.readBoolean(); requiredInstalledVersionCode = source.readLong(); + IncrementalDataLoaderParamsParcel dataLoaderParamsParcel = source.readParcelable( + IncrementalDataLoaderParamsParcel.class.getClassLoader()); + if (dataLoaderParamsParcel != null) { + incrementalParams = new IncrementalDataLoaderParams( + dataLoaderParamsParcel); + } } /** {@hide} */ @@ -1454,6 +1485,7 @@ public class PackageInstaller { ret.isMultiPackage = isMultiPackage; ret.isStaged = isStaged; ret.requiredInstalledVersionCode = requiredInstalledVersionCode; + ret.incrementalParams = incrementalParams; return ret; } @@ -1782,6 +1814,16 @@ public class PackageInstaller { return (installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0; } + /** + * Set Incremental data loader params. + * + * {@hide} + */ + @RequiresPermission(Manifest.permission.INSTALL_PACKAGES) + public void setIncrementalParams(@NonNull IncrementalDataLoaderParams incrementalParams) { + this.incrementalParams = incrementalParams; + } + /** {@hide} */ public void dump(IndentingPrintWriter pw) { pw.printPair("mode", mode); @@ -1831,6 +1873,11 @@ public class PackageInstaller { dest.writeBoolean(isMultiPackage); dest.writeBoolean(isStaged); dest.writeLong(requiredInstalledVersionCode); + if (incrementalParams != null) { + dest.writeParcelable(incrementalParams.getData(), flags); + } else { + dest.writeParcelable(null, flags); + } } public static final Parcelable.Creator<SessionParams> diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index bbfdf910a9da..6e890ba0d827 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -5989,11 +5989,30 @@ public abstract class PackageManager { * * @param packageName The name of the package to query * @throws IllegalArgumentException if the given package name is not installed + * + * @deprecated use {@link #getInstallSourceInfo(String)} instead */ + @Deprecated @Nullable public abstract String getInstallerPackageName(@NonNull String packageName); /** + * Retrieves information about how a package was installed or updated. + * <p> + * If the calling application does not hold the INSTALL_PACKAGES permission then + * the result will always return {@code null} from + * {@link InstallSourceInfo#getOriginatingPackageName()}. + * + * @param packageName The name of the package to query + * @throws NameNotFoundException if the given package name is not installed + */ + @NonNull + public InstallSourceInfo getInstallSourceInfo(@NonNull String packageName) + throws NameNotFoundException { + throw new UnsupportedOperationException("getInstallSourceInfo not implemented"); + } + + /** * Attempts to clear the user data directory of an application. * Since this may take a little while, the result will * be posted back to the given observer. A deletion will fail if the diff --git a/core/java/android/os/incremental/IIncrementalServiceProxy.aidl b/core/java/android/os/incremental/IIncrementalServiceProxy.aidl index 12740eaf3425..ffff52e5aac9 100644 --- a/core/java/android/os/incremental/IIncrementalServiceProxy.aidl +++ b/core/java/android/os/incremental/IIncrementalServiceProxy.aidl @@ -18,7 +18,7 @@ package android.os.incremental; import android.os.incremental.IncrementalFileSystemControlParcel; import android.os.incremental.IncrementalDataLoaderParamsParcel; -import android.service.incremental.IIncrementalDataLoaderStatusListener; +import android.content.pm.IDataLoaderStatusListener; /** * Binder service to receive calls from native Incremental Service and handle Java tasks such as @@ -29,7 +29,7 @@ interface IIncrementalServiceProxy { boolean prepareDataLoader(int mountId, in IncrementalFileSystemControlParcel control, in IncrementalDataLoaderParamsParcel params, - in IIncrementalDataLoaderStatusListener listener); + in IDataLoaderStatusListener listener); boolean startDataLoader(int mountId); void showHealthBlockedUI(int mountId); void destroyDataLoader(int mountId); diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java new file mode 100644 index 000000000000..5bd0748b8d97 --- /dev/null +++ b/core/java/android/os/incremental/IncrementalFileStorages.java @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os.incremental; + +/** + * Set up files and directories used in an installation session. + * Currently only used by Incremental Installation. + * For Incremental installation, the expected outcome of this function is: + * 0) All the files are in defaultStorage + * 1) All APK files are in the same directory, bound to mApkStorage, and bound to the + * InstallerSession's stage dir. The files are linked from mApkStorage to defaultStorage. + * 2) All lib files are in the sub directories as their names suggest, and in the same parent + * directory as the APK files. The files are linked from mApkStorage to defaultStorage. + * 3) OBB files are in another directory that is different from APK files and lib files, bound + * to mObbStorage. The files are linked from mObbStorage to defaultStorage. + * + * @throws IllegalStateException the session is not an Incremental installation session. + */ + +import static dalvik.system.VMRuntime.getInstructionSet; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.pm.InstallationFile; +import android.os.IVold; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.ArraySet; +import android.util.Slog; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Random; + +/** + * This class manages storage instances used during a package installation session. + * @hide + */ +public final class IncrementalFileStorages { + private static final String TAG = "IncrementalFileStorages"; + private @Nullable IncrementalStorage mDefaultStorage; + private @Nullable IncrementalStorage mApkStorage; + private @Nullable IncrementalStorage mObbStorage; + private @Nullable String mDefaultDir; + private @Nullable String mObbDir; + private @NonNull IncrementalManager mIncrementalManager; + private @Nullable ArraySet<String> mLibDirs; + private @NonNull String mPackageName; + private @NonNull File mStageDir; + + /** + * Set up files and directories used in an installation session. + * Currently only used by Incremental Installation. + * For Incremental installation, the expected outcome of this function is: + * 0) All the files are in defaultStorage + * 1) All APK files are in the same directory, bound to mApkStorage, and bound to the + * InstallerSession's stage dir. The files are linked from mApkStorage to defaultStorage. + * 2) All lib files are in the sub directories as their names suggest, and in the same parent + * directory as the APK files. The files are linked from mApkStorage to defaultStorage. + * 3) OBB files are in another directory that is different from APK files and lib files, bound + * to mObbStorage. The files are linked from mObbStorage to defaultStorage. + * + * @throws IllegalStateException the session is not an Incremental installation session. + */ + public IncrementalFileStorages(@NonNull String packageName, + @NonNull File stageDir, + @NonNull IncrementalManager incrementalManager, + @NonNull IncrementalDataLoaderParams incrementalDataLoaderParams) { + mPackageName = packageName; + mStageDir = stageDir; + mIncrementalManager = incrementalManager; + if (incrementalDataLoaderParams.getPackageName().equals("local")) { + final String incrementalPath = incrementalDataLoaderParams.getStaticArgs(); + mDefaultStorage = mIncrementalManager.openStorage(incrementalPath); + mDefaultDir = incrementalPath; + return; + } + mDefaultDir = getTempDir(); + if (mDefaultDir == null) { + return; + } + mDefaultStorage = mIncrementalManager.createStorage(mDefaultDir, + incrementalDataLoaderParams, + IncrementalManager.CREATE_MODE_CREATE + | IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false); + } + + /** + * Adds a file into the installation session. Makes sure it will be placed inside + * a proper storage instance, based on its file type. + */ + public void addFile(@NonNull InstallationFile file) throws IOException { + if (mDefaultStorage == null) { + throw new IOException("Cannot add file because default storage does not exist"); + } + if (file.getFileType() == InstallationFile.FILE_TYPE_APK) { + addApkFile(file); + } else if (file.getFileType() == InstallationFile.FILE_TYPE_OBB) { + addObbFile(file); + } else if (file.getFileType() == InstallationFile.FILE_TYPE_LIB) { + addLibFile(file); + } else { + throw new IOException("Unknown file type: " + file.getFileType()); + } + } + + private void addApkFile(@NonNull InstallationFile apk) throws IOException { + // Create a storage for APK files and lib files + final String stageDirPath = mStageDir.getAbsolutePath(); + if (mApkStorage == null) { + mApkStorage = mIncrementalManager.createStorage(stageDirPath, mDefaultStorage, + IncrementalManager.CREATE_MODE_CREATE + | IncrementalManager.CREATE_MODE_TEMPORARY_BIND); + mApkStorage.bind(stageDirPath); + } + + if (!new File(mDefaultDir, apk.getName()).exists()) { + mDefaultStorage.makeFile(apk.getName(), apk.getSize(), + apk.getMetadata()); + } + // Assuming APK files are already named properly, e.g., "base.apk" + mDefaultStorage.makeLink(apk.getName(), mApkStorage, apk.getName()); + } + + private void addLibFile(@NonNull InstallationFile lib) throws IOException { + // TODO(b/136132412): remove this after we have incfs support for lib file mapping + if (mApkStorage == null) { + throw new IOException("Cannot add lib file without adding an apk file first"); + } + if (mLibDirs == null) { + mLibDirs = new ArraySet<>(); + } + String current = ""; + final Path libDirPath = Paths.get(lib.getName()).getParent(); + final int numDirComponents = libDirPath.getNameCount(); + for (int i = 0; i < numDirComponents; i++) { + String dirName = libDirPath.getName(i).toString(); + try { + dirName = getInstructionSet(dirName); + } catch (IllegalArgumentException ignored) { + } + current += dirName; + if (!mLibDirs.contains(current)) { + mDefaultStorage.makeDirectory(current); + mApkStorage.makeDirectory(current); + mLibDirs.add(current); + } + current += '/'; + } + String libFilePath = current + Paths.get(lib.getName()).getFileName(); + mDefaultStorage.makeFile(libFilePath, lib.getSize(), lib.getMetadata()); + mDefaultStorage.makeLink(libFilePath, mApkStorage, libFilePath); + } + + private void addObbFile(@NonNull InstallationFile obb) throws IOException { + if (mObbStorage == null) { + // Create a storage for OBB files + mObbDir = getTempDir(); + if (mObbDir == null) { + throw new IOException("Failed to create obb storage directory."); + } + mObbStorage = mIncrementalManager.createStorage( + mObbDir, mDefaultStorage, + IncrementalManager.CREATE_MODE_CREATE + | IncrementalManager.CREATE_MODE_TEMPORARY_BIND); + } + mDefaultStorage.makeFile(obb.getName(), obb.getSize(), obb.getMetadata()); + mDefaultStorage.makeLink(obb.getName(), mObbStorage, obb.getName()); + } + + private boolean hasObb() { + return (mObbStorage != null && mObbDir != null); + } + + /** + * Starts loading data for default storage. + * TODO(b/136132412): update the implementation with latest API design. + */ + public boolean startLoading() { + if (mDefaultStorage == null) { + return false; + } + return mDefaultStorage.startLoading(); + } + + /** + * Sets up obb storage directory and create bindings. + */ + public void finishSetUp() { + if (!hasObb()) { + return; + } + final String mainObbDir = String.format("/storage/emulated/0/Android/obb/%s", mPackageName); + final String packageObbDirRoot = + String.format("/mnt/runtime/%s/emulated/0/Android/obb/", mPackageName); + final String[] obbDirs = { + packageObbDirRoot + "read", + packageObbDirRoot + "write", + packageObbDirRoot + "full", + packageObbDirRoot + "default", + String.format("/data/media/0/Android/obb/%s", mPackageName), + mainObbDir, + }; + try { + Slog.i(TAG, "Creating obb directory '" + mainObbDir + "'"); + final IVold vold = IVold.Stub.asInterface(ServiceManager.getServiceOrThrow("vold")); + vold.mkdirs(mainObbDir); + for (String d : obbDirs) { + mObbStorage.bindPermanent(d); + } + } catch (ServiceManager.ServiceNotFoundException ex) { + Slog.e(TAG, "vold service is not found."); + cleanUp(); + } catch (IOException | RemoteException ex) { + Slog.e(TAG, "Failed to create obb dir at: " + mainObbDir, ex); + cleanUp(); + } + } + + /** + * Resets the states and unbinds storage instances for an installation session. + * TODO(b/136132412): make sure unnecessary binds are removed but useful storages are kept + */ + public void cleanUp() { + if (mDefaultStorage != null && mDefaultDir != null) { + try { + mDefaultStorage.unBind(mDefaultDir); + } catch (IOException ignored) { + } + mDefaultDir = null; + mDefaultStorage = null; + } + if (mApkStorage != null && mStageDir != null) { + try { + mApkStorage.unBind(mStageDir.getAbsolutePath()); + } catch (IOException ignored) { + } + mApkStorage = null; + } + if (mObbStorage != null && mObbDir != null) { + try { + mObbStorage.unBind(mObbDir); + } catch (IOException ignored) { + } + mObbDir = null; + mObbStorage = null; + } + } + + private String getTempDir() { + final String tmpDirRoot = "/data/tmp"; + final Random random = new Random(); + final Path tmpDir = + Paths.get(tmpDirRoot, String.valueOf(random.nextInt(Integer.MAX_VALUE - 1))); + try { + Files.createDirectories(tmpDir); + } catch (Exception ex) { + Slog.e(TAG, "Failed to create dir", ex); + return null; + } + return tmpDir.toAbsolutePath().toString(); + } +} diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index 6c4ee016033d..09286feee6b9 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -17,6 +17,7 @@ package android.permission; import android.Manifest; +import android.annotation.CallbackExecutor; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -29,6 +30,8 @@ import android.content.Context; import android.content.pm.IPackageManager; import android.content.pm.permission.SplitPermissionInfoParcelable; import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; import android.util.Slog; import com.android.internal.annotations.Immutable; @@ -36,6 +39,8 @@ import com.android.internal.annotations.Immutable; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.Executor; +import java.util.function.Consumer; /** * System level service for accessing the permission capabilities of the platform. @@ -59,6 +64,8 @@ public final class PermissionManager { private final IPackageManager mPackageManager; + private final IPermissionManager mPermissionManager; + private List<SplitPermissionInfo> mSplitPermissionInfos; /** @@ -67,9 +74,12 @@ public final class PermissionManager { * @param context The current context in which to operate. * @hide */ - public PermissionManager(@NonNull Context context, IPackageManager packageManager) { + public PermissionManager(@NonNull Context context, IPackageManager packageManager) + throws ServiceManager.ServiceNotFoundException { mContext = context; mPackageManager = packageManager; + mPermissionManager = IPermissionManager.Stub.asInterface( + ServiceManager.getServiceOrThrow("permissionmgr")); } /** @@ -145,6 +155,48 @@ public final class PermissionManager { return mSplitPermissionInfos; } + /** + * Grant default permissions to currently active LUI app + * @param packageName The package name for the LUI app + * @param user The user handle + * @param callback The callback provided by caller to be notified when grant completes + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) + public void grantDefaultPermissionsToLuiApp( + @NonNull String packageName, @NonNull UserHandle user, + @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { + try { + mPermissionManager.grantDefaultPermissionsToActiveLuiApp( + packageName, user.getIdentifier()); + executor.execute(() -> callback.accept(true)); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * Revoke default permissions to currently active LUI app + * @param packageNames The package names for the LUI apps + * @param user The user handle + * @param callback The callback provided by caller to be notified when grant completes + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) + public void revokeDefaultPermissionsFromLuiApps( + @NonNull String[] packageNames, @NonNull UserHandle user, + @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { + try { + mPermissionManager.revokeDefaultPermissionsFromLuiApps( + packageNames, user.getIdentifier()); + executor.execute(() -> callback.accept(true)); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + private List<SplitPermissionInfo> splitPermissionInfoListToNonParcelableList( List<SplitPermissionInfoParcelable> parcelableList) { final int size = parcelableList.size(); diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 01f9c7300fa1..2c53025da350 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -20,6 +20,7 @@ import android.annotation.BytesLong; import android.annotation.CurrentTimeMillisLong; import android.annotation.CurrentTimeSecondsLong; import android.annotation.DurationMillisLong; +import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -78,6 +79,8 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.text.Collator; import java.util.ArrayList; import java.util.Collection; @@ -205,8 +208,10 @@ public final class MediaStore { public static final String PARAM_DELETE_DATA = "deletedata"; /** {@hide} */ + @Deprecated public static final String PARAM_INCLUDE_PENDING = "includePending"; /** {@hide} */ + @Deprecated public static final String PARAM_INCLUDE_TRASHED = "includeTrashed"; /** {@hide} */ public static final String PARAM_PROGRESS = "progress"; @@ -559,6 +564,101 @@ public final class MediaStore { public static final String UNKNOWN_STRING = "<unknown>"; /** + * Specify a {@link Uri} that is "related" to the current operation being + * performed. + * <p> + * This is typically used to allow an operation that may normally be + * rejected, such as making a copy of a pre-existing image located under a + * {@link MediaColumns#RELATIVE_PATH} where new images are not allowed. + * <p> + * It's strongly recommended that when making a copy of pre-existing content + * that you define the "original document ID" GUID as defined by the <em>XMP + * Media Management</em> standard. + * <p> + * This key can be placed in a {@link Bundle} of extras and passed to + * {@link ContentResolver#insert}. + */ + public static final String QUERY_ARG_RELATED_URI = "android:query-arg-related-uri"; + + /** + * Specify how {@link MediaColumns#IS_PENDING} items should be filtered when + * performing a {@link MediaStore} operation. + * <p> + * This key can be placed in a {@link Bundle} of extras and passed to + * {@link ContentResolver#query}, {@link ContentResolver#update}, or + * {@link ContentResolver#delete}. + * <p> + * By default, pending items are filtered away from operations. + */ + @Match + public static final String QUERY_ARG_MATCH_PENDING = "android:query-arg-match-pending"; + + /** + * Specify how {@link MediaColumns#IS_TRASHED} items should be filtered when + * performing a {@link MediaStore} operation. + * <p> + * This key can be placed in a {@link Bundle} of extras and passed to + * {@link ContentResolver#query}, {@link ContentResolver#update}, or + * {@link ContentResolver#delete}. + * <p> + * By default, trashed items are filtered away from operations. + */ + @Match + public static final String QUERY_ARG_MATCH_TRASHED = "android:query-arg-match-trashed"; + + /** + * Specify how {@link MediaColumns#IS_FAVORITE} items should be filtered + * when performing a {@link MediaStore} operation. + * <p> + * This key can be placed in a {@link Bundle} of extras and passed to + * {@link ContentResolver#query}, {@link ContentResolver#update}, or + * {@link ContentResolver#delete}. + * <p> + * By default, favorite items are <em>not</em> filtered away from + * operations. + */ + @Match + public static final String QUERY_ARG_MATCH_FAVORITE = "android:query-arg-match-favorite"; + + /** @hide */ + @IntDef(flag = true, prefix = { "MATCH_" }, value = { + MATCH_DEFAULT, + MATCH_INCLUDE, + MATCH_EXCLUDE, + MATCH_ONLY, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Match {} + + /** + * Value indicating that the default matching behavior should be used, as + * defined by the key documentation. + */ + public static final int MATCH_DEFAULT = 0; + + /** + * Value indicating that operations should include items matching the + * criteria defined by this key. + * <p> + * Note that items <em>not</em> matching the criteria <em>may</em> also be + * included depending on the default behavior documented by the key. If you + * want to operate exclusively on matching items, use {@link #MATCH_ONLY}. + */ + public static final int MATCH_INCLUDE = 1; + + /** + * Value indicating that operations should exclude items matching the + * criteria defined by this key. + */ + public static final int MATCH_EXCLUDE = 2; + + /** + * Value indicating that operations should only operate on items explicitly + * matching the criteria defined by this key. + */ + public static final int MATCH_ONLY = 3; + + /** * Update the given {@link Uri} to also include any pending media items from * calls such as * {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}. @@ -566,12 +666,16 @@ public final class MediaStore { * * @see MediaColumns#IS_PENDING * @see MediaStore#getIncludePending(Uri) + * @deprecated consider migrating to {@link #QUERY_ARG_MATCH_PENDING} which + * is more expressive. */ + @Deprecated public static @NonNull Uri setIncludePending(@NonNull Uri uri) { return setIncludePending(uri.buildUpon()).build(); } /** @hide */ + @Deprecated public static @NonNull Uri.Builder setIncludePending(@NonNull Uri.Builder uriBuilder) { return uriBuilder.appendQueryParameter(PARAM_INCLUDE_PENDING, "1"); } @@ -582,7 +686,11 @@ public final class MediaStore { * * @see MediaColumns#IS_PENDING * @see MediaStore#setIncludePending(Uri) + * @deprecated consider migrating to {@link #QUERY_ARG_MATCH_PENDING} which + * is more expressive. + * @removed */ + @Deprecated public static boolean getIncludePending(@NonNull Uri uri) { return parseBoolean(uri.getQueryParameter(MediaStore.PARAM_INCLUDE_PENDING)); } @@ -597,7 +705,11 @@ public final class MediaStore { * @see MediaStore#setIncludeTrashed(Uri) * @see MediaStore#trash(Context, Uri) * @see MediaStore#untrash(Context, Uri) + * @deprecated consider migrating to {@link #QUERY_ARG_MATCH_TRASHED} which + * is more expressive. + * @removed */ + @Deprecated public static @NonNull Uri setIncludeTrashed(@NonNull Uri uri) { return uri.buildUpon().appendQueryParameter(PARAM_INCLUDE_TRASHED, "1").build(); } @@ -1000,7 +1112,7 @@ public final class MediaStore { * the field to {@code 0}, or until they expire as defined by * {@link #DATE_EXPIRES}. * - * @see MediaStore#setIncludePending(Uri) + * @see MediaStore#QUERY_ARG_MATCH_PENDING */ @Column(Cursor.FIELD_TYPE_INTEGER) public static final String IS_PENDING = "is_pending"; @@ -1011,8 +1123,7 @@ public final class MediaStore { * Trashed items are retained until they expire as defined by * {@link #DATE_EXPIRES}. * - * @see MediaColumns#IS_TRASHED - * @see MediaStore#setIncludeTrashed(Uri) + * @see MediaStore#QUERY_ARG_MATCH_TRASHED * @see MediaStore#trash(Context, Uri) * @see MediaStore#untrash(Context, Uri) */ @@ -1186,6 +1297,8 @@ public final class MediaStore { /** * Flag indicating if the media item has been marked as being a * "favorite" by the user. + * + * @see MediaStore#QUERY_ARG_MATCH_FAVORITE */ @Column(Cursor.FIELD_TYPE_INTEGER) public static final String IS_FAVORITE = "is_favorite"; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index fa8896ebea80..ad8d55313795 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -8782,6 +8782,22 @@ public final class Settings { public static final String BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu"; /** + * The package name for the custom bugreport handler app. This app must be whitelisted. + * This is currently used only by Power Menu short press. + * + * @hide + */ + public static final String CUSTOM_BUGREPORT_HANDLER_APP = "custom_bugreport_handler_app"; + + /** + * The user id for the custom bugreport handler app. This is currently used only by Power + * Menu short press. + * + * @hide + */ + public static final String CUSTOM_BUGREPORT_HANDLER_USER = "custom_bugreport_handler_user"; + + /** * Whether ADB is enabled. */ public static final String ADB_ENABLED = "adb_enabled"; @@ -8918,14 +8934,33 @@ public final class Settings { * List of ISO country codes in which eUICC UI is shown. Country codes should be separated * by comma. * - * <p>Used to hide eUICC UI from users who are currently in countries no carriers support - * eUICC. + * Note: if {@link #EUICC_SUPPORTED_COUNTRIES} is empty, then {@link + * #EUICC_UNSUPPORTED_COUNTRIES} is used. + * + * <p>Used to hide eUICC UI from users who are currently in countries where no carriers + * support eUICC. + * * @hide */ - //TODO(b/77914569) Changes this to System Api. + @SystemApi public static final String EUICC_SUPPORTED_COUNTRIES = "euicc_supported_countries"; /** + * List of ISO country codes in which eUICC UI is not shown. Country codes should be + * separated by comma. + * + * Note: if {@link #EUICC_SUPPORTED_COUNTRIES} is empty, then {@link + * #EUICC_UNSUPPORTED_COUNTRIES} is used. + * + * <p>Used to hide eUICC UI from users who are currently in countries where no carriers + * support eUICC. + * + * @hide + */ + @SystemApi + public static final String EUICC_UNSUPPORTED_COUNTRIES = "euicc_unsupported_countries"; + + /** * Whether any activity can be resized. When this is true, any * activity, regardless of manifest values, can be resized for multi-window. * (0 = false, 1 = true) diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 0e5200983bc0..22f90f62b114 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -44,7 +44,6 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Patterns; -import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.SmsApplication; import java.lang.annotation.Retention; @@ -1379,7 +1378,7 @@ public final class Telephony { } String format = intent.getStringExtra("format"); - int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, + int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, SubscriptionManager.getDefaultSmsSubscriptionId()); Rlog.v(TAG, " getMessagesFromIntent sub_id : " + subId); diff --git a/core/java/android/service/controls/BooleanAction.aidl b/core/java/android/service/controls/BooleanAction.aidl new file mode 100644 index 000000000000..730ad36749f7 --- /dev/null +++ b/core/java/android/service/controls/BooleanAction.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +parcelable BooleanAction;
\ No newline at end of file diff --git a/core/java/android/service/controls/BooleanAction.java b/core/java/android/service/controls/BooleanAction.java new file mode 100644 index 000000000000..8508c635142f --- /dev/null +++ b/core/java/android/service/controls/BooleanAction.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; + +/** + * Action sent by a {@link ToggleTemplate} + * @hide + */ +public final class BooleanAction extends ControlAction { + + private final boolean mNewState; + + /** + * @param templateId the identifier of the {@link ToggleTemplate} that produced this action. + * @param newState new value for the state displayed by the {@link ToggleTemplate}. + */ + public BooleanAction(@NonNull String templateId, boolean newState) { + this(templateId, newState, null); + } + + /** + * @param templateId the identifier of the {@link ToggleTemplate} that originated this action. + * @param newValue new value for the state displayed by the {@link ToggleTemplate}. + * @param challengeValue a value sent by the user along with the action to authenticate. {@code} + * null is sent when no authentication is needed or has not been + * requested. + */ + public BooleanAction(@NonNull String templateId, boolean newValue, + @Nullable String challengeValue) { + super(templateId, challengeValue); + mNewState = newValue; + } + + BooleanAction(Parcel in) { + super(in); + mNewState = in.readByte() == 1; + } + + /** + * The new state set for the button in the corresponding {@link ToggleTemplate}. + * + * @return {@code true} if the button was toggled from an {@code off} state to an {@code on} + * state. + */ + public boolean getNewState() { + return mNewState; + } + + /** + * @return {@link ControlAction#TYPE_BOOLEAN} + */ + @Override + public int getActionType() { + return ControlAction.TYPE_BOOLEAN; + } + + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeByte(mNewState ? (byte) 1 : (byte) 0); + } + + public static final @NonNull Creator<BooleanAction> CREATOR = new Creator<BooleanAction>() { + @Override + public BooleanAction createFromParcel(Parcel source) { + return new BooleanAction(source); + } + + @Override + public BooleanAction[] newArray(int size) { + return new BooleanAction[size]; + } + }; +} diff --git a/core/java/android/service/controls/Control.aidl b/core/java/android/service/controls/Control.aidl new file mode 100644 index 000000000000..f4964f2e15d7 --- /dev/null +++ b/core/java/android/service/controls/Control.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +parcelable Control;
\ No newline at end of file diff --git a/core/java/android/service/controls/Control.java b/core/java/android/service/controls/Control.java new file mode 100644 index 000000000000..a69408c43df3 --- /dev/null +++ b/core/java/android/service/controls/Control.java @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Intent; +import android.content.res.ColorStateList; +import android.graphics.drawable.Icon; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + +/** + * Represents a physical object that can be represented by a {@link ControlTemplate} and whose + * properties may be modified through a {@link ControlAction}. + * + * The information is provided by a {@link ControlProviderService} and represents static + * information (not current status) about the device. + * <p> + * Each control needs a unique (per provider) identifier that is persistent across reboots of the + * system. + * <p> + * Each {@link Control} will have a name and an icon. The name is usually set up by the user in the + * {@link ControlProvider} while the icon is usually decided by the {@link ControlProvider} based + * on the type of device. + * <p> + * The {@link ControlTemplate.TemplateType} provided will be used as a hint when displaying this in + * non-interactive situations (for example when there's no state to display). This template is not + * the one that will be shown with the current state and provide interactions. That template is set + * using {@link ControlState}. + * <p> + * An {@link Intent} linking to the provider Activity that expands this {@link Control} should be + * provided. + * @hide + */ +public class Control implements Parcelable { + + private final @NonNull String mControlId; + private final @NonNull Icon mIcon; + private final @NonNull CharSequence mTitle; + private final @Nullable ColorStateList mTintColor; + private final @NonNull Intent mAppIntent; + private final @ControlTemplate.TemplateType int mPrimaryType; + + /** + * @param controlId the unique persistent identifier for this object. + * @param icon an icon to display identifying the control. + * @param title the user facing name of this control (e.g. "Bedroom thermostat"). + * @param tintColor the color to tint parts of the element UI. If {@code null} is passed, the + * system accent color will be used. + * @param appIntent an intent linking to a page to interact with the corresponding device. + * @param primaryType the primary template for this type. + */ + public Control(@NonNull String controlId, + @NonNull Icon icon, + @NonNull CharSequence title, + @Nullable ColorStateList tintColor, + @NonNull Intent appIntent, + int primaryType) { + Preconditions.checkNotNull(controlId); + Preconditions.checkNotNull(icon); + Preconditions.checkNotNull(title); + Preconditions.checkNotNull(appIntent); + mControlId = controlId; + mIcon = icon; + mTitle = title; + mTintColor = tintColor; + mAppIntent = appIntent; + mPrimaryType = primaryType; + } + + public Control(Parcel in) { + mControlId = in.readString(); + mIcon = Icon.CREATOR.createFromParcel(in); + mTitle = in.readCharSequence(); + if (in.readByte() == 1) { + mTintColor = ColorStateList.CREATOR.createFromParcel(in); + } else { + mTintColor = null; + } + mAppIntent = Intent.CREATOR.createFromParcel(in); + mPrimaryType = in.readInt(); + } + + @NonNull + public String getControlId() { + return mControlId; + } + + @NonNull + public Icon getIcon() { + return mIcon; + } + + @NonNull + public CharSequence getTitle() { + return mTitle; + } + + @Nullable + public ColorStateList getTint() { + return mTintColor; + } + + @NonNull + public Intent getAppIntent() { + return mAppIntent; + } + + @ControlTemplate.TemplateType + public int getPrimaryType() { + return mPrimaryType; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mControlId); + mIcon.writeToParcel(dest, flags); + dest.writeCharSequence(mTitle); + if (mTintColor != null) { + dest.writeByte((byte) 1); + mTintColor.writeToParcel(dest, flags); + } else { + dest.writeByte((byte) 0); + } + mAppIntent.writeToParcel(dest, flags); + dest.writeInt(mPrimaryType); + } + + public static final Creator<Control> CREATOR = new Creator<Control>() { + @Override + public Control createFromParcel(Parcel source) { + return new Control(source); + } + + @Override + public Control[] newArray(int size) { + return new Control[size]; + } + }; + + /** + * Builder class for {@link Control}. + * + * This class facilitates the creation of {@link Control}. It provides the following + * defaults for non-optional parameters: + * <ul> + * <li> Title: {@code ""} + * <li> Primary template: {@link ControlTemplate#TYPE_NONE} + * </ul> + */ + public static class Builder { + private String mControlId; + private Icon mIcon; + private CharSequence mTitle = ""; + private ColorStateList mTintColor; + private @Nullable Intent mAppIntent; + private @ControlTemplate.TemplateType int mPrimaryType = ControlTemplate.TYPE_NONE; + + /** + * @param controlId the identifier for the {@link Control}. + * @param icon the icon for the {@link Control}. + * @param appIntent the intent linking to the device Activity. + */ + public Builder(@NonNull String controlId, + @NonNull Icon icon, + @NonNull Intent appIntent) { + Preconditions.checkNotNull(controlId); + Preconditions.checkNotNull(icon); + Preconditions.checkNotNull(appIntent); + mControlId = controlId; + mIcon = icon; + mAppIntent = appIntent; + } + + /** + * Creates a {@link Builder} using an existing {@link Control} as a base. + * @param control base for the builder. + */ + public Builder(@NonNull Control control) { + Preconditions.checkNotNull(control); + mControlId = control.mControlId; + mIcon = control.mIcon; + mTitle = control.mTitle; + mTintColor = control.mTintColor; + mAppIntent = control.mAppIntent; + mPrimaryType = control.mPrimaryType; + } + + /** + * @param controlId the identifier for the {@link Control}. + * @return {@code this} + */ + public Builder setControlId(@NonNull String controlId) { + Preconditions.checkNotNull(controlId); + mControlId = controlId; + return this; + } + + /** + * @param icon the icon for the {@link Control} + * @return {@code this} + */ + @NonNull + public Builder setIcon(@NonNull Icon icon) { + Preconditions.checkNotNull(icon); + mIcon = icon; + return this; + } + + /** + * @param title the user facing name of the {@link Control} + * @return {@code this} + */ + @NonNull + public Builder setTitle(@NonNull CharSequence title) { + Preconditions.checkNotNull(title); + mTitle = title; + return this; + } + + /** + * @param tint colors for tinting parts of the {@link Control} UI. Passing {@code null} will + * default to using the current color accent. + * @return {@code this} + */ + @NonNull + public Builder setTint(@Nullable ColorStateList tint) { + mTintColor = tint; + return this; + } + + /** + * @param appIntent an {@link Intent} linking to an Activity for the {@link Control} + * @return {@code this} + */ + @NonNull + public Builder setAppIntent(@NonNull Intent appIntent) { + Preconditions.checkNotNull(appIntent); + mAppIntent = appIntent; + return this; + } + + /** + * @param type type to use as default in the {@link Control} + * @return {@code this} + */ + @NonNull + public Builder setPrimaryType(@ControlTemplate.TemplateType int type) { + mPrimaryType = type; + return this; + } + + /** + * Build a {@link Control} + * @return a valid {@link Control} + */ + @NonNull + public Control build() { + return new Control(mControlId, mIcon, mTitle, mTintColor, mAppIntent, mPrimaryType); + } + } +} diff --git a/core/java/android/service/controls/ControlAction.aidl b/core/java/android/service/controls/ControlAction.aidl new file mode 100644 index 000000000000..e1a5276b70d6 --- /dev/null +++ b/core/java/android/service/controls/ControlAction.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +parcelable ControlAction;
\ No newline at end of file diff --git a/core/java/android/service/controls/ControlAction.java b/core/java/android/service/controls/ControlAction.java new file mode 100644 index 000000000000..8b759556b597 --- /dev/null +++ b/core/java/android/service/controls/ControlAction.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * An abstract action that is executed from a {@link ControlTemplate}. + * + * The action may have a value to authenticate the input, when the provider has requested it to + * complete the action. + * @hide + */ +public abstract class ControlAction implements Parcelable { + + /** + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + TYPE_BOOLEAN, + TYPE_FLOAT + }) + public @interface ActionType {}; + + /** + * The identifier of {@link BooleanAction}. + */ + public static final @ActionType int TYPE_BOOLEAN = 0; + + /** + * The identifier of {@link FloatAction}. + */ + public static final @ActionType int TYPE_FLOAT = 1; + + /** + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + RESPONSE_OK, + RESPONSE_FAIL, + RESPONSE_CHALLENGE_ACK, + RESPONSE_CHALLENGE_PIN, + RESPONSE_CHALLENGE_PASSPHRASE + }) + public @interface ResponseResult {}; + + /** + * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that + * the action has been performed. The action may still fail later and the state may not change. + */ + public static final @ResponseResult int RESPONSE_OK = 0; + /** + * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that + * the action has failed. + */ + public static final @ResponseResult int RESPONSE_FAIL = 1; + /** + * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that + * in order for the action to be performed, acknowledgment from the user is required. + */ + public static final @ResponseResult int RESPONSE_CHALLENGE_ACK = 2; + /** + * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that + * in order for the action to be performed, a PIN is required. + */ + public static final @ResponseResult int RESPONSE_CHALLENGE_PIN = 3; + /** + * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that + * in order for the action to be performed, an alphanumeric passphrase is required. + */ + public static final @ResponseResult int RESPONSE_CHALLENGE_PASSPHRASE = 4; + + /** + * The {@link ActionType} associated with this class. + */ + public abstract @ActionType int getActionType(); + + private final @NonNull String mTemplateId; + private final @Nullable String mChallengeValue; + + private ControlAction() { + mTemplateId = ""; + mChallengeValue = null; + } + + /** + * @hide + */ + ControlAction(@NonNull String templateId, @Nullable String challengeValue) { + Preconditions.checkNotNull(templateId); + mTemplateId = templateId; + mChallengeValue = challengeValue; + } + + /** + * @hide + */ + ControlAction(Parcel in) { + mTemplateId = in.readString(); + if (in.readByte() == 1) { + mChallengeValue = in.readString(); + } else { + mChallengeValue = null; + } + } + + /** + * The identifier of the {@link ControlTemplate} that originated this action + */ + @NonNull + public String getTemplateId() { + return mTemplateId; + } + + /** + * The challenge value used to authenticate certain actions, if available. + */ + @Nullable + public String getChallengeValue() { + return mChallengeValue; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(getActionType()); + dest.writeString(mTemplateId); + if (mChallengeValue != null) { + dest.writeByte((byte) 1); + dest.writeString(mChallengeValue); + } else { + dest.writeByte((byte) 0); + } + } + + public static final @NonNull Creator<ControlAction> CREATOR = new Creator<ControlAction>() { + @Override + public ControlAction createFromParcel(Parcel source) { + int type = source.readInt(); + return createActionFromType(type, source); + } + + @Override + public ControlAction[] newArray(int size) { + return new ControlAction[size]; + } + }; + + private static ControlAction createActionFromType(@ActionType int type, Parcel source) { + switch(type) { + case TYPE_BOOLEAN: + return BooleanAction.CREATOR.createFromParcel(source); + case TYPE_FLOAT: + return FloatAction.CREATOR.createFromParcel(source); + default: + return null; + } + } + +} diff --git a/core/java/android/service/controls/ControlButton.aidl b/core/java/android/service/controls/ControlButton.aidl new file mode 100644 index 000000000000..6a7262d9542e --- /dev/null +++ b/core/java/android/service/controls/ControlButton.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +parcelable ControlButton;
\ No newline at end of file diff --git a/core/java/android/service/controls/ControlButton.java b/core/java/android/service/controls/ControlButton.java new file mode 100644 index 000000000000..fed31158be35 --- /dev/null +++ b/core/java/android/service/controls/ControlButton.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +import android.annotation.NonNull; +import android.graphics.drawable.Icon; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + +/** + * Button element for {@link ControlTemplate}. + * @hide + */ +public class ControlButton implements Parcelable { + + private final boolean mActive; + private final @NonNull Icon mIcon; + private final @NonNull CharSequence mContentDescription; + + /** + * @param active true if the button should be rendered as active. + * @param icon icon to display in the button. + * @param contentDescription content description for the button. + */ + public ControlButton(boolean active, @NonNull Icon icon, + @NonNull CharSequence contentDescription) { + Preconditions.checkNotNull(icon); + Preconditions.checkNotNull(contentDescription); + mActive = active; + mIcon = icon; + mContentDescription = contentDescription; + } + + /** + * Whether the button should be rendered in its active state. + */ + public boolean isActive() { + return mActive; + } + + /** + * The icon for this button. + */ + @NonNull + public Icon getIcon() { + return mIcon; + } + + /** + * The content description for this button. + */ + @NonNull + public CharSequence getContentDescription() { + return mContentDescription; + } + + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeByte(mActive ? (byte) 1 : (byte) 0); + mIcon.writeToParcel(dest, flags); + dest.writeCharSequence(mContentDescription); + } + + ControlButton(Parcel in) { + mActive = in.readByte() != 0; + mIcon = Icon.CREATOR.createFromParcel(in); + mContentDescription = in.readCharSequence(); + } + + public static final Creator<ControlButton> CREATOR = new Creator<ControlButton>() { + @Override + public ControlButton createFromParcel(Parcel source) { + return new ControlButton(source); + } + + @Override + public ControlButton[] newArray(int size) { + return new ControlButton[size]; + } + }; +} diff --git a/core/java/android/service/controls/ControlState.aidl b/core/java/android/service/controls/ControlState.aidl new file mode 100644 index 000000000000..520d85b47d3e --- /dev/null +++ b/core/java/android/service/controls/ControlState.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +parcelable ControlState;
\ No newline at end of file diff --git a/core/java/android/service/controls/ControlState.java b/core/java/android/service/controls/ControlState.java new file mode 100644 index 000000000000..804aef798eb5 --- /dev/null +++ b/core/java/android/service/controls/ControlState.java @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.res.ColorStateList; +import android.graphics.drawable.Icon; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Current state for a {@link Control}. + * + * Collects information to render the current state of a {@link Control} as well as possible action + * that can be performed on it. Some of the information may temporarily override the defaults + * provided by the corresponding {@link Control}, while this state is being displayed. + * + * Additionally, this can be used to modify information related to the corresponding + * {@link Control}. + * @hide + */ +public final class ControlState implements Parcelable { + + /** + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + STATUS_OK, + STATUS_NOT_FOUND, + STATUS_ERROR, + STATUS_DISABLED, + }) + public @interface Status {}; + + /** + * The device corresponding to the {@link Control} is responding correctly. + */ + public static final int STATUS_OK = 0; + + /** + * The device corresponding to the {@link Control} cannot be found or was removed. + */ + public static final int STATUS_NOT_FOUND = 1; + + /** + * The device corresponding to the {@link Control} is in an error state. + */ + public static final int STATUS_ERROR = 2; + + /** + * The {@link Control} is currently disabled. + */ + public static final int STATUS_DISABLED = 3; + + private final @NonNull Control mControl; + private final @Status int mStatus; + private final @NonNull ControlTemplate mControlTemplate; + private final @NonNull CharSequence mStatusText; + private final @Nullable Icon mOverrideIcon; + private final @Nullable ColorStateList mOverrideTint; + + /** + * @param control the {@link Control} this state should be applied to. Can be used to + * update information about the {@link Control} + * @param status the current status of the {@link Control}. + * @param controlTemplate the template to be used to render the {@link Control}. + * @param statusText the text describing the current status. + * @param overrideIcon the icon to temporarily override the one provided in + * {@link Control#getIcon()}. Pass {@code null} to use the icon in + * {@link Control#getIcon()}. + * @param overrideTint the colors to temporarily override those provided in + * {@link Control#getTint()}. Pass {@code null} to use the colors in + * {@link Control#getTint()}. + */ + public ControlState(@NonNull Control control, + int status, + @NonNull ControlTemplate controlTemplate, + @NonNull CharSequence statusText, + @Nullable Icon overrideIcon, + @Nullable ColorStateList overrideTint) { + Preconditions.checkNotNull(control); + Preconditions.checkNotNull(controlTemplate); + Preconditions.checkNotNull(statusText); + + mControl = control; + mStatus = status; + mControlTemplate = controlTemplate; + mOverrideIcon = overrideIcon; + mStatusText = statusText; + mOverrideTint = overrideTint; + } + + ControlState(Parcel in) { + mControl = Control.CREATOR.createFromParcel(in); + mStatus = in.readInt(); + mControlTemplate = ControlTemplate.CREATOR.createFromParcel(in); + mStatusText = in.readCharSequence(); + if (in.readByte() == 1) { + mOverrideIcon = Icon.CREATOR.createFromParcel(in); + } else { + mOverrideIcon = null; + } + if (in.readByte() == 1) { + mOverrideTint = ColorStateList.CREATOR.createFromParcel(in); + } else { + mOverrideTint = null; + } + } + + @Override + public int describeContents() { + return 0; + } + + @Status + public int getStatus() { + return mStatus; + } + + @NonNull + public ControlTemplate getControlTemplate() { + return mControlTemplate; + } + + @Nullable + public Icon getOverrideIcon() { + return mOverrideIcon; + } + + @NonNull + public CharSequence getStatusText() { + return mStatusText; + } + + @Nullable + public ColorStateList getOverrideTint() { + return mOverrideTint; + } + + @NonNull + public Control getControl() { + return mControl; + } + + @NonNull + public String getControlId() { + return mControl.getControlId(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + mControl.writeToParcel(dest, flags); + dest.writeInt(mStatus); + mControlTemplate.writeToParcel(dest, flags); + dest.writeCharSequence(mStatusText); + if (mOverrideIcon != null) { + dest.writeByte((byte) 1); + mOverrideIcon.writeToParcel(dest, flags); + } else { + dest.writeByte((byte) 0); + } + if (mOverrideTint != null) { + dest.writeByte((byte) 1); + mOverrideTint.writeToParcel(dest, flags); + } else { + dest.writeByte((byte) 0); + } + } + + public static final Creator<ControlState> CREATOR = new Creator<ControlState>() { + @Override + public ControlState createFromParcel(Parcel source) { + return new ControlState(source); + } + + @Override + public ControlState[] newArray(int size) { + return new ControlState[size]; + } + }; + + /** + * Builder class for {@link ControlState}. + * + * This class facilitates the creation of {@link ControlState}. It provides the following + * defaults for non-optional parameters: + * <ul> + * <li> Status: {@link ControlState#STATUS_OK} + * <li> Control template: {@link ControlTemplate#NO_TEMPLATE} + * <li> Status text: {@code ""} + * </ul> + */ + public static class Builder { + private @NonNull Control mControl; + private @Status int mStatus = STATUS_OK; + private @NonNull ControlTemplate mControlTemplate = ControlTemplate.NO_TEMPLATE; + private @NonNull CharSequence mStatusText = ""; + private @Nullable Icon mOverrideIcon; + private @Nullable ColorStateList mOverrideTint; + + /** + * @param control the {@link Control} that the resulting {@link ControlState} refers to. + */ + public Builder(@NonNull Control control) { + Preconditions.checkNotNull(control); + mControl = control; + } + + /** + * Creates a {@link Builder} using an existing {@link ControlState} as a base. + * @param controlState base for the builder. + */ + public Builder(@NonNull ControlState controlState) { + Preconditions.checkNotNull(controlState); + mControl = controlState.mControl; + mControlTemplate = controlState.mControlTemplate; + mOverrideIcon = controlState.mOverrideIcon; + mStatusText = controlState.mStatusText; + mOverrideTint = controlState.mOverrideTint; + } + + + /** + * @param control the updated {@link Control} information. + * @return {@code this} + */ + @NonNull + public Builder setControl(@NonNull Control control) { + mControl = control; + return this; + } + + /** + * @param status the current status of the {@link Control} + * @return {@code this} + */ + @NonNull + public Builder setStatus(@Status int status) { + mStatus = status; + return this; + } + + /** + * @param controlTemplate the template to use when rendering the {@code Control}. + * @return {@code this} + */ + @NonNull + public Builder setControlTemplate(@NonNull ControlTemplate controlTemplate) { + Preconditions.checkNotNull(controlTemplate); + mControlTemplate = controlTemplate; + return this; + } + + /** + * @param statusText the user-visible description of the status. + * @return {@code this} + */ + @NonNull + public Builder setStatusText(@NonNull CharSequence statusText) { + Preconditions.checkNotNull(statusText); + mStatusText = statusText; + return this; + } + + /** + * @param overrideIcon the icon to override the one defined in the corresponding + * {@code Control}. Pass {@code null} to remove the override. + * @return {@code this} + */ + @NonNull + public Builder setOverrideIcon(@Nullable Icon overrideIcon) { + mOverrideIcon = overrideIcon; + return this; + } + + /** + * @param overrideTint the colors to override the ones defined in the corresponding + * {@code Control}. Pass {@code null} to remove the override. + * @return {@code this} + */ + @NonNull + public Builder setOverrideTint(@Nullable ColorStateList overrideTint) { + mOverrideTint = overrideTint; + return this; + } + + /** + * @return a new {@link ControlState} + */ + public ControlState build() { + return new ControlState(mControl, mStatus, mControlTemplate, mStatusText, + mOverrideIcon, mOverrideTint); + } + } +} + diff --git a/core/java/android/service/controls/ControlTemplate.aidl b/core/java/android/service/controls/ControlTemplate.aidl new file mode 100644 index 000000000000..ecb948c8a306 --- /dev/null +++ b/core/java/android/service/controls/ControlTemplate.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +parcelable ControlTemplate;
\ No newline at end of file diff --git a/core/java/android/service/controls/ControlTemplate.java b/core/java/android/service/controls/ControlTemplate.java new file mode 100644 index 000000000000..e559862e86d6 --- /dev/null +++ b/core/java/android/service/controls/ControlTemplate.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * An abstract input template for a {@link Control}. + * + * Specifies what layout is presented to the user when a {@link ControlState} is assigned to a + * particular {@link Control}. + * <p> + * Some instances of {@link Control} can originate actions (via user interaction) to modify its + * associated state. The actions available to a given {@link Control} in a particular + * {@link ControlState} are determined by its {@link ControlTemplate}. + * @see ControlAction + * @hide + */ +public abstract class ControlTemplate implements Parcelable { + + /** + * Singleton representing a {@link Control} with no input. + */ + public static final ControlTemplate NO_TEMPLATE = new ControlTemplate("") { + @Override + public int getTemplateType() { + return TYPE_NONE; + } + }; + + /** + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + TYPE_NONE, + TYPE_TOGGLE, + TYPE_RANGE, + TYPE_THUMBNAIL, + TYPE_DISCRETE_TOGGLE, + TYPE_COORD_RANGE + }) + public @interface TemplateType {} + + /** + * Type identifier of {@link ControlTemplate#NO_TEMPLATE}. + */ + public static final int TYPE_NONE = 0; + + /** + * Type identifier of {@link ToggleTemplate}. + */ + public static final int TYPE_TOGGLE = 1; + + /** + * Type identifier of {@link RangeTemplate}. + */ + public static final int TYPE_RANGE = 2; + + /** + * Type identifier of {@link ThumbnailTemplate}. + */ + public static final int TYPE_THUMBNAIL = 3; + + /** + * Type identifier of {@link DiscreteToggleTemplate}. + */ + public static final int TYPE_DISCRETE_TOGGLE = 4; + + /** + * @hide + */ + public static final int TYPE_COORD_RANGE = 5; + + private @NonNull final String mTemplateId; + + /** + * @return the identifier for this object. + */ + public String getTemplateId() { + return mTemplateId; + } + + /** + * The {@link TemplateType} associated with this class. + */ + public abstract @TemplateType int getTemplateType(); + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(getTemplateType()); + dest.writeString(mTemplateId); + } + + private ControlTemplate() { + mTemplateId = ""; + } + + ControlTemplate(Parcel in) { + mTemplateId = in.readString(); + } + + /** + * @hide + */ + ControlTemplate(@NonNull String templateId) { + Preconditions.checkNotNull(templateId); + mTemplateId = templateId; + } + + public static final Creator<ControlTemplate> CREATOR = new Creator<ControlTemplate>() { + @Override + public ControlTemplate createFromParcel(Parcel source) { + int type = source.readInt(); + return createTemplateFromType(type, source); + } + + @Override + public ControlTemplate[] newArray(int size) { + return new ControlTemplate[size]; + } + }; + + private static ControlTemplate createTemplateFromType(@TemplateType int type, Parcel source) { + switch(type) { + case TYPE_TOGGLE: + return ToggleTemplate.CREATOR.createFromParcel(source); + case TYPE_RANGE: + return RangeTemplate.CREATOR.createFromParcel(source); + case TYPE_THUMBNAIL: + return ThumbnailTemplate.CREATOR.createFromParcel(source); + case TYPE_DISCRETE_TOGGLE: + return DiscreteToggleTemplate.CREATOR.createFromParcel(source); + case TYPE_NONE: + return NO_TEMPLATE; + default: + return null; + } + } +} diff --git a/core/java/android/service/controls/DiscreteToggleTemplate.java b/core/java/android/service/controls/DiscreteToggleTemplate.java new file mode 100644 index 000000000000..5167af41c2f0 --- /dev/null +++ b/core/java/android/service/controls/DiscreteToggleTemplate.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +import android.annotation.NonNull; +import android.os.Parcel; + +import com.android.internal.util.Preconditions; + +/** + * A template for a {@link Control} with two discrete inputs. + * + * The two inputs represent a <i>Negative</i> input and a <i>Positive</i> input. + * <p> + * When one of the buttons is actioned, a {@link BooleanAction} will be sent. + * {@link BooleanAction#getNewState} will be {@code false} if the button was + * {@link DiscreteToggleTemplate#getNegativeButton} and {@code true} if the button was + * {@link DiscreteToggleTemplate#getPositiveButton}. + * @hide + */ +public class DiscreteToggleTemplate extends ControlTemplate { + + private final @NonNull ControlButton mNegativeButton; + private final @NonNull ControlButton mPositiveButton; + + /** + * @param templateId the identifier for this template object + * @param negativeButton a {@ControlButton} for the <i>Negative</i> input + * @param positiveButton a {@ControlButton} for the <i>Positive</i> input + */ + public DiscreteToggleTemplate(@NonNull String templateId, + @NonNull ControlButton negativeButton, + @NonNull ControlButton positiveButton) { + super(templateId); + Preconditions.checkNotNull(negativeButton); + Preconditions.checkNotNull(positiveButton); + mNegativeButton = negativeButton; + mPositiveButton = positiveButton; + } + + DiscreteToggleTemplate(Parcel in) { + super(in); + this.mNegativeButton = ControlButton.CREATOR.createFromParcel(in); + this.mPositiveButton = ControlButton.CREATOR.createFromParcel(in); + } + + /** + * The {@link ControlButton} associated with the <i>Negative</i> action. + */ + @NonNull + public ControlButton getNegativeButton() { + return mNegativeButton; + } + + /** + * The {@link ControlButton} associated with the <i>Positive</i> action. + */ + @NonNull + public ControlButton getPositiveButton() { + return mPositiveButton; + } + + /** + * @return {@link ControlTemplate#TYPE_DISCRETE_TOGGLE} + */ + @Override + public int getTemplateType() { + return TYPE_DISCRETE_TOGGLE; + } + + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + mNegativeButton.writeToParcel(dest, flags); + mPositiveButton.writeToParcel(dest, flags); + } + + public static final Creator<DiscreteToggleTemplate> CREATOR = + new Creator<DiscreteToggleTemplate>() { + @Override + public DiscreteToggleTemplate createFromParcel(Parcel source) { + return new DiscreteToggleTemplate(source); + } + + @Override + public DiscreteToggleTemplate[] newArray(int size) { + return new DiscreteToggleTemplate[size]; + } + }; +} diff --git a/core/java/android/service/controls/FloatAction.aidl b/core/java/android/service/controls/FloatAction.aidl new file mode 100644 index 000000000000..dbc0f726880c --- /dev/null +++ b/core/java/android/service/controls/FloatAction.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +parcelable FloatAction;
\ No newline at end of file diff --git a/core/java/android/service/controls/FloatAction.java b/core/java/android/service/controls/FloatAction.java new file mode 100644 index 000000000000..fe6db10a98cd --- /dev/null +++ b/core/java/android/service/controls/FloatAction.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; + +/** + * Action sent by a {@link RangeTemplate}. + * @hide + */ +public final class FloatAction extends ControlAction { + + private final float mNewValue; + + /** + * @param templateId the identifier of the {@link RangeTemplate} that produced this action. + * @param newValue new value for the state displayed by the {@link RangeTemplate}. + */ + public FloatAction(@NonNull String templateId, float newValue) { + this(templateId, newValue, null); + } + + /** + * @param templateId the identifier of the {@link RangeTemplate} that originated this action. + * @param newValue new value for the state of the {@link RangeTemplate}. + * @param challengeValue a value sent by the user along with the action to authenticate. {@code} + * null is sent when no authentication is needed or has not been + * requested. + */ + + public FloatAction(@NonNull String templateId, float newValue, + @Nullable String challengeValue) { + super(templateId, challengeValue); + mNewValue = newValue; + } + + public FloatAction(Parcel in) { + super(in); + mNewValue = in.readFloat(); + } + + /** + * The new value set for the range in the corresponding {@link RangeTemplate}. + */ + public float getNewValue() { + return mNewValue; + } + + /** + * @return {@link ControlAction#TYPE_FLOAT} + */ + @Override + public int getActionType() { + return TYPE_FLOAT; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeFloat(mNewValue); + } + + public static final @NonNull Creator<FloatAction> CREATOR = new Creator<FloatAction>() { + @Override + public FloatAction createFromParcel(Parcel source) { + return new FloatAction(source); + } + + @Override + public FloatAction[] newArray(int size) { + return new FloatAction[size]; + } + }; +} diff --git a/core/java/android/service/controls/IControlsProvider.aidl b/core/java/android/service/controls/IControlsProvider.aidl new file mode 100644 index 000000000000..f778653eb3d3 --- /dev/null +++ b/core/java/android/service/controls/IControlsProvider.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +import android.service.controls.ControlAction; + +/** @hide */ +oneway interface IControlsProvider { + void load(); + + void subscribe(in List<String> controlIds); + + void unsubscribe(); + + void onAction(in String controlId, in ControlAction action); +}
\ No newline at end of file diff --git a/core/java/android/service/controls/IControlsProviderCallback.aidl b/core/java/android/service/controls/IControlsProviderCallback.aidl new file mode 100644 index 000000000000..3dbb68c1c7f0 --- /dev/null +++ b/core/java/android/service/controls/IControlsProviderCallback.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +import android.service.controls.Control; +import android.service.controls.ControlState; + +/** @hide */ +oneway interface IControlsProviderCallback { + void onLoad(in List<Control> controls); + + void onRefreshState(in List<ControlState> controlStates); + + void onControlActionResponse(in String controlId, int response); +}
\ No newline at end of file diff --git a/core/java/android/service/controls/RangeTemplate.aidl b/core/java/android/service/controls/RangeTemplate.aidl new file mode 100644 index 000000000000..a3d1ca074276 --- /dev/null +++ b/core/java/android/service/controls/RangeTemplate.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +parcelable RangeTemplate;
\ No newline at end of file diff --git a/core/java/android/service/controls/RangeTemplate.java b/core/java/android/service/controls/RangeTemplate.java new file mode 100644 index 000000000000..70bf2dd4aad4 --- /dev/null +++ b/core/java/android/service/controls/RangeTemplate.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; + +import com.android.internal.util.Preconditions; + +import java.security.InvalidParameterException; + +/** + * A template for a {@link Control} with inputs in a "continuous" range of values. + * + * @see FloatAction + * @hide + */ +public final class RangeTemplate extends ControlTemplate { + + private final float mMinValue; + private final float mMaxValue; + private final float mCurrentValue; + private final float mStepValue; + private final @NonNull CharSequence mFormatString; + + /** + * Construct a new {@link RangeTemplate}. + * + * The range must be valid, meaning: + * <ul> + * <li> {@code minValue} < {@code maxValue} + * <li> {@code minValue} < {@code currentValue} + * <li> {@code currentValue} < {@code maxValue} + * <li> 0 < {@code stepValue} + * </ul> + * <p> + * The current value of the Control will be formatted accordingly. + * + * @param templateId the identifier for this template object + * @param minValue minimum value for the input + * @param maxValue maximum value for the input + * @param currentValue the current value of the {@link ControlState} containing this object. + * @param stepValue minimum value of increments/decrements when interacting with this control. + * @param formatString a formatting string as per {@link String#format} used to display the + * {@code currentValue}. If {@code null} is passed, the "%.1f" is used. + * @throws InvalidParameterException if the parameters passed do not make a valid range. + */ + public RangeTemplate(@NonNull String templateId, + float minValue, + float maxValue, + float currentValue, + float stepValue, + @Nullable CharSequence formatString) { + super(templateId); + Preconditions.checkNotNull(formatString); + mMinValue = minValue; + mMaxValue = maxValue; + mCurrentValue = currentValue; + mStepValue = stepValue; + if (formatString != null) { + mFormatString = formatString; + } else { + mFormatString = "%.1f"; + } + validate(); + } + + /** + * Construct a new {@link RangeTemplate} from a {@link Parcel}. + * + * @throws InvalidParameterException if the parameters passed do not make a valid range + * @see RangeTemplate#RangeTemplate(String, float, float, float, float, CharSequence) + * @hide + */ + RangeTemplate(Parcel in) { + super(in); + mMinValue = in.readFloat(); + mMaxValue = in.readFloat(); + mCurrentValue = in.readFloat(); + mStepValue = in.readFloat(); + mFormatString = in.readCharSequence(); + validate(); + } + + /** + * The minimum value for this range. + */ + public float getMinValue() { + return mMinValue; + } + + /** + * The maximum value for this range. + */ + public float getMaxValue() { + return mMaxValue; + } + + /** + * The current value for this range. + */ + public float getCurrentValue() { + return mCurrentValue; + } + + /** + * The value of the smallest increment or decrement that can be performed on this range. + */ + public float getStepValue() { + return mStepValue; + } + + /** + * Formatter for generating a user visible {@link String} representing the value + * returned by {@link RangeTemplate#getCurrentValue}. + * @return a formatting string as specified in {@link String#format} + */ + @NonNull + public CharSequence getFormatString() { + return mFormatString; + } + + /** + * @return {@link ControlTemplate#TYPE_RANGE} + */ + @Override + public int getTemplateType() { + return TYPE_RANGE; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeFloat(mMinValue); + dest.writeFloat(mMaxValue); + dest.writeFloat(mCurrentValue); + dest.writeFloat(mStepValue); + dest.writeCharSequence(mFormatString); + } + + /** + * Validate constructor parameters + * + * @throws InvalidParameterException if the parameters passed do not make a valid range + */ + private void validate() { + if (Float.compare(mMinValue, mMaxValue) > 0) { + throw new InvalidParameterException( + String.format("minValue=%f > maxValue=%f", mMinValue, mMaxValue)); + } + if (Float.compare(mMinValue, mCurrentValue) > 0) { + throw new InvalidParameterException( + String.format("minValue=%f > currentValue=%f", mMinValue, mCurrentValue)); + } + if (Float.compare(mCurrentValue, mMaxValue) > 0) { + throw new InvalidParameterException( + String.format("currentValue=%f > maxValue=%f", mCurrentValue, mMaxValue)); + } + if (mStepValue <= 0) { + throw new InvalidParameterException(String.format("stepValue=%f <= 0", mStepValue)); + } + } + + public static final Creator<RangeTemplate> CREATOR = new Creator<RangeTemplate>() { + @Override + public RangeTemplate createFromParcel(Parcel source) { + return new RangeTemplate(source); + } + + @Override + public RangeTemplate[] newArray(int size) { + return new RangeTemplate[size]; + } + }; +} diff --git a/core/java/android/service/controls/ThumbnailTemplate.aidl b/core/java/android/service/controls/ThumbnailTemplate.aidl new file mode 100644 index 000000000000..fe8c7fed7c89 --- /dev/null +++ b/core/java/android/service/controls/ThumbnailTemplate.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +parcelable ThumbnailTemplate;
\ No newline at end of file diff --git a/core/java/android/service/controls/ThumbnailTemplate.java b/core/java/android/service/controls/ThumbnailTemplate.java new file mode 100644 index 000000000000..796d2de89576 --- /dev/null +++ b/core/java/android/service/controls/ThumbnailTemplate.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +import android.annotation.NonNull; +import android.graphics.drawable.Icon; +import android.os.Parcel; + +import com.android.internal.util.Preconditions; + +/** + * A template for a {@link Control} that displays an image. + * @hide + */ +public final class ThumbnailTemplate extends ControlTemplate { + + private final @NonNull Icon mThumbnail; + private final @NonNull CharSequence mContentDescription; + + /** + * @param templateId the identifier for this template object + * @param thumbnail an image to display on the {@link Control} + * @param contentDescription a description of the image for accessibility. + */ + public ThumbnailTemplate(@NonNull String templateId, @NonNull Icon thumbnail, + @NonNull CharSequence contentDescription) { + super(templateId); + Preconditions.checkNotNull(thumbnail); + Preconditions.checkNotNull(contentDescription); + mThumbnail = thumbnail; + mContentDescription = contentDescription; + } + + ThumbnailTemplate(Parcel in) { + super(in); + mThumbnail = Icon.CREATOR.createFromParcel(in); + mContentDescription = in.readCharSequence(); + } + + /** + * The {@link Icon} (image) displayed by this template. + */ + @NonNull + public Icon getThumbnail() { + return mThumbnail; + } + + /** + * The description of the image returned by {@link ThumbnailTemplate#getThumbnail()} + */ + @NonNull + public CharSequence getContentDescription() { + return mContentDescription; + } + + /** + * @return {@link ControlTemplate#TYPE_THUMBNAIL} + */ + @Override + public int getTemplateType() { + return TYPE_THUMBNAIL; + } + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + mThumbnail.writeToParcel(dest, flags); + dest.writeCharSequence(mContentDescription); + } + + public static final Creator<ThumbnailTemplate> CREATOR = new Creator<ThumbnailTemplate>() { + @Override + public ThumbnailTemplate createFromParcel(Parcel source) { + return new ThumbnailTemplate(source); + } + + @Override + public ThumbnailTemplate[] newArray(int size) { + return new ThumbnailTemplate[size]; + } + }; +} diff --git a/core/java/android/service/controls/ToggleTemplate.aidl b/core/java/android/service/controls/ToggleTemplate.aidl new file mode 100644 index 000000000000..1c823d9aee6d --- /dev/null +++ b/core/java/android/service/controls/ToggleTemplate.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +parcelable ToggleTemplate;
\ No newline at end of file diff --git a/core/java/android/service/controls/ToggleTemplate.java b/core/java/android/service/controls/ToggleTemplate.java new file mode 100644 index 000000000000..3766bd168477 --- /dev/null +++ b/core/java/android/service/controls/ToggleTemplate.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +import android.annotation.NonNull; +import android.os.Parcel; + +import com.android.internal.util.Preconditions; + +/** + * A template for a {@link Control} with a single button that can be toggled between two states. + * + * The states for the toggle correspond to the states in {@link ControlButton#isActive()}. + * An action on this template will originate a {@link BooleanAction} to change that state. + * + * @see BooleanAction + * @hide + */ +public final class ToggleTemplate extends ControlTemplate { + + private final @NonNull ControlButton mButton; + + /** + * @param templateId the identifier for this template object + * @param button a {@ControlButton} that can show the current state and toggle it + */ + public ToggleTemplate(@NonNull String templateId, @NonNull ControlButton button) { + super(templateId); + Preconditions.checkNotNull(button); + mButton = button; + } + + ToggleTemplate(Parcel in) { + super(in); + mButton = ControlButton.CREATOR.createFromParcel(in); + } + + /** + * The button provided to this object in {@link ToggleTemplate#ToggleTemplate} + */ + @NonNull + public ControlButton getButton() { + return mButton; + } + + /** + * @return {@link ControlTemplate#TYPE_TOGGLE} + */ + @Override + public int getTemplateType() { + return TYPE_TOGGLE; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + mButton.writeToParcel(dest, flags); + } + + public static final Creator<ToggleTemplate> CREATOR = new Creator<ToggleTemplate>() { + @Override + public ToggleTemplate createFromParcel(Parcel source) { + return new ToggleTemplate(source); + } + + @Override + public ToggleTemplate[] newArray(int size) { + return new ToggleTemplate[size]; + } + }; + +} diff --git a/core/java/android/service/incremental/IIncrementalDataLoaderService.aidl b/core/java/android/service/incremental/IIncrementalDataLoaderService.aidl deleted file mode 100644 index 723fc594bd72..000000000000 --- a/core/java/android/service/incremental/IIncrementalDataLoaderService.aidl +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.service.incremental; - -import android.os.incremental.IncrementalDataLoaderParamsParcel; -import android.os.incremental.IncrementalFileSystemControlParcel; -import android.service.incremental.IIncrementalDataLoaderStatusListener; - -/** @hide */ -oneway interface IIncrementalDataLoaderService { - void createDataLoader(in int storageId, - in IncrementalFileSystemControlParcel control, - in IncrementalDataLoaderParamsParcel params, - in IIncrementalDataLoaderStatusListener listener, - in boolean start); - void startDataLoader(in int storageId); - void stopDataLoader(in int storageId); - void destroyDataLoader(in int storageId); - void onFileCreated(in int storageId, in long inode, in byte[] metadata); -} diff --git a/core/java/android/service/notification/IConditionProvider.aidl b/core/java/android/service/notification/IConditionProvider.aidl index 3f3c6b80286d..dd3904fc28ce 100644 --- a/core/java/android/service/notification/IConditionProvider.aidl +++ b/core/java/android/service/notification/IConditionProvider.aidl @@ -1,5 +1,5 @@ /** - * Copyright (c) 2014, The Android Open Source Project + * Copyright (c) 2019, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/telephony/java/android/telephony/DataConnectionRealTimeInfo.aidl b/core/java/android/telephony/DataConnectionRealTimeInfo.aidl index 70fbb1180e15..70fbb1180e15 100644 --- a/telephony/java/android/telephony/DataConnectionRealTimeInfo.aidl +++ b/core/java/android/telephony/DataConnectionRealTimeInfo.aidl diff --git a/telephony/java/android/telephony/DataConnectionRealTimeInfo.java b/core/java/android/telephony/DataConnectionRealTimeInfo.java index 8106f5f30d26..8106f5f30d26 100644 --- a/telephony/java/android/telephony/DataConnectionRealTimeInfo.java +++ b/core/java/android/telephony/DataConnectionRealTimeInfo.java diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index f66a679ad87d..9d7b57ba250d 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -455,6 +455,19 @@ public class TelephonyRegistryManager { } /** + * Sim activation type: voice + * @see #notifyVoiceActivationStateChanged + * @hide + */ + public static final int SIM_ACTIVATION_TYPE_VOICE = 0; + /** + * Sim activation type: data + * @see #notifyDataActivationStateChanged + * @hide + */ + public static final int SIM_ACTIVATION_TYPE_DATA = 1; + + /** * Notify data activation state changed on certain subscription. * @see TelephonyManager#getDataActivationState() * @@ -469,7 +482,7 @@ public class TelephonyRegistryManager { @SimActivationState int activationState) { try { sRegistry.notifySimActivationStateChangedForPhoneId(slotIndex, subId, - TelephonyManager.SIM_ACTIVATION_TYPE_DATA, activationState); + SIM_ACTIVATION_TYPE_DATA, activationState); } catch (RemoteException ex) { // system process is dead } @@ -490,7 +503,7 @@ public class TelephonyRegistryManager { @SimActivationState int activationState) { try { sRegistry.notifySimActivationStateChangedForPhoneId(slotIndex, subId, - TelephonyManager.SIM_ACTIVATION_TYPE_VOICE, activationState); + SIM_ACTIVATION_TYPE_VOICE, activationState); } catch (RemoteException ex) { // system process is dead } diff --git a/core/java/android/util/StatsEvent.java b/core/java/android/util/StatsEvent.java index d7ec30c531eb..0a4069d15706 100644 --- a/core/java/android/util/StatsEvent.java +++ b/core/java/android/util/StatsEvent.java @@ -177,7 +177,7 @@ public final class StatsEvent implements Parcelable { * @hide **/ @VisibleForTesting - public static final int ERROR_ATTRIBUTION_UIDS_TAGS_SIZES_NOT_EQUAL = 0x400; + public static final int ERROR_ATTRIBUTION_UIDS_TAGS_SIZES_NOT_EQUAL = 0x1000; // Size limits. @@ -628,9 +628,9 @@ public final class StatsEvent implements Parcelable { if (0 == mErrorMask) { mBuffer.putByte(POS_NUM_ELEMENTS, (byte) mNumElements); } else { - mBuffer.putByte(0, TYPE_ERRORS); - mBuffer.putByte(POS_NUM_ELEMENTS, (byte) 3); + mPos += mBuffer.putByte(mPos, TYPE_ERRORS); mPos += mBuffer.putInt(mPos, mErrorMask); + mBuffer.putByte(POS_NUM_ELEMENTS, (byte) 3); size = mPos; } diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 3c93bb7a59b2..bc70d634b4ec 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -467,6 +467,10 @@ public class InsetsController implements WindowInsetsController { } } + boolean isAnimating() { + return mAnimationDirection != DIRECTION_NONE; + } + private InsetsSourceConsumer createConsumerOfType(int type) { if (type == ITYPE_IME) { return new ImeInsetsSourceConsumer(mState, Transaction::new, this); @@ -514,6 +518,7 @@ public class InsetsController implements WindowInsetsController { } else { hideDirectly(types); } + mAnimationDirection = show ? DIRECTION_SHOW : DIRECTION_HIDE; mAnimator = ObjectAnimator.ofObject( controller, new InsetsProperty(), diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index b1caf1872de2..c6d9898a425c 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -167,7 +167,8 @@ public class InsetsSourceConsumer { } private void applyHiddenToControl() { - if (mSourceControl == null || mSourceControl.getLeash() == null) { + if (mSourceControl == null || mSourceControl.getLeash() == null + || mController.isAnimating()) { return; } diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index fc80e00f7381..9e5e20ab046b 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -442,6 +442,12 @@ public final class SurfaceControl implements Parcelable { public static final int METADATA_TASK_ID = 3; /** + * Accessibility ID to allow association between surfaces and accessibility tree. + * @hide + */ + public static final int METADATA_ACCESSIBILITY_ID = 4; + + /** * A wrapper around GraphicBuffer that contains extra information about how to * interpret the screenshot GraphicBuffer. * @hide @@ -2651,6 +2657,7 @@ public final class SurfaceControl implements Parcelable { * @hide */ public Transaction setMetadata(SurfaceControl sc, int key, Parcel data) { + sc.checkNotReleased(); nativeSetMetadata(mNativeObject, sc.mNativeObject, key, data); return this; } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index c62e69cc5ed1..7a817b6d4dc4 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1181,7 +1181,8 @@ public interface WindowManager extends ViewManager { * a soft input method, so it will be Z-ordered and positioned * independently of any active input method (typically this means it * gets Z-ordered on top of the input method, so it can use the full - * screen for its content and cover the input method if needed.) */ + * screen for its content and cover the input method if needed. You + * can use {@link #FLAG_ALT_FOCUSABLE_IM} to modify this behavior. */ public static final int FLAG_NOT_FOCUSABLE = 0x00000008; /** Window flag: this window can never receive touch events. */ @@ -1287,11 +1288,14 @@ public interface WindowManager extends ViewManager { * set for you by Window as described in {@link Window#setFlags}.*/ public static final int FLAG_LAYOUT_INSET_DECOR = 0x00010000; - /** Window flag: When set, input method can't interact with the focusable window - * and can be placed to use more space and cover the input method. - * Note: When combined with {@link #FLAG_NOT_FOCUSABLE}, this flag has no - * effect since input method cannot interact with windows having {@link #FLAG_NOT_FOCUSABLE} - * flag set. + /** Window flag: invert the state of {@link #FLAG_NOT_FOCUSABLE} with + * respect to how this window interacts with the current method. That + * is, if FLAG_NOT_FOCUSABLE is set and this flag is set, then the + * window will behave as if it needs to interact with the input method + * and thus be placed behind/away from it; if FLAG_NOT_FOCUSABLE is + * not set and this flag is set, then the window will behave as if it + * doesn't need to interact with the input method and can be placed + * to use more space and cover the input method. */ public static final int FLAG_ALT_FOCUSABLE_IM = 0x00020000; @@ -1989,12 +1993,16 @@ public interface WindowManager extends ViewManager { * * @param flags The current window manager flags. * - * @return Returns {@code true} if such a window should be behind/interact - * with an input method, (@code false} if not. + * @return Returns true if such a window should be behind/interact + * with an input method, false if not. */ public static boolean mayUseInputMethod(int flags) { - return (flags & FLAG_NOT_FOCUSABLE) != FLAG_NOT_FOCUSABLE - && (flags & FLAG_ALT_FOCUSABLE_IM) != FLAG_ALT_FOCUSABLE_IM; + switch (flags&(FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM)) { + case 0: + case FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM: + return true; + } + return false; } /** diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java index b0888f2c8df9..121ae1a17456 100644 --- a/core/java/com/android/internal/util/AsyncChannel.java +++ b/core/java/com/android/internal/util/AsyncChannel.java @@ -29,7 +29,7 @@ import android.os.Looper; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; -import android.util.Slog; +import android.util.Log; import java.util.Stack; @@ -841,7 +841,7 @@ public class AsyncChannel { msg.replyTo = sm.mMessenger; synchronized (sm.mHandler.mLockObject) { if (sm.mHandler.mResultMsg != null) { - Slog.wtf(TAG, "mResultMsg should be null here"); + Log.wtf(TAG, "mResultMsg should be null here"); sm.mHandler.mResultMsg = null; } dstMessenger.send(msg); @@ -851,9 +851,9 @@ public class AsyncChannel { } } } catch (InterruptedException e) { - Slog.e(TAG, "error in sendMessageSynchronously", e); + Log.e(TAG, "error in sendMessageSynchronously", e); } catch (RemoteException e) { - Slog.e(TAG, "error in sendMessageSynchronously", e); + Log.e(TAG, "error in sendMessageSynchronously", e); } sm.recycle(); return resultMsg; @@ -939,7 +939,7 @@ public class AsyncChannel { * @param s */ private static void log(String s) { - Slog.d(TAG, s); + Log.d(TAG, s); } private final class DeathMonitor implements IBinder.DeathRecipient { diff --git a/core/java/com/android/internal/util/AsyncService.java b/core/java/com/android/internal/util/AsyncService.java index e39a2bfc6abd..58e4a605f88c 100644 --- a/core/java/com/android/internal/util/AsyncService.java +++ b/core/java/com/android/internal/util/AsyncService.java @@ -22,7 +22,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; -import android.util.Slog; +import android.util.Log; /** * A service that receives Intents and IBinder transactions @@ -92,7 +92,7 @@ abstract public class AsyncService extends Service { */ @Override public int onStartCommand(Intent intent, int flags, int startId) { - if (DBG) Slog.d(TAG, "onStartCommand"); + if (DBG) Log.d(TAG, "onStartCommand"); Message msg = mHandler.obtainMessage(); msg.what = CMD_ASYNC_SERVICE_ON_START_INTENT; @@ -111,7 +111,7 @@ abstract public class AsyncService extends Service { */ @Override public void onDestroy() { - if (DBG) Slog.d(TAG, "onDestroy"); + if (DBG) Log.d(TAG, "onDestroy"); Message msg = mHandler.obtainMessage(); msg.what = CMD_ASYNC_SERVICE_DESTROY; diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java index 57bfcac5bf54..2ac2975d0a44 100644 --- a/core/java/com/android/internal/view/BaseIWindow.java +++ b/core/java/com/android/internal/view/BaseIWindow.java @@ -36,6 +36,8 @@ import com.android.internal.os.IResultReceiver; import dalvik.annotation.compat.UnsupportedAppUsage; +import java.io.IOException; + public class BaseIWindow extends IWindow.Stub { @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P) @@ -101,6 +103,13 @@ public class BaseIWindow extends IWindow.Stub { @Override public void executeCommand(String command, String parameters, ParcelFileDescriptor out) { + if (out != null) { + try { + out.closeWithError("Unsupported command " + command); + } catch (IOException e) { + // Ignore + } + } } @Override diff --git a/core/proto/android/stats/mediaprovider/mediaprovider_enums.proto b/core/proto/android/stats/mediaprovider/mediaprovider_enums.proto new file mode 100644 index 000000000000..138782bf5d19 --- /dev/null +++ b/core/proto/android/stats/mediaprovider/mediaprovider_enums.proto @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; +package android.stats.mediaprovider; +option java_multiple_files = true; + +enum VolumeType { + // Volume is unknown + UNKNOWN = 0; + // Volume is MediaStore.VOLUME_INTERNAL + INTERNAL = 1; + // Volume is MediaStore.VOLUME_EXTERNAL_PRIMARY + EXTERNAL_PRIMARY = 2; + // Volume is non-primary external storage + EXTERNAL_OTHER = 3; +} diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index fc9fa8595c5b..10a75842d7c6 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3695,6 +3695,8 @@ instead of 'Emergency calls only' when SIM is unready. --> <string-array translatable="false" name="config_display_no_service_when_sim_unready"> <item>"DE"</item> + <item>"GB"</item> + <item>"JP"</item> </string-array> <!-- Class names of device specific services inheriting com.android.server.SystemService. The @@ -4179,4 +4181,10 @@ <string-array name="config_integrityRuleProviderPackages" translatable="false"> <!-- Add packages here --> </string-array> + + <!-- When true, enables the whitelisted app to handle bug reports from power menu short press. --> + <bool name="config_bugReportHandlerEnabled">false</bool> + + <!-- The package name for the default bug report handler app from power menu short press. This app must be whitelisted. --> + <string name="config_defaultBugReportHandlerApp" translatable="false"></string> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index dcf73792e041..0f5da39a2c1e 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1014,23 +1014,29 @@ <string name="permdesc_readContacts" product="tablet">Allows the app to read data about your contacts stored on your tablet, including the frequency with which you\'ve called, emailed, or communicated in other ways with - specific individuals. This permission allows apps to save your contact + specific individuals. Apps will also have access to the accounts on your + tablet that have created contacts. This may include accounts created by + apps you have installed. This permission allows apps to save your contact data, and malicious apps may share contact data without your knowledge.</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_readContacts" product="tv">Allows the app to read data about your contacts stored on your Android TV device, including the frequency with which you\'ve called, emailed, or communicated in other ways with - specific individuals. This permission allows apps to save your contact - data, and malicious apps may share contact data without your + specific individuals. Apps will also have access to the accounts on your + Android TV device that have created contacts. This may include accounts + created by apps you have installed. This permission allows apps to save + your contact data, and malicious apps may share contact data without your knowledge.</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_readContacts" product="default">Allows the app to read data about your contacts stored on your phone, including the frequency with which you\'ve called, emailed, or communicated in other ways - with specific individuals. This permission allows apps to save your - contact data, and malicious apps may share contact data without your - knowledge.</string> + with specific individuals. Apps will also have access to the accounts + on your phone that have created contacts. This may include accounts + created by apps you have installed. This permission allows apps to + save your contact data, and malicious apps may share contact data + without your knowledge.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_writeContacts">modify your contacts</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index c6c9094229b9..083f33cc8035 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3751,6 +3751,10 @@ <java-symbol type="bool" name="config_showBuiltinWirelessChargingAnim" /> + <!-- For bug report handler --> + <java-symbol type="bool" name="config_bugReportHandlerEnabled" /> + <java-symbol type="string" name="config_defaultBugReportHandlerApp" /> + <java-symbol type="string" name="usb_device_resolve_prompt_warn" /> <!-- For Accessibility system actions --> diff --git a/core/tests/coretests/src/android/service/controls/ControlActionTest.java b/core/tests/coretests/src/android/service/controls/ControlActionTest.java new file mode 100644 index 000000000000..ef4912fe1f74 --- /dev/null +++ b/core/tests/coretests/src/android/service/controls/ControlActionTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +import static junit.framework.Assert.assertTrue; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import android.os.Parcel; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class ControlActionTest { + + private static final String TEST_ID = "TEST_ID"; + + @Test + public void testUnparcelingCorrectClass_boolean() { + ControlAction toParcel = new BooleanAction(TEST_ID, true); + + ControlAction fromParcel = parcelAndUnparcel(toParcel); + + assertEquals(ControlAction.TYPE_BOOLEAN, fromParcel.getActionType()); + assertTrue(fromParcel instanceof BooleanAction); + } + + @Test + public void testUnparcelingCorrectClass_float() { + ControlAction toParcel = new FloatAction(TEST_ID, 1); + + ControlAction fromParcel = parcelAndUnparcel(toParcel); + + assertEquals(ControlAction.TYPE_FLOAT, fromParcel.getActionType()); + assertTrue(fromParcel instanceof FloatAction); + } + + private ControlAction parcelAndUnparcel(ControlAction toParcel) { + Parcel parcel = Parcel.obtain(); + + assertNotNull(parcel); + + parcel.setDataPosition(0); + toParcel.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + return ControlAction.CREATOR.createFromParcel(parcel); + } +} diff --git a/core/tests/coretests/src/android/service/controls/ControlTemplateTest.java b/core/tests/coretests/src/android/service/controls/ControlTemplateTest.java new file mode 100644 index 000000000000..4fa4e1d7b733 --- /dev/null +++ b/core/tests/coretests/src/android/service/controls/ControlTemplateTest.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.controls; + +import static junit.framework.Assert.assertTrue; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import android.annotation.DrawableRes; +import android.graphics.drawable.Icon; +import android.os.Parcel; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.frameworks.coretests.R; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; + +import java.security.InvalidParameterException; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class ControlTemplateTest { + + private static final String PACKAGE_NAME = "com.android.frameworks.coretests"; + private static final @DrawableRes int TEST_ICON_ID = R.drawable.box; + private static final String TEST_ID = "TEST_ID"; + private static final CharSequence TEST_CONTENT_DESCRIPTION = "TEST_CONTENT_DESCRIPTION"; + private Icon mIcon; + private ControlButton mControlButton; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mIcon = Icon.createWithResource(PACKAGE_NAME, TEST_ICON_ID); + mControlButton = new ControlButton(true, mIcon, TEST_CONTENT_DESCRIPTION); + } + + @Test + public void testUnparcelingCorrectClass_none() { + ControlTemplate toParcel = ControlTemplate.NO_TEMPLATE; + + ControlTemplate fromParcel = parcelAndUnparcel(toParcel); + + assertEquals(ControlTemplate.NO_TEMPLATE, fromParcel); + } + + @Test + public void testUnparcelingCorrectClass_toggle() { + ControlTemplate toParcel = new ToggleTemplate(TEST_ID, mControlButton); + + ControlTemplate fromParcel = parcelAndUnparcel(toParcel); + + assertEquals(ControlTemplate.TYPE_TOGGLE, fromParcel.getTemplateType()); + assertTrue(fromParcel instanceof ToggleTemplate); + } + + @Test + public void testUnparcelingCorrectClass_range() { + ControlTemplate toParcel = new RangeTemplate(TEST_ID, 0, 2, 1, 1, "%f"); + + ControlTemplate fromParcel = parcelAndUnparcel(toParcel); + + assertEquals(ControlTemplate.TYPE_RANGE, fromParcel.getTemplateType()); + assertTrue(fromParcel instanceof RangeTemplate); + } + + @Test(expected = InvalidParameterException.class) + public void testRangeParameters_minMax() { + RangeTemplate range = new RangeTemplate(TEST_ID, 2, 0, 1, 1, "%f"); + } + + @Test(expected = InvalidParameterException.class) + public void testRangeParameters_minCurrent() { + RangeTemplate range = new RangeTemplate(TEST_ID, 0, 2, -1, 1, "%f"); + } + + @Test(expected = InvalidParameterException.class) + public void testRangeParameters_maxCurrent() { + RangeTemplate range = new RangeTemplate(TEST_ID, 0, 2, 3, 1, "%f"); + } + + @Test(expected = InvalidParameterException.class) + public void testRangeParameters_negativeStep() { + RangeTemplate range = new RangeTemplate(TEST_ID, 0, 2, 1, -1, "%f"); + } + + @Test + public void testUnparcelingCorrectClass_thumbnail() { + ControlTemplate toParcel = new ThumbnailTemplate(TEST_ID, mIcon, TEST_CONTENT_DESCRIPTION); + + ControlTemplate fromParcel = parcelAndUnparcel(toParcel); + + assertEquals(ControlTemplate.TYPE_THUMBNAIL, fromParcel.getTemplateType()); + assertTrue(fromParcel instanceof ThumbnailTemplate); + } + + @Test + public void testUnparcelingCorrectClass_discreteToggle() { + ControlTemplate toParcel = + new DiscreteToggleTemplate(TEST_ID, mControlButton, mControlButton); + + ControlTemplate fromParcel = parcelAndUnparcel(toParcel); + + assertEquals(ControlTemplate.TYPE_DISCRETE_TOGGLE, fromParcel.getTemplateType()); + assertTrue(fromParcel instanceof DiscreteToggleTemplate); + } + + private ControlTemplate parcelAndUnparcel(ControlTemplate toParcel) { + Parcel parcel = Parcel.obtain(); + + assertNotNull(parcel); + + parcel.setDataPosition(0); + toParcel.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + return ControlTemplate.CREATOR.createFromParcel(parcel); + } +} diff --git a/core/tests/coretests/src/android/util/StatsEventTest.java b/core/tests/coretests/src/android/util/StatsEventTest.java index 93f11dbccf64..097badadcea9 100644 --- a/core/tests/coretests/src/android/util/StatsEventTest.java +++ b/core/tests/coretests/src/android/util/StatsEventTest.java @@ -53,8 +53,8 @@ public class StatsEventTest { final ByteBuffer buffer = ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN); - assertWithMessage("Root element in buffer is not TYPE_ERRORS") - .that(buffer.get()).isEqualTo(StatsEvent.TYPE_ERRORS); + assertWithMessage("Root element in buffer is not TYPE_OBJECT") + .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT); assertWithMessage("Incorrect number of elements in root object") .that(buffer.get()).isEqualTo(3); @@ -71,6 +71,9 @@ public class StatsEventTest { assertWithMessage("Incorrect atom id") .that(buffer.getInt()).isEqualTo(expectedAtomId); + assertWithMessage("Third element is not errors type") + .that(buffer.get()).isEqualTo(StatsEvent.TYPE_ERRORS); + final int errorMask = buffer.getInt(); assertWithMessage("ERROR_NO_ATOM_ID should be the only error in the error mask") diff --git a/media/Android.bp b/media/Android.bp index 022fa9b7bb9e..1912930f2081 100644 --- a/media/Android.bp +++ b/media/Android.bp @@ -45,8 +45,8 @@ java_library { filegroup { name: "updatable-media-srcs", srcs: [ - ":mediasession2-srcs", ":mediaparser-srcs", + ":mediasession2-srcs", ], } @@ -73,7 +73,8 @@ filegroup { name: "mediaparser-srcs", srcs: [ "apex/java/android/media/MediaParser.java" - ] + ], + path: "apex/java" } metalava_updatable_media_args = " --error UnhiddenSystemApi " + diff --git a/media/apex/java/android/media/MediaParser.java b/media/apex/java/android/media/MediaParser.java index c06e2837bcdc..8824269ea0c0 100644 --- a/media/apex/java/android/media/MediaParser.java +++ b/media/apex/java/android/media/MediaParser.java @@ -15,10 +15,47 @@ */ package android.media; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.Uri; +import android.text.TextUtils; import android.util.Pair; - +import android.util.SparseArray; + +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.extractor.DefaultExtractorInput; +import com.google.android.exoplayer2.extractor.Extractor; +import com.google.android.exoplayer2.extractor.ExtractorInput; +import com.google.android.exoplayer2.extractor.ExtractorOutput; +import com.google.android.exoplayer2.extractor.PositionHolder; +import com.google.android.exoplayer2.extractor.SeekMap.SeekPoints; +import com.google.android.exoplayer2.extractor.TrackOutput; +import com.google.android.exoplayer2.extractor.amr.AmrExtractor; +import com.google.android.exoplayer2.extractor.flv.FlvExtractor; +import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor; +import com.google.android.exoplayer2.extractor.mp3.Mp3Extractor; +import com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor; +import com.google.android.exoplayer2.extractor.mp4.Mp4Extractor; +import com.google.android.exoplayer2.extractor.ogg.OggExtractor; +import com.google.android.exoplayer2.extractor.ts.Ac3Extractor; +import com.google.android.exoplayer2.extractor.ts.Ac4Extractor; +import com.google.android.exoplayer2.extractor.ts.AdtsExtractor; +import com.google.android.exoplayer2.extractor.ts.PsExtractor; +import com.google.android.exoplayer2.extractor.ts.TsExtractor; +import com.google.android.exoplayer2.extractor.wav.WavExtractor; +import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.DataSpec; +import com.google.android.exoplayer2.upstream.TransferListener; +import com.google.android.exoplayer2.util.ParsableByteArray; + +import java.io.EOFException; import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; /** * Parses media container formats and extracts contained media samples and metadata. @@ -32,16 +69,93 @@ import java.util.List; * <p>Users must implement the following to use this class. * * <ul> - * <li>{@link Input}: Provides the media containers bytes to parse. - * <li>{@link OutputCallback}: Provides a sink for all extracted data and metadata. + * <li>{@link InputReader}: Provides the media container's bytes to parse. + * <li>{@link OutputConsumer}: Provides a sink for all extracted data and metadata. * </ul> * - * TODO: Add usage example here. + * <p>The following code snippet includes a usage example: + * + * <pre> + * MyOutputConsumer myOutputConsumer = new MyOutputConsumer(); + * MyInputReader myInputReader = new MyInputReader("www.example.com"); + * MediaParser mediaParser = MediaParser.create(myOutputConsumer); + * + * while (mediaParser.advance(myInputReader)) {} + * + * mediaParser.release(); + * mediaParser = null; + * </pre> + * + * <p>The following code snippet provides a rudimentary {@link OutputConsumer} sample implementation + * which extracts and publishes all video samples: + * + * <pre> + * + * class VideoOutputConsumer implements MediaParser.OutputConsumer { + * + * private static final int MAXIMUM_SAMPLE_SIZE = ...; + * private byte[] sampleDataBuffer = new byte[MAXIMUM_SAMPLE_SIZE]; + * private int videoTrackIndex = -1; + * private int bytesWrittenCount = 0; + * + * \@Override + * public void onSeekMap(int i, @NonNull MediaFormat mediaFormat) { \/* Do nothing. *\/ } + * + * \@Override + * public void onFormat(int i, @NonNull MediaFormat mediaFormat) { + * if (videoTrackIndex == -1 && mediaFormat + * .getString(MediaFormat.KEY_MIME, \/* defaultValue= *\/ "").startsWith("video/")) { + * videoTrackIndex = i; + * } + * } + * + * \@Override + * public void onSampleData(int trackIndex, @NonNull InputReader inputReader) + * throws IOException, InterruptedException { + * int numberOfBytesToRead = (int) inputReader.getLength(); + * if (videoTrackIndex != trackIndex) { + * // Discard contents. + * inputReader.read(\/* bytes= *\/ null, \/* offset= *\/ 0, numberOfBytesToRead); + * } + * int bytesRead = inputReader.read(sampleDataBuffer, bytesWrittenCount, numberOfBytesToRead); + * bytesWrittenCount += bytesRead; + * } + * + * \@Override + * public void onSampleCompleted( + * int trackIndex, + * long timeUs, + * int flags, + * int size, + * int offset, + * \@Nullable CryptoInfo cryptoData) { + * if (videoTrackIndex != trackIndex) { + * return; // It's not the video track. Ignore. + * } + * byte[] sampleData = new byte[size]; + * System.arraycopy(sampleDataBuffer, bytesWrittenCount - size - offset, sampleData, \/* + * destPos= *\/ 0, size); + * // Place trailing bytes at the start of the buffer. + * System.arraycopy( + * sampleDataBuffer, + * bytesWrittenCount - offset, + * sampleDataBuffer, + * \/* destPos= *\/ 0, + * \/* size= *\/ offset); + * publishSample(sampleData, timeUs, flags); + * } + * } + * + * </pre> */ -// @HiddenApi public final class MediaParser { - /** Maps seek positions to corresponding positions in the stream. */ + /** + * Maps seek positions to {@link SeekPoint SeekPoints} in the stream. + * + * <p>A {@link SeekPoint} is a position in the stream from which a player may successfully start + * playing media samples. + */ public interface SeekMap { /** Returned by {@link #getDurationUs()} when the duration is unknown. */ @@ -62,13 +176,14 @@ public final class MediaParser { * <p>{@code getSeekPoints(timeUs).first} contains the latest seek point for samples with * timestamp equal to or smaller than {@code timeUs}. * - * <p>{@code getSeekPoints(timeUs).second} contains the earlies seek point for samples with + * <p>{@code getSeekPoints(timeUs).second} contains the earliest seek point for samples with * timestamp equal to or greater than {@code timeUs}. If a seek point exists for {@code * timeUs}, the returned pair will contain the same {@link SeekPoint} twice. * * @param timeUs A seek time in microseconds. * @return The corresponding {@link SeekPoint SeekPoints}. */ + @NonNull Pair<SeekPoint, SeekPoint> getSeekPoints(long timeUs); } @@ -76,30 +191,30 @@ public final class MediaParser { public static final class SeekPoint { /** A {@link SeekPoint} whose time and byte offset are both set to 0. */ - public static final SeekPoint START = new SeekPoint(0, 0); + public static final @NonNull SeekPoint START = new SeekPoint(0, 0); /** The time of the seek point, in microseconds. */ - public final long mTimeUs; + public final long timeUs; /** The byte offset of the seek point. */ - public final long mPosition; + public final long position; /** * @param timeUs The time of the seek point, in microseconds. * @param position The byte offset of the seek point. */ - public SeekPoint(long timeUs, long position) { - this.mTimeUs = timeUs; - this.mPosition = position; + private SeekPoint(long timeUs, long position) { + this.timeUs = timeUs; + this.position = position; } @Override - public String toString() { - return "[timeUs=" + mTimeUs + ", position=" + mPosition + "]"; + public @NonNull String toString() { + return "[timeUs=" + timeUs + ", position=" + position + "]"; } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } @@ -107,25 +222,26 @@ public final class MediaParser { return false; } SeekPoint other = (SeekPoint) obj; - return mTimeUs == other.mTimeUs && mPosition == other.mPosition; + return timeUs == other.timeUs && position == other.position; } @Override public int hashCode() { - int result = (int) mTimeUs; - result = 31 * result + (int) mPosition; + int result = (int) timeUs; + result = 31 * result + (int) position; return result; } } /** Provides input data to {@link MediaParser}. */ - public interface Input { + public interface InputReader { /** * Reads up to {@code readLength} bytes of data and stores them into {@code buffer}, * starting at index {@code offset}. * - * <p>The call will block until at least one byte of data has been read. + * <p>This method blocks until at least one byte is read, the end of input is detected, or + * an exception is thrown. The read position advances to the first unread byte. * * @param buffer The buffer into which the read data should be stored. * @param offset The start offset into {@code buffer} at which data should be written. @@ -134,7 +250,7 @@ public final class MediaParser { * of the input has been reached. * @throws java.io.IOException If an error occurs reading from the source. */ - int read(byte[] buffer, int offset, int readLength) + int read(@NonNull byte[] buffer, int offset, int readLength) throws IOException, InterruptedException; /** Returns the current read position (byte offset) in the stream. */ @@ -144,22 +260,39 @@ public final class MediaParser { long getLength(); } - /** Receives extracted media sample data and metadata from {@link MediaParser}. */ - public interface OutputCallback { + /** {@link InputReader} that allows setting the read position. */ + public interface SeekableInputReader extends InputReader { /** - * Called when the number of tracks is defined. + * Sets the read position at the given {@code position}. * - * @param numberOfTracks The number of tracks in the stream. + * <p>{@link #advance} will immediately return after calling this method. + * + * @param position The position to seek to, in bytes. */ - void onTracksFound(int numberOfTracks); + void seekToPosition(long position); + } + + /** Receives extracted media sample data and metadata from {@link MediaParser}. */ + public interface OutputConsumer { /** * Called when a {@link SeekMap} has been extracted from the stream. * + * <p>This method is called at least once before any samples are {@link #onSampleCompleted + * complete}. May be called multiple times after that in order to add {@link SeekPoint + * SeekPoints}. + * * @param seekMap The extracted {@link SeekMap}. */ - void onSeekMap(SeekMap seekMap); + void onSeekMap(@NonNull SeekMap seekMap); + + /** + * Called when the number of tracks is found. + * + * @param numberOfTracks The number of tracks in the stream. + */ + void onTracksFound(int numberOfTracks); /** * Called when the {@link MediaFormat} of the track is extracted from the stream. @@ -167,7 +300,7 @@ public final class MediaParser { * @param trackIndex The index of the track for which the {@link MediaFormat} was found. * @param format The extracted {@link MediaFormat}. */ - void onFormat(int trackIndex, MediaFormat format); + void onFormat(int trackIndex, @NonNull MediaFormat format); /** * Called to write sample data to the output. @@ -176,16 +309,15 @@ public final class MediaParser { * thrown {@link IOException} caused by reading from {@code input}. * * @param trackIndex The index of the track to which the sample data corresponds. - * @param input The {@link Input} from which to read the data. - * @return + * @param inputReader The {@link InputReader} from which to read the data. */ - int onSampleData(int trackIndex, Input input) throws IOException, InterruptedException; + void onSampleData(int trackIndex, @NonNull InputReader inputReader) + throws IOException, InterruptedException; /** - * Defines the boundaries and metadata of an extracted sample. + * Called once all the data of a sample has been passed to {@link #onSampleData}. * - * <p>The corresponding sample data will have already been passed to the output via calls to - * {@link #onSampleData}. + * <p>Also includes sample metadata, like presentation timestamp and flags. * * @param trackIndex The index of the track to which the sample corresponds. * @param timeUs The media timestamp associated with the sample, in microseconds. @@ -203,57 +335,22 @@ public final class MediaParser { int flags, int size, int offset, - MediaCodec.CryptoInfo cryptoData); - } - - /** - * Controls the behavior of extractors' implementations. - * - * <p>DESIGN NOTE: For setting flags like workarounds and special behaviors for adaptive - * streaming. - */ - public static final class Parameters { - - // TODO: Implement. - - } - - /** Holds the result of an {@link #advance} invocation. */ - public static final class ResultHolder { - - /** Creates a new instance with {@link #result} holding {@link #ADVANCE_RESULT_CONTINUE}. */ - public ResultHolder() { - result = ADVANCE_RESULT_CONTINUE; - } - - /** - * May hold {@link #ADVANCE_RESULT_END_OF_INPUT}, {@link #ADVANCE_RESULT_CONTINUE}, {@link - * #ADVANCE_RESULT_SEEK}. - */ - public int result; - - /** - * If {@link #result} holds {@link #ADVANCE_RESULT_SEEK}, holds the stream position required - * from the passed {@link Input} to the next {@link #advance} call. If {@link #result} does - * not hold {@link #ADVANCE_RESULT_SEEK}, the value of this variable is undefined and should - * be ignored. - */ - public long seekPosition; + @Nullable MediaCodec.CryptoInfo cryptoData); } /** * Thrown if all extractors implementations provided to {@link #create} failed to sniff the * input content. */ - // @HiddenApi public static final class UnrecognizedInputFormatException extends IOException { /** * Creates a new instance which signals that the extractors with the given names failed to * parse the input. */ - public static UnrecognizedInputFormatException createForExtractors( - String... extractorNames) { + @NonNull + private static UnrecognizedInputFormatException createForExtractors( + @NonNull String... extractorNames) { StringBuilder builder = new StringBuilder(); builder.append("None of the available extractors ( "); builder.append(extractorNames[0]); @@ -270,21 +367,9 @@ public final class MediaParser { } } - // Public constants. + // Private constants. - /** - * Returned by {@link #advance} if the {@link Input} passed to the next {@link #advance} is - * required to provide data continuing from the position in the stream reached by the returning - * call. - */ - public static final int ADVANCE_RESULT_CONTINUE = -1; - /** Returned by {@link #advance} if the end of the {@link Input} was reached. */ - public static final int ADVANCE_RESULT_END_OF_INPUT = -2; - /** - * Returned by {@link #advance} when its next call expects a specific stream position, which - * will be held by {@link ResultHolder#seekPosition}. - */ - public static final int ADVANCE_RESULT_SEEK = -3; + private static final Map<String, ExtractorFactory> EXTRACTOR_FACTORIES_BY_NAME; // Instance creation methods. @@ -293,13 +378,15 @@ public final class MediaParser { * instance will attempt extraction without sniffing the content. * * @param name The name of the extractor that will be associated with the created instance. - * @param outputCallback The {@link OutputCallback} to which track data and samples are pushed. - * @param parameters Parameters that control specific aspects of the behavior of the extractors. + * @param outputConsumer The {@link OutputConsumer} to which track data and samples are pushed. * @return A new instance. + * @throws IllegalArgumentException If an invalid name is provided. */ - public static MediaParser createByName( - String name, OutputCallback outputCallback, Parameters parameters) { - throw new UnsupportedOperationException(); + public static @NonNull MediaParser createByName( + @NonNull String name, @NonNull OutputConsumer outputConsumer) { + String[] nameAsArray = new String[] {name}; + assertValidNames(nameAsArray); + return new MediaParser(outputConsumer, /* sniff= */ false, name); } /** @@ -307,30 +394,46 @@ public final class MediaParser { * the first {@link #advance} call. Extractor implementations will sniff the content in order of * appearance in {@code extractorNames}. * - * @param outputCallback The {@link OutputCallback} to track data and samples are obtained. - * @param parameters Parameters that control specific aspects of the behavior of the extractors. + * @param outputConsumer The {@link OutputConsumer} to which extracted data is output. * @param extractorNames The names of the extractors to sniff the content with. If empty, a * default array of names is used. * @return A new instance. */ - public static MediaParser create( - OutputCallback outputCallback, Parameters parameters, String... extractorNames) { - throw new UnsupportedOperationException(); + public static @NonNull MediaParser create( + @NonNull OutputConsumer outputConsumer, @NonNull String... extractorNames) { + assertValidNames(extractorNames); + if (extractorNames.length == 0) { + extractorNames = EXTRACTOR_FACTORIES_BY_NAME.keySet().toArray(new String[0]); + } + return new MediaParser(outputConsumer, /* sniff= */ true, extractorNames); } // Misc static methods. /** * Returns an immutable list with the names of the extractors that are suitable for container - * formats with the given {@code mimeTypes}. If an empty string is passed, all available - * extractors' names are returned. + * formats with the given {@link MediaFormat}. * - * <p>TODO: Replace string with media type object. + * <p>TODO: List which properties are taken into account. E.g. MimeType. */ - public static List<String> getExtractorNames(String mimeTypes) { + public static @NonNull List<String> getExtractorNames(@NonNull MediaFormat mediaFormat) { throw new UnsupportedOperationException(); } + // Private fields. + + private final OutputConsumer mOutputConsumer; + private final String[] mExtractorNamesPool; + private final PositionHolder mPositionHolder; + private final InputReadingDataSource mDataSource; + private final ExtractorInputAdapter mScratchExtractorInputAdapter; + private final ParsableByteArrayAdapter mScratchParsableByteArrayAdapter; + private String mExtractorName; + private Extractor mExtractor; + private ExtractorInput mExtractorInput; + private long mPendingSeekPosition; + private long mPendingSeekTimeUs; + // Public methods. /** @@ -344,8 +447,8 @@ public final class MediaParser { * @return The name of the backing extractor implementation, or null if the backing extractor * implementation has not yet been selected. */ - public String getExtractorName() { - throw new UnsupportedOperationException(); + public @Nullable String getExtractorName() { + return mExtractorName; } /** @@ -357,26 +460,85 @@ public final class MediaParser { * <p>If this instance was created using {@link #create}. the first call to this method will * sniff the content with the extractors with the provided names. * - * @param input The {@link Input} from which to obtain the media container data. - * @param resultHolder The {@link ResultHolder} into which the result of the operation will be - * written. + * @param seekableInputReader The {@link SeekableInputReader} from which to obtain the media + * container data. + * @return Whether there is any data left to extract. Returns false if the end of input has been + * reached. * @throws UnrecognizedInputFormatException */ - public void advance(Input input, ResultHolder resultHolder) + public boolean advance(@NonNull SeekableInputReader seekableInputReader) throws IOException, InterruptedException { - throw new UnsupportedOperationException(); + if (mExtractorInput == null) { + // TODO: For efficiency, the same implementation should be used, by providing a + // clearBuffers() method, or similar. + mExtractorInput = + new DefaultExtractorInput( + mDataSource, + seekableInputReader.getPosition(), + seekableInputReader.getLength()); + } + mDataSource.mInputReader = seekableInputReader; + + if (mExtractor == null) { + for (String extractorName : mExtractorNamesPool) { + Extractor extractor = + EXTRACTOR_FACTORIES_BY_NAME.get(extractorName).createInstance(); + try { + if (extractor.sniff(mExtractorInput)) { + mExtractorName = extractorName; + mExtractor = extractor; + mExtractor.init(new ExtractorOutputAdapter()); + break; + } + } catch (EOFException e) { + // Do nothing. + } catch (IOException | InterruptedException e) { + throw new IllegalStateException(e); + } finally { + mExtractorInput.resetPeekPosition(); + } + } + if (mExtractor == null) { + UnrecognizedInputFormatException.createForExtractors(mExtractorNamesPool); + } + return true; + } + + if (isPendingSeek()) { + mExtractor.seek(mPendingSeekPosition, mPendingSeekTimeUs); + removePendingSeek(); + } + + mPositionHolder.position = seekableInputReader.getPosition(); + int result = mExtractor.read(mExtractorInput, mPositionHolder); + if (result == Extractor.RESULT_END_OF_INPUT) { + return false; + } + if (result == Extractor.RESULT_SEEK) { + mExtractorInput = null; + seekableInputReader.seekToPosition(mPositionHolder.position); + } + return true; } /** * Seeks within the media container being extracted. * - * <p>Following a call to this method, the {@link Input} passed to the next invocation of {@link - * #advance} must provide data starting from {@link SeekPoint#mPosition} in the stream. + * <p>{@link SeekPoint SeekPoints} can be obtained from the {@link SeekMap} passed to {@link + * OutputConsumer#onSeekMap(SeekMap)}. + * + * <p>Following a call to this method, the {@link InputReader} passed to the next invocation of + * {@link #advance} must provide data starting from {@link SeekPoint#position} in the stream. * * @param seekPoint The {@link SeekPoint} to seek to. */ - public void seek(SeekPoint seekPoint) { - throw new UnsupportedOperationException(); + public void seek(@NonNull SeekPoint seekPoint) { + if (mExtractor == null) { + mPendingSeekPosition = seekPoint.position; + mPendingSeekTimeUs = seekPoint.timeUs; + } else { + mExtractor.seek(seekPoint.position, seekPoint.timeUs); + } } /** @@ -386,6 +548,359 @@ public final class MediaParser { * invoked. DESIGN NOTE: Should be removed. There shouldn't be any resource for releasing. */ public void release() { - throw new UnsupportedOperationException(); + mExtractorInput = null; + mExtractor = null; + } + + // Private methods. + + private MediaParser( + OutputConsumer outputConsumer, boolean sniff, String... extractorNamesPool) { + mOutputConsumer = outputConsumer; + mExtractorNamesPool = extractorNamesPool; + if (!sniff) { + mExtractorName = extractorNamesPool[0]; + mExtractor = EXTRACTOR_FACTORIES_BY_NAME.get(mExtractorName).createInstance(); + } + mPositionHolder = new PositionHolder(); + mDataSource = new InputReadingDataSource(); + removePendingSeek(); + mScratchExtractorInputAdapter = new ExtractorInputAdapter(); + mScratchParsableByteArrayAdapter = new ParsableByteArrayAdapter(); + } + + private boolean isPendingSeek() { + return mPendingSeekPosition >= 0; + } + + private void removePendingSeek() { + mPendingSeekPosition = -1; + mPendingSeekTimeUs = -1; + } + + // Private classes. + + private static final class InputReadingDataSource implements DataSource { + + public InputReader mInputReader; + + @Override + public void addTransferListener(TransferListener transferListener) { + // Do nothing. + } + + @Override + public long open(DataSpec dataSpec) { + throw new UnsupportedOperationException(); + } + + @Override + public int read(byte[] buffer, int offset, int readLength) throws IOException { + // TODO: Reevaluate interruption in Input. + try { + return mInputReader.read(buffer, offset, readLength); + } catch (InterruptedException e) { + // TODO: Remove. + throw new RuntimeException(); + } + } + + @Override + public Uri getUri() { + return null; + } + + @Override + public Map<String, List<String>> getResponseHeaders() { + return null; + } + + @Override + public void close() { + throw new UnsupportedOperationException(); + } + } + + private final class ExtractorOutputAdapter implements ExtractorOutput { + + private final SparseArray<TrackOutput> mTrackOutputAdapters; + private boolean mTracksEnded; + + private ExtractorOutputAdapter() { + mTrackOutputAdapters = new SparseArray<>(); + } + + @Override + public TrackOutput track(int id, int type) { + TrackOutput trackOutput = mTrackOutputAdapters.get(id); + if (trackOutput == null) { + trackOutput = new TrackOutputAdapter(mTrackOutputAdapters.size()); + mTrackOutputAdapters.put(id, trackOutput); + } + return trackOutput; + } + + @Override + public void endTracks() { + mOutputConsumer.onTracksFound(mTrackOutputAdapters.size()); + } + + @Override + public void seekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) { + mOutputConsumer.onSeekMap(new ExoToMediaParserSeekMapAdapter(exoplayerSeekMap)); + } + } + + private class TrackOutputAdapter implements TrackOutput { + + private final int mTrackIndex; + + private TrackOutputAdapter(int trackIndex) { + mTrackIndex = trackIndex; + } + + @Override + public void format(Format format) { + mOutputConsumer.onFormat(mTrackIndex, toMediaFormat(format)); + } + + @Override + public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput) + throws IOException, InterruptedException { + mScratchExtractorInputAdapter.setExtractorInput(input, length); + long positionBeforeReading = mScratchExtractorInputAdapter.getPosition(); + mOutputConsumer.onSampleData(mTrackIndex, mScratchExtractorInputAdapter); + return (int) (mScratchExtractorInputAdapter.getPosition() - positionBeforeReading); + } + + @Override + public void sampleData(ParsableByteArray data, int length) { + mScratchParsableByteArrayAdapter.resetWithByteArray(data, length); + try { + mOutputConsumer.onSampleData(mTrackIndex, mScratchParsableByteArrayAdapter); + } catch (IOException | InterruptedException e) { + // Unexpected. + throw new RuntimeException(e); + } + } + + @Override + public void sampleMetadata( + long timeUs, int flags, int size, int offset, CryptoData encryptionData) { + mOutputConsumer.onSampleCompleted( + mTrackIndex, timeUs, flags, size, offset, toCryptoInfo(encryptionData)); + } + } + + private static final class ExtractorInputAdapter implements InputReader { + + private ExtractorInput mExtractorInput; + private int mCurrentPosition; + private long mLength; + + public void setExtractorInput(ExtractorInput extractorInput, long length) { + mExtractorInput = extractorInput; + mCurrentPosition = 0; + mLength = length; + } + + // Input implementation. + + @Override + public int read(byte[] buffer, int offset, int readLength) + throws IOException, InterruptedException { + int readBytes = mExtractorInput.read(buffer, offset, readLength); + mCurrentPosition += readBytes; + return readBytes; + } + + @Override + public long getPosition() { + return mCurrentPosition; + } + + @Override + public long getLength() { + return mLength - mCurrentPosition; + } + } + + private static final class ParsableByteArrayAdapter implements InputReader { + + private ParsableByteArray mByteArray; + private long mLength; + private int mCurrentPosition; + + public void resetWithByteArray(ParsableByteArray byteArray, long length) { + mByteArray = byteArray; + mCurrentPosition = 0; + mLength = length; + } + + // Input implementation. + + @Override + public int read(byte[] buffer, int offset, int readLength) { + mByteArray.readBytes(buffer, offset, readLength); + mCurrentPosition += readLength; + return readLength; + } + + @Override + public long getPosition() { + return mCurrentPosition; + } + + @Override + public long getLength() { + return mLength - mCurrentPosition; + } + } + + /** Creates extractor instances. */ + private interface ExtractorFactory { + + /** Returns a new extractor instance. */ + Extractor createInstance(); + } + + private static class ExoToMediaParserSeekMapAdapter implements SeekMap { + + private final com.google.android.exoplayer2.extractor.SeekMap mExoPlayerSeekMap; + + private ExoToMediaParserSeekMapAdapter( + com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) { + mExoPlayerSeekMap = exoplayerSeekMap; + } + + @Override + public boolean isSeekable() { + return mExoPlayerSeekMap.isSeekable(); + } + + @Override + public long getDurationUs() { + return mExoPlayerSeekMap.getDurationUs(); + } + + @Override + public Pair<SeekPoint, SeekPoint> getSeekPoints(long timeUs) { + SeekPoints seekPoints = mExoPlayerSeekMap.getSeekPoints(timeUs); + return new Pair<>(toSeekPoint(seekPoints.first), toSeekPoint(seekPoints.second)); + } + } + + // Private static methods. + + private static MediaFormat toMediaFormat(Format format) { + + // TODO: Add if (value != Format.NO_VALUE); + + MediaFormat result = new MediaFormat(); + result.setInteger(MediaFormat.KEY_BIT_RATE, format.bitrate); + result.setInteger(MediaFormat.KEY_CHANNEL_COUNT, format.channelCount); + if (format.colorInfo != null) { + result.setInteger(MediaFormat.KEY_COLOR_TRANSFER, format.colorInfo.colorTransfer); + result.setInteger(MediaFormat.KEY_COLOR_RANGE, format.colorInfo.colorRange); + result.setInteger(MediaFormat.KEY_COLOR_STANDARD, format.colorInfo.colorSpace); + if (format.colorInfo.hdrStaticInfo != null) { + result.setByteBuffer( + MediaFormat.KEY_HDR_STATIC_INFO, + ByteBuffer.wrap(format.colorInfo.hdrStaticInfo)); + } + } + result.setString(MediaFormat.KEY_MIME, format.sampleMimeType); + result.setFloat(MediaFormat.KEY_FRAME_RATE, format.frameRate); + result.setInteger(MediaFormat.KEY_WIDTH, format.width); + result.setInteger(MediaFormat.KEY_HEIGHT, format.height); + List<byte[]> initData = format.initializationData; + if (initData != null) { + for (int i = 0; i < initData.size(); i++) { + result.setByteBuffer("csd-" + i, ByteBuffer.wrap(initData.get(i))); + } + } + result.setString(MediaFormat.KEY_LANGUAGE, format.language); + result.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize); + result.setInteger(MediaFormat.KEY_PCM_ENCODING, format.pcmEncoding); + result.setInteger(MediaFormat.KEY_ROTATION, format.rotationDegrees); + result.setInteger(MediaFormat.KEY_SAMPLE_RATE, format.sampleRate); + + int selectionFlags = format.selectionFlags; + // We avoid setting selection flags in the MediaFormat, unless explicitly signaled by the + // extractor. + if ((selectionFlags & C.SELECTION_FLAG_AUTOSELECT) != 0) { + result.setInteger(MediaFormat.KEY_IS_AUTOSELECT, 1); + } + if ((selectionFlags & C.SELECTION_FLAG_DEFAULT) != 0) { + result.setInteger(MediaFormat.KEY_IS_DEFAULT, 1); + } + if ((selectionFlags & C.SELECTION_FLAG_FORCED) != 0) { + result.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, 1); + } + + // LACK OF SUPPORT FOR: + // format.accessibilityChannel; + // format.codecs; + // format.containerMimeType; + // format.drmInitData; + // format.encoderDelay; + // format.encoderPadding; + // format.id; + // format.metadata; + // format.pixelWidthHeightRatio; + // format.roleFlags; + // format.stereoMode; + // format.subsampleOffsetUs; + return result; + } + + private static int toFrameworkFlags(int flags) { + // TODO: Implement. + return 0; + } + + private static MediaCodec.CryptoInfo toCryptoInfo(TrackOutput.CryptoData encryptionData) { + // TODO: Implement. + return null; + } + + /** Returns a new {@link SeekPoint} equivalent to the given {@code exoPlayerSeekPoint}. */ + private static SeekPoint toSeekPoint( + com.google.android.exoplayer2.extractor.SeekPoint exoPlayerSeekPoint) { + return new SeekPoint(exoPlayerSeekPoint.timeUs, exoPlayerSeekPoint.position); + } + + private static void assertValidNames(@NonNull String[] names) { + for (String name : names) { + if (!EXTRACTOR_FACTORIES_BY_NAME.containsKey(name)) { + throw new IllegalArgumentException( + "Invalid extractor name: " + + name + + ". Supported extractors are: " + + TextUtils.join(", ", EXTRACTOR_FACTORIES_BY_NAME.keySet()) + + "."); + } + } + } + + // Static initialization. + + static { + // Using a LinkedHashMap to keep the insertion order when iterating over the keys. + LinkedHashMap<String, ExtractorFactory> extractorFactoriesByName = new LinkedHashMap<>(); + extractorFactoriesByName.put("exo.Ac3Extractor", Ac3Extractor::new); + extractorFactoriesByName.put("exo.Ac4Extractor", Ac4Extractor::new); + extractorFactoriesByName.put("exo.AdtsExtractor", AdtsExtractor::new); + extractorFactoriesByName.put("exo.AmrExtractor", AmrExtractor::new); + extractorFactoriesByName.put("exo.FlvExtractor", FlvExtractor::new); + extractorFactoriesByName.put("exo.FragmentedMp4Extractor", FragmentedMp4Extractor::new); + extractorFactoriesByName.put("exo.MatroskaExtractor", MatroskaExtractor::new); + extractorFactoriesByName.put("exo.Mp3Extractor", Mp3Extractor::new); + extractorFactoriesByName.put("exo.Mp4Extractor", Mp4Extractor::new); + extractorFactoriesByName.put("exo.OggExtractor", OggExtractor::new); + extractorFactoriesByName.put("exo.PsExtractor", PsExtractor::new); + extractorFactoriesByName.put("exo.TsExtractor", TsExtractor::new); + extractorFactoriesByName.put("exo.WavExtractor", WavExtractor::new); + EXTRACTOR_FACTORIES_BY_NAME = Collections.unmodifiableMap(extractorFactoriesByName); } } diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 2496d44f1488..c8532e006489 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -119,11 +119,11 @@ import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.StatusBarComponent; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter; import com.android.systemui.statusbar.phone.StatusBarWindowController; -import com.android.systemui.statusbar.phone.StatusBarWindowViewController; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -140,6 +140,7 @@ import java.util.Map; import java.util.Optional; import javax.inject.Named; +import javax.inject.Provider; import dagger.Lazy; @@ -156,7 +157,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt private static final float FLING_SPEED_UP_FACTOR = 0.6f; private final ScrimController mScrimController; - private final StatusBarWindowViewController mStatusBarWindowViewController; private final LockscreenLockIconController mLockscreenLockIconController; private float mOpeningVelocity = DEFAULT_FLING_VELOCITY; @@ -288,7 +288,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt NotificationListener notificationListener, ConfigurationController configurationController, StatusBarWindowController statusBarWindowController, - StatusBarWindowViewController statusBarWindowViewController, LockscreenLockIconController lockscreenLockIconController, DozeParameters dozeParameters, ScrimController scrimController, @@ -301,6 +300,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt VolumeComponent volumeComponent, CommandQueue commandQueue, Optional<Recents> recents, + Provider<StatusBarComponent.Builder> statusBarComponentBuilder, PluginManager pluginManager, RemoteInputUriController remoteInputUriController, Optional<Divider> dividerOptional, @@ -366,7 +366,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt notificationListener, configurationController, statusBarWindowController, - statusBarWindowViewController, lockscreenLockIconController, dozeParameters, scrimController, @@ -380,6 +379,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt volumeComponent, commandQueue, recents, + statusBarComponentBuilder, pluginManager, remoteInputUriController, dividerOptional, @@ -390,7 +390,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt viewMediatorCallback, dismissCallbackRegistry); mScrimController = scrimController; - mStatusBarWindowViewController = statusBarWindowViewController; mLockscreenLockIconController = lockscreenLockIconController; mDeviceProvisionedController = deviceProvisionedController; mCarServiceProvider = carServiceProvider; diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java index 324c6e41e339..eff60fa246a4 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java @@ -78,11 +78,11 @@ import com.android.systemui.statusbar.phone.LockscreenWallpaper; import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ScrimController; +import com.android.systemui.statusbar.phone.StatusBarComponent; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter; import com.android.systemui.statusbar.phone.StatusBarWindowController; -import com.android.systemui.statusbar.phone.StatusBarWindowViewController; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -96,6 +96,7 @@ import com.android.systemui.volume.VolumeComponent; import java.util.Optional; import javax.inject.Named; +import javax.inject.Provider; import javax.inject.Singleton; import dagger.Lazy; @@ -162,7 +163,6 @@ public class CarStatusBarModule { NotificationListener notificationListener, ConfigurationController configurationController, StatusBarWindowController statusBarWindowController, - StatusBarWindowViewController statusBarWindowViewController, LockscreenLockIconController lockscreenLockIconController, DozeParameters dozeParameters, ScrimController scrimController, @@ -175,6 +175,7 @@ public class CarStatusBarModule { VolumeComponent volumeComponent, CommandQueue commandQueue, Optional<Recents> recentsOptional, + Provider<StatusBarComponent.Builder> statusBarComponentBuilder, PluginManager pluginManager, RemoteInputUriController remoteInputUriController, Optional<Divider> dividerOptional, @@ -239,7 +240,6 @@ public class CarStatusBarModule { notificationListener, configurationController, statusBarWindowController, - statusBarWindowViewController, lockscreenLockIconController, dozeParameters, scrimController, @@ -252,6 +252,7 @@ public class CarStatusBarModule { volumeComponent, commandQueue, recentsOptional, + statusBarComponentBuilder, pluginManager, remoteInputUriController, dividerOptional, diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java index 785dd561d1b7..96aee512c090 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java @@ -44,8 +44,8 @@ public class A2dpProfile implements LocalBluetoothProfile { private final CachedBluetoothDeviceManager mDeviceManager; static final ParcelUuid[] SINK_UUIDS = { - BluetoothUuid.AudioSink, - BluetoothUuid.AdvAudioDist, + BluetoothUuid.A2DP_SINK, + BluetoothUuid.ADV_AUDIO_DIST, }; static final String NAME = "A2DP"; diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java index 4ce9d3e2dff2..55765dd40d36 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java @@ -40,8 +40,8 @@ final class A2dpSinkProfile implements LocalBluetoothProfile { private final CachedBluetoothDeviceManager mDeviceManager; static final ParcelUuid[] SRC_UUIDS = { - BluetoothUuid.AudioSource, - BluetoothUuid.AdvAudioDist, + BluetoothUuid.A2DP_SOURCE, + BluetoothUuid.ADV_AUDIO_DIST, }; static final String NAME = "A2DPSink"; diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java index 8dec86ac3614..b8ad321d6dd3 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java @@ -22,6 +22,8 @@ import android.bluetooth.BluetoothUuid; import android.os.ParcelUuid; import android.util.Log; +import com.android.internal.util.ArrayUtils; + /** * BluetoothDeviceFilter contains a static method that returns a * Filter object that returns whether or not the BluetoothDevice @@ -130,7 +132,7 @@ public final class BluetoothDeviceFilter { @Override boolean matches(ParcelUuid[] uuids, BluetoothClass btClass) { if (uuids != null) { - if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush)) { + if (ArrayUtils.contains(uuids, BluetoothUuid.OBEX_OBJECT_PUSH)) { return true; } } @@ -144,7 +146,7 @@ public final class BluetoothDeviceFilter { @Override boolean matches(ParcelUuid[] uuids, BluetoothClass btClass) { if (uuids != null) { - if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.PANU)) { + if (ArrayUtils.contains(uuids, BluetoothUuid.PANU)) { return true; } } @@ -158,7 +160,7 @@ public final class BluetoothDeviceFilter { @Override boolean matches(ParcelUuid[] uuids, BluetoothClass btClass) { if (uuids != null) { - if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.NAP)) { + if (ArrayUtils.contains(uuids, BluetoothUuid.NAP)) { return true; } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 833c4ac07633..066659677add 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -35,6 +35,7 @@ import android.util.Log; import androidx.annotation.VisibleForTesting; +import com.android.internal.util.ArrayUtils; import com.android.settingslib.R; import com.android.settingslib.Utils; @@ -685,9 +686,9 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> ParcelUuid[] uuids = mDevice.getUuids(); long timeout = MAX_UUID_DELAY_FOR_AUTO_CONNECT; - if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hogp)) { + if (ArrayUtils.contains(uuids, BluetoothUuid.HOGP)) { timeout = MAX_HOGP_DELAY_FOR_AUTO_CONNECT; - } else if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid)) { + } else if (ArrayUtils.contains(uuids, BluetoothUuid.HEARING_AID)) { timeout = MAX_HEARING_AIDS_DELAY_FOR_AUTO_CONNECT; } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java index c1933fd87633..9f7b7181b19f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java @@ -45,7 +45,7 @@ public class HeadsetProfile implements LocalBluetoothProfile { static final ParcelUuid[] UUIDS = { BluetoothUuid.HSP, - BluetoothUuid.Handsfree, + BluetoothUuid.HFP, }; static final String NAME = "HEADSET"; diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java index 4bdbc3196556..860b77d1ebcd 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java @@ -44,7 +44,7 @@ final class HfpClientProfile implements LocalBluetoothProfile { static final ParcelUuid[] SRC_UUIDS = { BluetoothUuid.HSP_AG, - BluetoothUuid.Handsfree_AG, + BluetoothUuid.HFP_AG, }; static final String NAME = "HEADSET_CLIENT"; diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java index 29c6d719641a..ae2acbea8e4d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java @@ -30,8 +30,8 @@ import android.bluetooth.BluetoothMapClient; import android.bluetooth.BluetoothPan; import android.bluetooth.BluetoothPbap; import android.bluetooth.BluetoothPbapClient; -import android.bluetooth.BluetoothSap; import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothSap; import android.bluetooth.BluetoothUuid; import android.content.Context; import android.content.Intent; @@ -40,6 +40,7 @@ import android.util.Log; import androidx.annotation.VisibleForTesting; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; import java.util.ArrayList; @@ -471,43 +472,40 @@ public class LocalBluetoothProfileManager { } if (mHeadsetProfile != null) { - if ((BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.HSP_AG) && - BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP)) || - (BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.Handsfree_AG) && - BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree))) { + if ((ArrayUtils.contains(localUuids, BluetoothUuid.HSP_AG) + && ArrayUtils.contains(uuids, BluetoothUuid.HSP)) + || (ArrayUtils.contains(localUuids, BluetoothUuid.HFP_AG) + && ArrayUtils.contains(uuids, BluetoothUuid.HFP))) { profiles.add(mHeadsetProfile); removedProfiles.remove(mHeadsetProfile); } } if ((mHfpClientProfile != null) && - BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree_AG) && - BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.Handsfree)) { + ArrayUtils.contains(uuids, BluetoothUuid.HFP_AG) + && ArrayUtils.contains(localUuids, BluetoothUuid.HFP)) { profiles.add(mHfpClientProfile); removedProfiles.remove(mHfpClientProfile); } - if (BluetoothUuid.containsAnyUuid(uuids, A2dpProfile.SINK_UUIDS) && - mA2dpProfile != null) { + if (BluetoothUuid.containsAnyUuid(uuids, A2dpProfile.SINK_UUIDS) && mA2dpProfile != null) { profiles.add(mA2dpProfile); removedProfiles.remove(mA2dpProfile); } - if (BluetoothUuid.containsAnyUuid(uuids, A2dpSinkProfile.SRC_UUIDS) && - mA2dpSinkProfile != null) { + if (BluetoothUuid.containsAnyUuid(uuids, A2dpSinkProfile.SRC_UUIDS) + && mA2dpSinkProfile != null) { profiles.add(mA2dpSinkProfile); removedProfiles.remove(mA2dpSinkProfile); } - if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush) && - mOppProfile != null) { + if (ArrayUtils.contains(uuids, BluetoothUuid.OBEX_OBJECT_PUSH) && mOppProfile != null) { profiles.add(mOppProfile); removedProfiles.remove(mOppProfile); } - if ((BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hid) || - BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hogp)) && - mHidProfile != null) { + if ((ArrayUtils.contains(uuids, BluetoothUuid.HID) + || ArrayUtils.contains(uuids, BluetoothUuid.HOGP)) && mHidProfile != null) { profiles.add(mHidProfile); removedProfiles.remove(mHidProfile); } @@ -520,8 +518,8 @@ public class LocalBluetoothProfileManager { if(isPanNapConnected) if(DEBUG) Log.d(TAG, "Valid PAN-NAP connection exists."); - if ((BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.NAP) && - mPanProfile != null) || isPanNapConnected) { + if ((ArrayUtils.contains(uuids, BluetoothUuid.NAP) && mPanProfile != null) + || isPanNapConnected) { profiles.add(mPanProfile); removedProfiles.remove(mPanProfile); } @@ -545,20 +543,18 @@ public class LocalBluetoothProfileManager { removedProfiles.remove(mMapClientProfile); } - if ((mPbapClientProfile != null) && - BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.PBAP_PCE) && - BluetoothUuid.containsAnyUuid(uuids, PbapClientProfile.SRC_UUIDS)) { + if ((mPbapClientProfile != null) && ArrayUtils.contains(localUuids, BluetoothUuid.PBAP_PCE) + && BluetoothUuid.containsAnyUuid(uuids, PbapClientProfile.SRC_UUIDS)) { profiles.add(mPbapClientProfile); removedProfiles.remove(mPbapClientProfile); } - if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid) && - mHearingAidProfile != null) { + if (ArrayUtils.contains(uuids, BluetoothUuid.HEARING_AID) && mHearingAidProfile != null) { profiles.add(mHearingAidProfile); removedProfiles.remove(mHearingAidProfile); } - if (mSapProfile != null && BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.SAP)) { + if (mSapProfile != null && ArrayUtils.contains(uuids, BluetoothUuid.SAP)) { profiles.add(mSapProfile); removedProfiles.remove(mSapProfile); } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java index 17104e4e96cd..d91226e02878 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java @@ -46,7 +46,7 @@ public class PbapServerProfile implements LocalBluetoothProfile { // The UUIDs indicate that remote device might access pbap server static final ParcelUuid[] PBAB_CLIENT_UUIDS = { BluetoothUuid.HSP, - BluetoothUuid.Handsfree, + BluetoothUuid.HFP, BluetoothUuid.PBAP_PCE }; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java index 5d5872ea2354..fd5b05311247 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java @@ -132,7 +132,7 @@ public class LocalBluetoothProfileManagerTest { mShadowBluetoothAdapter.setSupportedProfiles(generateList( new int[] {BluetoothProfile.HID_HOST})); mProfileManager.updateLocalProfiles(); - ParcelUuid[] uuids = new ParcelUuid[]{BluetoothUuid.Hid}; + ParcelUuid[] uuids = new ParcelUuid[]{BluetoothUuid.HID}; ParcelUuid[] localUuids = new ParcelUuid[]{}; List<LocalBluetoothProfile> profiles = new ArrayList<>(); List<LocalBluetoothProfile> removedProfiles = new ArrayList<>(); diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java index 1527de1a1d17..1f687420a377 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java @@ -70,5 +70,7 @@ public class GlobalSettings { Settings.Global.CHARGING_VIBRATION_ENABLED, Settings.Global.AWARE_ALLOWED, Settings.Global.NOTIFICATION_BUBBLES, + Settings.Global.CUSTOM_BUGREPORT_HANDLER_APP, + Settings.Global.CUSTOM_BUGREPORT_HANDLER_USER, }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java index 4a0ed6f91ed3..3d278db800a6 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java @@ -149,5 +149,7 @@ public class GlobalSettingsValidators { VALIDATORS.put( Global.POWER_BUTTON_VERY_LONG_PRESS, new InclusiveIntegerRangeValidator(0, 1)); VALIDATORS.put(Global.NOTIFICATION_BUBBLES, BOOLEAN_VALIDATOR); + VALIDATORS.put(Global.CUSTOM_BUGREPORT_HANDLER_APP, ANY_STRING_VALIDATOR); + VALIDATORS.put(Global.CUSTOM_BUGREPORT_HANDLER_USER, ANY_INTEGER_VALIDATOR); } } diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 540726bddf5e..c23a494d3312 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -268,6 +268,7 @@ public class SettingsBackupTest { Settings.Global.ERROR_LOGCAT_PREFIX, Settings.Global.EUICC_PROVISIONED, Settings.Global.EUICC_SUPPORTED_COUNTRIES, + Settings.Global.EUICC_UNSUPPORTED_COUNTRIES, Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS, Settings.Global.EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS, Settings.Global.FANCY_IME_ANIMATIONS, diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 0a671d9185f0..6f6803817138 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -288,6 +288,11 @@ android:exported="false" android:permission="com.android.systemui.permission.SELF" /> + <service android:name=".assist.AssistHandleService" + android:exported="true" + android:enabled="false" + /> + <!-- started from PhoneWindowManager TODO: Should have an android:permission attribute --> <service android:name=".screenshot.TakeScreenshotService" diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java index 493186b2bd85..41dd5bbf272b 100644 --- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java +++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java @@ -139,6 +139,8 @@ public class ForegroundServiceController { } } + // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by + // ForegroundCoordinator // Update appOp if there's an associated pending or visible notification: final String foregroundKey = getStandardLayoutKey(userId, packageName); if (foregroundKey != null) { diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java index c4e276277df5..8105faa23e89 100644 --- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java +++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java @@ -52,6 +52,9 @@ public class ForegroundServiceNotificationListener { NotifCollection notifCollection) { mContext = context; mForegroundServiceController = foregroundServiceController; + + // TODO: (b/145659174) remove mEntryManager when moving to NewNotifPipeline. Replaced by + // ForegroundCoordinator mEntryManager = notificationEntryManager; mEntryManager.addNotificationEntryListener(new NotificationEntryListener() { @Override @@ -171,8 +174,8 @@ public class ForegroundServiceNotificationListener { true /* create if not found */); } - // TODO: remove this when fully migrated to the NewNotifPipeline (work done in - // ForegroundCoordinator) + // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by + // ForegroundCoordinator private void tagForeground(NotificationEntry entry) { final StatusBarNotification sbn = entry.getSbn(); ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps( diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleService.kt b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleService.kt new file mode 100644 index 000000000000..9ceafc674d34 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleService.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.assist + +import android.app.Service +import android.content.Intent +import android.os.IBinder +import dagger.Lazy +import javax.inject.Inject + +class AssistHandleService @Inject constructor(private val assistManager: Lazy<AssistManager>) + : Service() { + + private val binder = object : IAssistHandleService.Stub() { + override fun requestAssistHandles() { + assistManager.get().requestAssistHandles() + } + } + + override fun onBind(intent: Intent?): IBinder? { + return binder + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java index 6f5a17dca432..96939b010555 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java @@ -16,6 +16,7 @@ package com.android.systemui.assist; +import android.app.Service; import android.content.Context; import android.os.Handler; import android.os.HandlerThread; @@ -33,8 +34,11 @@ import java.util.Map; import javax.inject.Named; import javax.inject.Singleton; +import dagger.Binds; import dagger.Module; import dagger.Provides; +import dagger.multibindings.ClassKey; +import dagger.multibindings.IntoMap; /** Module for dagger injections related to the Assistant. */ @Module @@ -87,4 +91,9 @@ public abstract class AssistModule { static Clock provideSystemClock() { return SystemClock::uptimeMillis; } + + @Binds + @IntoMap + @ClassKey(AssistHandleService.class) + abstract Service bindAssistHandleService(AssistHandleService assistHandleService); } diff --git a/packages/SystemUI/src/com/android/systemui/assist/IAssistHandleService.aidl b/packages/SystemUI/src/com/android/systemui/assist/IAssistHandleService.aidl new file mode 100644 index 000000000000..ef07d9d3f182 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/assist/IAssistHandleService.aidl @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2009, 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.assist; + +/** Interface implemented by AssisthandleService and called by on-device intelligence. */ +interface IAssistHandleService { + + /** Request that the Assistant Handles be shown. */ + oneway void requestAssistHandles(); +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java index 24ee969526cc..46d060e5ebda 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -199,7 +199,7 @@ class Bubble { mExpandedView = (BubbleExpandedView) inflater.inflate( R.layout.bubble_expanded_view, stackView, false /* attachToRoot */); - mExpandedView.setBubble(this, stackView, mAppName); + mExpandedView.setBubble(this, stackView); mInflated = true; } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index ed21e141c12d..15bb3305ea52 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -700,10 +700,11 @@ public class BubbleController implements ConfigurationController.ConfigurationLi @Override public void onPendingEntryAdded(NotificationEntry entry) { boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey()); - BubbleExperimentConfig.adjustForExperiments(mContext, entry, previouslyUserCreated); + boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments( + mContext, entry, previouslyUserCreated); if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry) - && canLaunchInActivityView(mContext, entry)) { + && (canLaunchInActivityView(mContext, entry) || wasAdjusted)) { updateBubble(entry); } } @@ -711,10 +712,11 @@ public class BubbleController implements ConfigurationController.ConfigurationLi @Override public void onPreEntryUpdated(NotificationEntry entry) { boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey()); - BubbleExperimentConfig.adjustForExperiments(mContext, entry, previouslyUserCreated); + boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments( + mContext, entry, previouslyUserCreated); boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry) - && canLaunchInActivityView(mContext, entry); + && (canLaunchInActivityView(mContext, entry) || wasAdjusted); if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) { // It was previously a bubble but no longer a bubble -- lets remove it removeBubble(entry.getKey(), DISMISS_NO_LONGER_BUBBLE); @@ -1022,11 +1024,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi PendingIntent intent = entry.getBubbleMetadata() != null ? entry.getBubbleMetadata().getIntent() : null; - return canLaunchIntentInActivityView(context, entry, intent); - } - - static boolean canLaunchIntentInActivityView(Context context, NotificationEntry entry, - PendingIntent intent) { if (intent == null) { Log.w(TAG, "Unable to create bubble -- no intent: " + entry.getKey()); return false; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index 512b38e895bb..856b15e7135d 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -31,15 +31,12 @@ import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; -import android.graphics.drawable.Drawable; import android.graphics.drawable.ShapeDrawable; import android.os.RemoteException; import android.service.notification.StatusBarNotification; @@ -102,9 +99,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList private int mExpandedViewTouchSlop; private Bubble mBubble; - private PackageManager mPm; private String mAppName; - private Drawable mAppIcon; private BubbleController mBubbleController = Dependency.get(BubbleController.class); private WindowManager mWindowManager; @@ -212,7 +207,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - mPm = context.getPackageManager(); mDisplaySize = new Point(); mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); // Get the real size -- this includes screen decorations (notches, statusbar, navbar). @@ -350,29 +344,14 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList /** * Sets the bubble used to populate this view. */ - public void setBubble(Bubble bubble, BubbleStackView stackView, String appName) { + public void setBubble(Bubble bubble, BubbleStackView stackView) { if (DEBUG_BUBBLE_EXPANDED_VIEW) { Log.d(TAG, "setBubble: bubble=" + (bubble != null ? bubble.getKey() : "null")); } - mStackView = stackView; mBubble = bubble; - mAppName = appName; - - try { - ApplicationInfo info = mPm.getApplicationInfo( - bubble.getPackageName(), - PackageManager.MATCH_UNINSTALLED_PACKAGES - | PackageManager.MATCH_DISABLED_COMPONENTS - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE - | PackageManager.MATCH_DIRECT_BOOT_AWARE); - mAppIcon = mPm.getApplicationIcon(info); - } catch (PackageManager.NameNotFoundException e) { - // Do nothing. - } - if (mAppIcon == null) { - mAppIcon = mPm.getDefaultActivityIcon(); - } + mAppName = bubble.getAppName(); + applyThemeAttrs(); showSettingsIcon(); updateExpandedView(); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java index 17d6737b04d6..fd7fff4112c8 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java @@ -21,7 +21,6 @@ import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC; import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_MANIFEST; import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED; -import static com.android.systemui.bubbles.BubbleController.canLaunchIntentInActivityView; import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_EXPERIMENTS; import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; @@ -62,7 +61,7 @@ public class BubbleExperimentConfig { private static final boolean ALLOW_ANY_NOTIF_TO_BUBBLE_DEFAULT = false; private static final String ALLOW_MESSAGE_NOTIFS_TO_BUBBLE = "allow_message_notifs_to_bubble"; - private static final boolean ALLOW_MESSAGE_NOTIFS_TO_BUBBLE_DEFAULT = false; + private static final boolean ALLOW_MESSAGE_NOTIFS_TO_BUBBLE_DEFAULT = true; private static final String ALLOW_SHORTCUTS_TO_BUBBLE = "allow_shortcuts_to_bubble"; private static final boolean ALLOW_SHORTCUT_TO_BUBBLE_DEFAULT = false; @@ -107,8 +106,10 @@ public class BubbleExperimentConfig { * If {@link #allowAnyNotifToBubble(Context)} is true, this method creates and adds * {@link android.app.Notification.BubbleMetadata} to the notification entry as long as * the notification has necessary info for BubbleMetadata. + * + * @return whether an adjustment was made. */ - static void adjustForExperiments(Context context, NotificationEntry entry, + static boolean adjustForExperiments(Context context, NotificationEntry entry, boolean previouslyUserCreated) { Notification.BubbleMetadata metadata = null; boolean addedMetadata = false; @@ -176,7 +177,9 @@ public class BubbleExperimentConfig { Log.d(TAG, "Setting FLAG_BUBBLE for: " + entry.getKey()); } entry.setFlagBubble(true); + return true; } + return addedMetadata; } static Notification.BubbleMetadata createFromNotif(Context context, NotificationEntry entry) { @@ -193,7 +196,7 @@ public class BubbleExperimentConfig { ? notification.getLargeIcon() : notification.getSmallIcon(); } - if (canLaunchIntentInActivityView(context, entry, intent)) { + if (intent != null) { return new Notification.BubbleMetadata.Builder() .setDesiredHeight(BUBBLE_HEIGHT) .setIcon(icon) diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 9bd729edd210..442313d763ec 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -35,7 +35,9 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.NotifL import com.android.systemui.statusbar.notification.people.PeopleHubModule; import com.android.systemui.statusbar.phone.KeyguardLiftController; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.StatusBarComponent; import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.util.concurrency.ConcurrencyModule; import com.android.systemui.util.sensors.AsyncSensorManager; import com.android.systemui.util.time.SystemClock; import com.android.systemui.util.time.SystemClockImpl; @@ -52,7 +54,9 @@ import dagger.Provides; * implementation. */ @Module(includes = {AssistModule.class, - PeopleHubModule.class}) + ConcurrencyModule.class, + PeopleHubModule.class}, + subcomponents = {StatusBarComponent.class}) public abstract class SystemUIModule { @Binds diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java index e926574977d0..e50e0fe0d224 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java @@ -25,7 +25,6 @@ import com.android.systemui.Dependency; import com.android.systemui.SystemUIAppComponentFactory; import com.android.systemui.SystemUIFactory; import com.android.systemui.fragments.FragmentService; -import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.InjectionInflationController; @@ -73,12 +72,6 @@ public interface SystemUIRootComponent { Dependency.DependencyInjector createDependency(); /** - * Injects the StatusBar. - */ - @Singleton - StatusBar.StatusBarInjector getStatusBarInjector(); - - /** * FragmentCreator generates all Fragments that need injection. */ @Singleton diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Background.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Background.java new file mode 100644 index 000000000000..141c9019b3e4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Background.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dagger.qualifiers; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface Background { +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Main.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Main.java new file mode 100644 index 000000000000..7b097740ff11 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Main.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dagger.qualifiers; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface Main { +} diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index b58459470f3f..63a777154254 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -669,7 +669,10 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, // Take an "interactive" bugreport. MetricsLogger.action(mContext, MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_INTERACTIVE); - ActivityManager.getService().requestInteractiveBugReport(); + if (!ActivityManager.getService().launchBugReportHandlerApp()) { + Log.w(TAG, "Bugreport handler could not be launched"); + ActivityManager.getService().requestInteractiveBugReport(); + } } catch (RemoteException e) { } } diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java index 99c55f13a8a3..f815b5d476ec 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java @@ -79,12 +79,6 @@ class ImageRevealHelper { }); } - private void animate() { - mAnimator.cancel(); - mAnimator.setFloatValues(mReveal, mAwake ? MAX_REVEAL : MIN_REVEAL); - mAnimator.start(); - } - public float getReveal() { return mReveal; } @@ -93,8 +87,8 @@ class ImageRevealHelper { if (DEBUG) { Log.d(TAG, "updateAwake: awake=" + awake + ", duration=" + duration); } + mAnimator.cancel(); mAwake = awake; - mAnimator.setDuration(duration); if (duration == 0) { // We are transiting from home to aod or aod to home directly, // we don't need to do transition in these cases. @@ -103,7 +97,9 @@ class ImageRevealHelper { mRevealListener.onRevealStateChanged(); mRevealListener.onRevealEnd(); } else { - animate(); + mAnimator.setDuration(duration); + mAnimator.setFloatValues(mReveal, mAwake ? MAX_REVEAL : MIN_REVEAL); + mAnimator.start(); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java index 0a9100f6c7d5..f30c181b3c99 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java @@ -25,7 +25,6 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.os.Build; -import android.os.Handler; import android.provider.Settings; import android.service.quicksettings.Tile; import android.service.quicksettings.TileService; @@ -34,8 +33,8 @@ import android.util.ArraySet; import android.widget.Button; import com.android.systemui.R; -import com.android.systemui.dagger.qualifiers.BgHandler; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTile.State; import com.android.systemui.qs.QSTileHost; @@ -47,6 +46,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.concurrent.Executor; import javax.inject.Inject; @@ -55,8 +55,8 @@ public class TileQueryHelper { private final ArrayList<TileInfo> mTiles = new ArrayList<>(); private final ArraySet<String> mSpecs = new ArraySet<>(); - private final Handler mBgHandler; - private final Handler mMainHandler; + private final Executor mMainExecutor; + private final Executor mBgExecutor; private final Context mContext; private TileStateListener mListener; @@ -64,10 +64,10 @@ public class TileQueryHelper { @Inject public TileQueryHelper(Context context, - @MainHandler Handler mainHandler, @BgHandler Handler bgHandler) { + @Main Executor mainExecutor, @Background Executor bgExecutor) { mContext = context; - mMainHandler = mainHandler; - mBgHandler = bgHandler; + mMainExecutor = mainExecutor; + mBgExecutor = bgExecutor; } public void setListener(TileStateListener listener) { @@ -126,7 +126,7 @@ public class TileQueryHelper { tilesToAdd.add(tile); } - mBgHandler.post(() -> { + mBgExecutor.execute(() -> { for (QSTile tile : tilesToAdd) { final QSTile.State state = tile.getState().copy(); // Ignore the current state and get the generic label instead. @@ -139,7 +139,7 @@ public class TileQueryHelper { } private void addPackageTiles(final QSTileHost host) { - mBgHandler.post(() -> { + mBgExecutor.execute(() -> { Collection<QSTile> params = host.getTiles(); PackageManager pm = mContext.getPackageManager(); List<ResolveInfo> services = pm.queryIntentServicesAsUser( @@ -185,7 +185,7 @@ public class TileQueryHelper { private void notifyTilesChanged(final boolean finished) { final ArrayList<TileInfo> tilesToReturn = new ArrayList<>(mTiles); - mMainHandler.post(() -> { + mMainExecutor.execute(() -> { if (mListener != null) { mListener.onTilesChanged(tilesToReturn); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java index fcdd23463c5b..f3e2f104621e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java @@ -162,6 +162,8 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation { if (runningTask.supportsSplitScreenMultiWindow) { if (ActivityManagerWrapper.getInstance().setTaskWindowingModeSplitScreenPrimary( runningTask.id, stackCreateMode, initialBounds)) { + mDividerOptional.ifPresent(Divider::onDockedTopTask); + // The overview service is handling split screen, so just skip the wait for the // first draw and notify the divider to start animating now mDividerOptional.ifPresent(Divider::onRecentsDrawn); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java index 341c49a87156..2005d794c9d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java @@ -17,14 +17,13 @@ package com.android.systemui.statusbar; import android.annotation.NonNull; -import android.os.Handler; -import android.os.HandlerExecutor; import android.provider.DeviceConfig; import android.util.ArrayMap; -import com.android.systemui.dagger.qualifiers.BgHandler; +import com.android.systemui.dagger.qualifiers.Background; import java.util.Map; +import java.util.concurrent.Executor; import javax.inject.Inject; import javax.inject.Singleton; @@ -49,10 +48,10 @@ public class FeatureFlags { private final Map<String, Boolean> mCachedDeviceConfigFlags = new ArrayMap<>(); @Inject - public FeatureFlags(@BgHandler Handler bgHandler) { + public FeatureFlags(@Background Executor executor) { DeviceConfig.addOnPropertiesChangedListener( "systemui", - new HandlerExecutor(bgHandler), + executor, this::onPropertiesChanged); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index a98f826c0284..d3a9c2c3c87d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -62,6 +62,7 @@ import com.android.systemui.statusbar.phone.LockscreenWallpaper; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ScrimState; import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarWindowController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -122,6 +123,7 @@ public class NotificationMediaManager implements Dumpable { private final Context mContext; private final MediaSessionManager mMediaSessionManager; private final ArrayList<MediaListener> mMediaListeners; + private final Lazy<StatusBar> mStatusBarLazy; private final MediaArtworkProcessor mMediaArtworkProcessor; private final Set<AsyncTask<?, ?, ?>> mProcessArtworkTasks = new ArraySet<>(); @@ -182,6 +184,7 @@ public class NotificationMediaManager implements Dumpable { public NotificationMediaManager( Context context, Lazy<ShadeController> shadeController, + Lazy<StatusBar> statusBarLazy, Lazy<StatusBarWindowController> statusBarWindowController, NotificationEntryManager notificationEntryManager, MediaArtworkProcessor mediaArtworkProcessor, @@ -195,6 +198,8 @@ public class NotificationMediaManager implements Dumpable { // TODO: use MediaSessionManager.SessionListener to hook us up to future updates // in session state mShadeController = shadeController; + // TODO: use KeyguardStateController#isOccluded to remove this dependency + mStatusBarLazy = statusBarLazy; mStatusBarWindowController = statusBarWindowController; mEntryManager = notificationEntryManager; notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() { @@ -526,9 +531,8 @@ public class NotificationMediaManager implements Dumpable { } } - ShadeController shadeController = mShadeController.get(); StatusBarWindowController windowController = mStatusBarWindowController.get(); - boolean hideBecauseOccluded = shadeController != null && shadeController.isOccluded(); + boolean hideBecauseOccluded = mStatusBarLazy.get().isOccluded(); final boolean hasArtwork = artworkDrawable != null; mColorExtractor.setHasMediaArtwork(hasMediaArtwork); @@ -599,6 +603,7 @@ public class NotificationMediaManager implements Dumpable { if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork"); } + ShadeController shadeController = mShadeController.get(); boolean cannotAnimateDoze = shadeController != null && shadeController.isDozing() && !ScrimState.AOD.getAnimateChange(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java index 3f7fd1a4868c..7b1dc074fcbc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java @@ -64,6 +64,8 @@ public class NotificationListController { } }; + // TODO: (b/145659174) remove after moving to NewNotifPipeline. Replaced by + // DeviceProvisionedCoordinator private final DeviceProvisionedListener mDeviceProvisionedListener = new DeviceProvisionedListener() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt index 8bce528bab8c..48a4882bcf82 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt @@ -16,37 +16,29 @@ package com.android.systemui.statusbar.notification.collection -import android.app.Notification import android.app.NotificationManager.IMPORTANCE_DEFAULT import android.app.NotificationManager.IMPORTANCE_HIGH import android.app.NotificationManager.IMPORTANCE_LOW import android.app.NotificationManager.IMPORTANCE_MIN -import android.app.Person import android.service.notification.NotificationListenerService.Ranking import android.service.notification.NotificationListenerService.RankingMap import android.service.notification.StatusBarNotification import com.android.internal.annotations.VisibleForTesting - import com.android.systemui.statusbar.NotificationMediaManager import com.android.systemui.statusbar.notification.NotificationFilter import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager import com.android.systemui.statusbar.notification.logging.NotifEvent import com.android.systemui.statusbar.notification.logging.NotifLog +import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT import com.android.systemui.statusbar.phone.NotificationGroupManager import com.android.systemui.statusbar.policy.HeadsUpManager - +import dagger.Lazy import java.util.Objects -import java.util.ArrayList - import javax.inject.Inject -import kotlin.Comparator - -import dagger.Lazy - private const val TAG = "NotifRankingManager" /** @@ -64,7 +56,8 @@ open class NotificationRankingManager @Inject constructor( private val headsUpManager: HeadsUpManager, private val notifFilter: NotificationFilter, private val notifLog: NotifLog, - sectionsFeatureManager: NotificationSectionsFeatureManager + sectionsFeatureManager: NotificationSectionsFeatureManager, + private val peopleNotificationIdentifier: PeopleNotificationIdentifier ) { var rankingMap: RankingMap? = null @@ -79,6 +72,9 @@ open class NotificationRankingManager @Inject constructor( val aRank = a.ranking.rank val bRank = b.ranking.rank + val aIsPeople = a.isPeopleNotification() + val bIsPeople = b.isPeopleNotification() + val aMedia = isImportantMedia(a) val bMedia = isImportantMedia(b) @@ -88,25 +84,19 @@ open class NotificationRankingManager @Inject constructor( val aHeadsUp = a.isRowHeadsUp val bHeadsUp = b.isRowHeadsUp - if (usePeopleFiltering && a.isPeopleNotification() != b.isPeopleNotification()) { - if (a.isPeopleNotification()) -1 else 1 - } else if (aHeadsUp != bHeadsUp) { - if (aHeadsUp) -1 else 1 - } else if (aHeadsUp) { + when { + usePeopleFiltering && aIsPeople != bIsPeople -> if (aIsPeople) -1 else 1 + aHeadsUp != bHeadsUp -> if (aHeadsUp) -1 else 1 // Provide consistent ranking with headsUpManager - headsUpManager.compare(a, b) - } else if (aMedia != bMedia) { + aHeadsUp -> headsUpManager.compare(a, b) // Upsort current media notification. - if (aMedia) -1 else 1 - } else if (aSystemMax != bSystemMax) { + aMedia != bMedia -> if (aMedia) -1 else 1 // Upsort PRIORITY_MAX system notifications - if (aSystemMax) -1 else 1 - } else if (a.isHighPriority != b.isHighPriority) { - -1 * java.lang.Boolean.compare(a.isHighPriority, b.isHighPriority) - } else if (aRank != bRank) { - aRank - bRank - } else { - nb.notification.`when`.compareTo(na.notification.`when`) + aSystemMax != bSystemMax -> if (aSystemMax) -1 else 1 + a.isHighPriority != b.isHighPriority -> + -1 * a.isHighPriority.compareTo(b.isHighPriority) + aRank != bRank -> aRank - bRank + else -> nb.notification.`when`.compareTo(na.notification.`when`) } } @@ -138,10 +128,9 @@ open class NotificationRankingManager @Inject constructor( val c = entry.channel val n = entry.sbn.notification - if (((n.isForegroundService && entry.ranking.importance >= IMPORTANCE_LOW) || - n.hasMediaSession() || - n.hasPerson() || - n.hasStyle(Notification.MessagingStyle::class.java))) { + if ((n.isForegroundService && entry.ranking.importance >= IMPORTANCE_LOW) || + n.hasMediaSession() || + entry.isPeopleNotification()) { // Users who have long pressed and demoted to silent should not see the notification // in the top section if (c != null && c.hasUserSetImportance()) { @@ -204,7 +193,7 @@ open class NotificationRankingManager @Inject constructor( isMedia: Boolean, isSystemMax: Boolean ) { - if (usePeopleFiltering && entry.hasAssociatedPeople()) { + if (usePeopleFiltering && entry.isPeopleNotification()) { entry.bucket = BUCKET_PEOPLE } else if (isHeadsUp || isMedia || isSystemMax || entry.isHighPriority) { entry.bucket = BUCKET_ALERTING @@ -235,6 +224,11 @@ open class NotificationRankingManager @Inject constructor( } } } + + private fun NotificationEntry.isPeopleNotification() = + sbn.isPeopleNotification() + private fun StatusBarNotification.isPeopleNotification() = + peopleNotificationIdentifier.isPeopleNotification(this) } // Convenience functions @@ -245,16 +239,3 @@ private fun NotificationEntry.isSystemMax(): Boolean { private fun StatusBarNotification.isSystemNotification(): Boolean { return "android" == packageName || "com.android.systemui" == packageName } - -private fun Notification.hasPerson(): Boolean { - val people: ArrayList<Person> = - (extras?.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST)) ?: ArrayList() - return people.isNotEmpty() -} - -private fun Notification.hasStyle(targetStyleClass: Class<*>): Boolean { - return targetStyleClass == notificationStyle -} - -private fun NotificationEntry.isPeopleNotification(): Boolean = - sbn.notification.hasStyle(Notification.MessagingStyle::class.java) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java index f5ed089b63bd..6daf3fc50b30 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java @@ -32,6 +32,7 @@ import android.service.notification.StatusBarNotification; import androidx.annotation.MainThread; import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -165,6 +166,7 @@ public class KeyguardCoordinator implements Coordinator { private void setupInvalidateNotifListCallbacks() { // register onKeyguardShowing callback mKeyguardStateController.addCallback(mKeyguardCallback); + mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback); // register lockscreen settings changed callbacks: final ContentObserver settingsObserver = new ContentObserver(mMainHandler) { @@ -230,4 +232,13 @@ public class KeyguardCoordinator implements Coordinator { invalidateListFromFilter("onStatusBarStateChanged"); } }; + + private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback = + new KeyguardUpdateMonitorCallback() { + @Override + public void onStrongAuthStateChanged(int userId) { + // maybe lockdown mode changed + invalidateListFromFilter("onStrongAuthStateChanged"); + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java index fdd51e9e7790..8e9a051e7d43 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java @@ -318,7 +318,7 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section } mParent.addView(mPeopleHubView, targetIndex); return true; - } else if (currentHubIndex != targetIndex - 1) { + } else if (currentHubIndex != targetIndex) { if (currentHubIndex < targetIndex) { targetIndex--; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java index 063ad855343f..09ebb6468a11 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java @@ -88,10 +88,9 @@ public final class DozeServiceHost implements DozeHost { private final PulseExpansionHandler mPulseExpansionHandler; private final StatusBarWindowController mStatusBarWindowController; private final NotificationWakeUpCoordinator mNotificationWakeUpCoordinator; - private final StatusBarWindowViewController mStatusBarWindowViewController; + private StatusBarWindowViewController mStatusBarWindowViewController; private final LockscreenLockIconController mLockscreenLockIconController; private NotificationIconAreaController mNotificationIconAreaController; - private StatusBarWindowView mStatusBarWindow; private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private NotificationPanelView mNotificationPanel; private View mAmbientIndicationContainer; @@ -112,7 +111,6 @@ public final class DozeServiceHost implements DozeHost { PulseExpansionHandler pulseExpansionHandler, StatusBarWindowController statusBarWindowController, NotificationWakeUpCoordinator notificationWakeUpCoordinator, - StatusBarWindowViewController statusBarWindowViewController, LockscreenLockIconController lockscreenLockIconController) { super(); mDozeLog = dozeLog; @@ -132,7 +130,6 @@ public final class DozeServiceHost implements DozeHost { mPulseExpansionHandler = pulseExpansionHandler; mStatusBarWindowController = statusBarWindowController; mNotificationWakeUpCoordinator = notificationWakeUpCoordinator; - mStatusBarWindowViewController = statusBarWindowViewController; mLockscreenLockIconController = lockscreenLockIconController; } @@ -143,14 +140,14 @@ public final class DozeServiceHost implements DozeHost { */ public void initialize(StatusBar statusBar, NotificationIconAreaController notificationIconAreaController, - StatusBarWindowView statusBarWindow, StatusBarKeyguardViewManager statusBarKeyguardViewManager, + StatusBarWindowViewController statusBarWindowViewController, NotificationPanelView notificationPanel, View ambientIndicationContainer) { mStatusBar = statusBar; mNotificationIconAreaController = notificationIconAreaController; - mStatusBarWindow = statusBarWindow; mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mNotificationPanel = notificationPanel; + mStatusBarWindowViewController = statusBarWindowViewController; mAmbientIndicationContainer = ambientIndicationContainer; mBiometricUnlockController = mBiometricUnlockControllerLazy.get(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java index 4d6b54ccfaff..f3e9b6b3d155 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java @@ -134,6 +134,7 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen if (fd != null) { try { BitmapFactory.Options options = new BitmapFactory.Options(); + options.inPreferredConfig = Bitmap.Config.HARDWARE; return LoaderResult.success(BitmapFactory.decodeFileDescriptor( fd.getFileDescriptor(), null, options)); } catch (OutOfMemoryError e) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java index f359fe7cfec4..b31ce6afc281 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java @@ -77,14 +77,6 @@ public interface ShadeController { void goToKeyguard(); /** - * When the keyguard is showing and covered by something (bouncer, keyguard activity, etc.) it - * is occluded. This is controlled by {@link com.android.server.policy.PhoneWindowManager} - * - * @return whether the keyguard is currently occluded - */ - boolean isOccluded(); - - /** * Notify the shade controller that the current user changed * * @param newUserId userId of the new user diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 170261e989a0..5c71a57cba77 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -237,9 +237,9 @@ import java.util.Map; import java.util.Optional; import javax.inject.Named; +import javax.inject.Provider; import dagger.Lazy; -import dagger.Subcomponent; public class StatusBar extends SystemUI implements DemoMode, ActivityStarter, KeyguardStateController.Callback, @@ -377,9 +377,10 @@ public class StatusBar extends SystemUI implements DemoMode, private final FalsingManager mFalsingManager; private final BroadcastDispatcher mBroadcastDispatcher; private final ConfigurationController mConfigurationController; - private final StatusBarWindowViewController mStatusBarWindowViewController; + protected StatusBarWindowViewController mStatusBarWindowViewController; private final DozeParameters mDozeParameters; private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy; + private final Provider<StatusBarComponent.Builder> mStatusBarComponentBuilder; private final PluginManager mPluginManager; private final RemoteInputUriController mRemoteInputUriController; private final Optional<Divider> mDividerOptional; @@ -583,6 +584,8 @@ public class StatusBar extends SystemUI implements DemoMode, } } + // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by + // KeyguardCoordinator @Override public void onStrongAuthStateChanged(int userId) { super.onStrongAuthStateChanged(userId); @@ -659,7 +662,6 @@ public class StatusBar extends SystemUI implements DemoMode, NotificationListener notificationListener, ConfigurationController configurationController, StatusBarWindowController statusBarWindowController, - StatusBarWindowViewController statusBarWindowViewController, LockscreenLockIconController lockscreenLockIconController, DozeParameters dozeParameters, ScrimController scrimController, @@ -673,6 +675,7 @@ public class StatusBar extends SystemUI implements DemoMode, VolumeComponent volumeComponent, CommandQueue commandQueue, Optional<Recents> recentsOptional, + Provider<StatusBarComponent.Builder> statusBarComponentBuilder, PluginManager pluginManager, RemoteInputUriController remoteInputUriController, Optional<Divider> dividerOptional, @@ -732,7 +735,6 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationListener = notificationListener; mConfigurationController = configurationController; mStatusBarWindowController = statusBarWindowController; - mStatusBarWindowViewController = statusBarWindowViewController; mLockscreenLockIconController = lockscreenLockIconController; mDozeServiceHost = dozeServiceHost; mPowerManager = powerManager; @@ -746,6 +748,7 @@ public class StatusBar extends SystemUI implements DemoMode, mVolumeComponent = volumeComponent; mCommandQueue = commandQueue; mRecentsOptional = recentsOptional; + mStatusBarComponentBuilder = statusBarComponentBuilder; mPluginManager = pluginManager; mRemoteInputUriController = remoteInputUriController; mDividerOptional = dividerOptional; @@ -895,7 +898,8 @@ public class StatusBar extends SystemUI implements DemoMode, mKeyguardUpdateMonitor.registerCallback(mUpdateCallback); mDozeServiceHost.initialize(this, mNotificationIconAreaController, - mStatusBarWindow, mStatusBarKeyguardViewManager, + mStatusBarKeyguardViewManager, + mStatusBarWindowViewController, mNotificationPanel, mAmbientIndicationContainer); Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(this); @@ -962,7 +966,7 @@ public class StatusBar extends SystemUI implements DemoMode, updateResources(); updateTheme(); - inflateStatusBarWindow(context); + inflateStatusBarWindow(); mStatusBarWindowViewController.setService(this); mStatusBarWindow.setOnTouchListener(getStatusBarWindowTouchListener()); @@ -1233,7 +1237,8 @@ public class StatusBar extends SystemUI implements DemoMode, mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel, mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController, mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController, - mNotificationAlertingManager, rowBinder, mKeyguardStateController, mCommandQueue); + mNotificationAlertingManager, rowBinder, mKeyguardStateController, + this /* statusBar */, mCommandQueue); mNotificationListController = new NotificationListController( @@ -1246,7 +1251,7 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationActivityStarter = mStatusBarNotificationActivityStarterBuilder - .setShadeController(this) + .setStatusBar(this) .setActivityLaunchAnimator(mActivityLaunchAnimator) .setNotificationPresenter(mPresenter) .build(); @@ -1380,8 +1385,11 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationPanel); } - protected void inflateStatusBarWindow(Context context) { + private void inflateStatusBarWindow() { mStatusBarWindow = mSuperStatusBarViewFactory.getStatusBarWindowView(); + StatusBarComponent statusBarComponent = mStatusBarComponentBuilder.get() + .statusBarWindowView(mStatusBarWindow).build(); + mStatusBarWindowViewController = statusBarComponent.getStatusBarWindowViewController(); mStatusBarWindowViewController.setupExpandedStatusBar(); } @@ -1751,7 +1759,12 @@ public class StatusBar extends SystemUI implements DemoMode, return mAmbientIndicationContainer; } - @Override + /** + * When the keyguard is showing and covered by a "showWhenLocked" activity it + * is occluded. This is controlled by {@link com.android.server.policy.PhoneWindowManager} + * + * @return whether the keyguard is currently occluded + */ public boolean isOccluded() { return mIsOccluded; } @@ -4391,11 +4404,6 @@ public class StatusBar extends SystemUI implements DemoMode, return mGutsManager; } - @Subcomponent - public interface StatusBarInjector { - void createStatusBar(StatusBar statusbar); - } - boolean isTransientShown() { return mTransientShown; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarComponent.java new file mode 100644 index 000000000000..f3c843c6d62d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarComponent.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Scope; + +import dagger.BindsInstance; +import dagger.Subcomponent; + +/** + * Dagger subcomponent tied to the lifecycle of StatusBar views. + */ +@Subcomponent +public interface StatusBarComponent { + /** + * Builder for {@link StatusBarComponent}. + */ + @Subcomponent.Builder + interface Builder { + @BindsInstance Builder statusBarWindowView(StatusBarWindowView statusBarWindowView); + StatusBarComponent build(); + } + + /** + * Scope annotation for singleton items within the StatusBarComponent. + */ + @Documented + @Retention(RUNTIME) + @Scope + @interface StatusBarScope {} + + /** + * Creates a StatusBarWindowViewController. + */ + @StatusBarScope + StatusBarWindowViewController getStatusBarWindowViewController(); + +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java index 5d69409d1d4c..312c85f01275 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java @@ -77,6 +77,7 @@ import com.android.systemui.volume.VolumeComponent; import java.util.Optional; import javax.inject.Named; +import javax.inject.Provider; import javax.inject.Singleton; import dagger.Lazy; @@ -143,7 +144,6 @@ public class StatusBarModule { NotificationListener notificationListener, ConfigurationController configurationController, StatusBarWindowController statusBarWindowController, - StatusBarWindowViewController statusBarWindowViewController, LockscreenLockIconController lockscreenLockIconController, DozeParameters dozeParameters, ScrimController scrimController, @@ -157,6 +157,7 @@ public class StatusBarModule { VolumeComponent volumeComponent, CommandQueue commandQueue, Optional<Recents> recentsOptional, + Provider<StatusBarComponent.Builder> statusBarComponentBuilder, PluginManager pluginManager, RemoteInputUriController remoteInputUriController, Optional<Divider> dividerOptional, @@ -217,7 +218,6 @@ public class StatusBarModule { notificationListener, configurationController, statusBarWindowController, - statusBarWindowViewController, lockscreenLockIconController, dozeParameters, scrimController, @@ -231,6 +231,7 @@ public class StatusBarModule { volumeComponent, commandQueue, recentsOptional, + statusBarComponentBuilder, pluginManager, remoteInputUriController, dividerOptional, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 863874e788c6..e2832587e2ea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -93,6 +93,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final NotificationRemoteInputManager mRemoteInputManager; private final NotificationLockscreenUserManager mLockscreenUserManager; private final ShadeController mShadeController; + private final StatusBar mStatusBar; private final KeyguardStateController mKeyguardStateController; private final ActivityStarter mActivityStarter; private final NotificationEntryManager mEntryManager; @@ -125,7 +126,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit IDreamManager dreamManager, NotificationRemoteInputManager remoteInputManager, StatusBarRemoteInputCallback remoteInputCallback, NotificationGroupManager groupManager, NotificationLockscreenUserManager lockscreenUserManager, - ShadeController shadeController, KeyguardStateController keyguardStateController, + ShadeController shadeController, StatusBar statusBar, + KeyguardStateController keyguardStateController, NotificationInterruptionStateProvider notificationInterruptionStateProvider, MetricsLogger metricsLogger, LockPatternUtils lockPatternUtils, Handler mainThreadHandler, Handler backgroundHandler, @@ -142,6 +144,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mRemoteInputManager = remoteInputManager; mLockscreenUserManager = lockscreenUserManager; mShadeController = shadeController; + // TODO: use KeyguardStateController#isOccluded to remove this dependency + mStatusBar = statusBar; mKeyguardStateController = keyguardStateController; mActivityStarter = activityStarter; mEntryManager = entryManager; @@ -198,7 +202,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit final boolean afterKeyguardGone = isActivityIntent && mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(), mLockscreenUserManager.getCurrentUserId()); - final boolean wasOccluded = mShadeController.isOccluded(); + final boolean wasOccluded = mStatusBar.isOccluded(); boolean showOverLockscreen = mKeyguardStateController.isShowing() && intent != null && mActivityIntentHelper.wouldShowOverLockscreen(intent.getIntent(), mLockscreenUserManager.getCurrentUserId()); @@ -253,7 +257,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mShadeController.addPostCollapseAction(runnable); mShadeController.collapsePanel(true /* animate */); } else if (mKeyguardStateController.isShowing() - && mShadeController.isOccluded()) { + && mStatusBar.isOccluded()) { mShadeController.addAfterKeyguardGoneRunnable(runnable); mShadeController.collapsePanel(); } else { @@ -384,7 +388,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit .addNextIntentWithParentStack(intent) .startActivities(getActivityOptions( mActivityLaunchAnimator.getLaunchAnimation( - row, mShadeController.isOccluded())), + row, mStatusBar.isOccluded())), new UserHandle(UserHandle.getUserId(appUid))); mActivityLaunchAnimator.setLaunchResult(launchResult, true /* isActivityIntent */); if (shouldCollapse()) { @@ -518,6 +522,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private ShadeController mShadeController; private NotificationPresenter mNotificationPresenter; private ActivityLaunchAnimator mActivityLaunchAnimator; + private StatusBar mStatusBar; @Inject public Builder(Context context, @@ -567,8 +572,11 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mBubbleController = bubbleController; mSuperStatusBarViewFactory = superStatusBarViewFactory; } - public Builder setShadeController(ShadeController shadeController) { - mShadeController = shadeController; + + /** Sets the status bar to use as {@link StatusBar} and {@link ShadeController}. */ + public Builder setStatusBar(StatusBar statusBar) { + mStatusBar = statusBar; + mShadeController = statusBar; return this; } @@ -600,6 +608,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mGroupManager, mLockscreenUserManager, mShadeController, + mStatusBar, mKeyguardStateController, mNotificationInterruptionStateProvider, mMetricsLogger, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index 30e26e57e435..6650cf6a5af6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -113,6 +113,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, private final DozeScrimController mDozeScrimController; private final ScrimController mScrimController; private final Context mContext; + private final StatusBar mStatusBar; private final CommandQueue mCommandQueue; private final AccessibilityManager mAccessibilityManager; @@ -140,12 +141,15 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, NotificationAlertingManager notificationAlertingManager, NotificationRowBinderImpl notificationRowBinder, KeyguardStateController keyguardStateController, + StatusBar statusBar, CommandQueue commandQueue) { mContext = context; mKeyguardStateController = keyguardStateController; mNotificationPanel = panel; mHeadsUpManager = headsUp; mDynamicPrivacyController = dynamicPrivacyController; + // TODO: use KeyguardStateController#isOccluded to remove this dependency + mStatusBar = statusBar; mCommandQueue = commandQueue; mAboveShelfObserver = new AboveShelfObserver(stackScroller); mActivityLaunchAnimator = activityLaunchAnimator; @@ -330,7 +334,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, } public boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn) { - if (mShadeController.isOccluded()) { + if (mStatusBar.isOccluded()) { boolean devicePublic = mLockscreenUserManager. isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId()); boolean userPublic = devicePublic @@ -356,7 +360,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, } else { // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent return !mKeyguardStateController.isShowing() - || mShadeController.isOccluded(); + || mStatusBar.isOccluded(); } } return true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java index feac5da38138..3935df02fd91 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java @@ -43,7 +43,6 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.PulseExpansionHandler; -import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -57,14 +56,13 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import javax.inject.Inject; -import javax.inject.Singleton; import dagger.Lazy; /** * Controller for {@link StatusBarWindowView}. */ -@Singleton +//@Singleton public class StatusBarWindowViewController { private final InjectionInflationController mInjectionInflationController; private final NotificationWakeUpCoordinator mCoordinator; @@ -116,9 +114,9 @@ public class StatusBarWindowViewController { DozeLog dozeLog, DozeParameters dozeParameters, CommandQueue commandQueue, - SuperStatusBarViewFactory superStatusBarViewFactory, Lazy<ShadeController> shadeControllerLazy, - DockManager dockManager) { + DockManager dockManager, + StatusBarWindowView statusBarWindowView) { mInjectionInflationController = injectionInflationController; mCoordinator = coordinator; mPulseExpansionHandler = pulseExpansionHandler; @@ -134,7 +132,7 @@ public class StatusBarWindowViewController { mDozeLog = dozeLog; mDozeParameters = dozeParameters; mCommandQueue = commandQueue; - mView = superStatusBarViewFactory.getStatusBarWindowView(); + mView = statusBarWindowView; mShadeControllerLazy = shadeControllerLazy; mDockManager = dockManager; diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java new file mode 100644 index 000000000000..24f49ff99879 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.concurrency; + +import android.content.Context; +import android.os.Handler; + +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.BgHandler; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.dagger.qualifiers.MainHandler; + +import java.util.concurrent.Executor; + +import dagger.Module; +import dagger.Provides; + +/** + * Dagger Module for classes found within the concurrent package. + */ +@Module +public abstract class ConcurrencyModule { + /** + * Provide a Background-Thread Executor by default. + */ + @Provides + public static Executor provideExecutor(@BgHandler Handler handler) { + return new ExecutorImpl(handler); + } + + /** + * Provide a Background-Thread Executor. + */ + @Provides + @Background + public static Executor provideBackgroundExecutor(@BgHandler Handler handler) { + return new ExecutorImpl(handler); + } + + /** + * Provide a Main-Thread Executor. + */ + @Provides + @Main + public static Executor provideMainExecutor(Context context) { + return context.getMainExecutor(); + } + + /** + * Provide a Background-Thread Executor by default. + */ + @Provides + public static DelayableExecutor provideDelayableExecutor(@BgHandler Handler handler) { + return new ExecutorImpl(handler); + } + + /** + * Provide a Background-Thread Executor. + */ + @Provides + @Background + public static DelayableExecutor provideBackgroundDelayableExecutor(@BgHandler Handler handler) { + return new ExecutorImpl(handler); + } + + /** + * Provide a Main-Thread Executor. + */ + @Provides + @Main + public static DelayableExecutor provideMainDelayableExecutor(@MainHandler Handler handler) { + return new ExecutorImpl(handler); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/DelayableExecutor.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/DelayableExecutor.java new file mode 100644 index 000000000000..2d6c4a62047d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/DelayableExecutor.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.concurrency; + +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; + +/** + * A sub-class of {@link Executor} that allows Runnables to be delayed and/or cancelled. + */ +public interface DelayableExecutor extends Executor { + /** + * Execute supplied Runnable on the Executors thread after a specified delay. + * + * See {@link android.os.Handler#postDelayed(Runnable, long)}. + * + * @return A Runnable that, when run, removes the supplied argument from the Executor queue. + */ + default Runnable executeDelayed(Runnable r, long delayMillis) { + return executeDelayed(r, delayMillis, TimeUnit.MILLISECONDS); + } + + /** + * Execute supplied Runnable on the Executors thread after a specified delay. + * + * See {@link android.os.Handler#postDelayed(Runnable, long)}. + * + * @return A Runnable that, when run, removes the supplied argument from the Executor queue.. + */ + Runnable executeDelayed(Runnable r, long delay, TimeUnit unit); + + /** + * Execute supplied Runnable on the Executors thread at a specified uptime. + * + * See {@link android.os.Handler#postAtTime(Runnable, long)}. + * + * @return A Runnable that, when run, removes the supplied argument from the Executor queue. + */ + default Runnable executeAtTime(Runnable r, long uptime) { + return executeAtTime(r, uptime, TimeUnit.MILLISECONDS); + } + + /** + * Execute supplied Runnable on the Executors thread at a specified uptime. + * + * See {@link android.os.Handler#postAtTime(Runnable, long)}. + * + * @return A Runnable that, when run, removes the supplied argument from the Executor queue. + */ + Runnable executeAtTime(Runnable r, long uptimeMillis, TimeUnit unit); + + /** + * Remove all pending Runnables. + * + */ + void removeAll(); + +} diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ExecutorImpl.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ExecutorImpl.java new file mode 100644 index 000000000000..2a99de3ac5ad --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ExecutorImpl.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.concurrency; + +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.Message; + +import java.util.concurrent.TimeUnit; + +/** + * Implementations of {@link DelayableExecutor} for SystemUI. + */ +public class ExecutorImpl extends HandlerExecutor implements DelayableExecutor { + private final Handler mHandler; + + public ExecutorImpl(Handler handler) { + super(handler); + mHandler = handler; + } + + @Override + public Runnable executeDelayed(Runnable r, long delay, TimeUnit unit) { + Object token = new Object(); + Message m = mHandler.obtainMessage(0, token); + mHandler.sendMessageDelayed(m, unit.toMillis(delay)); + + return () -> mHandler.removeCallbacksAndMessages(token); + } + + @Override + public Runnable executeAtTime(Runnable r, long uptimeMillis, TimeUnit unit) { + Object token = new Object(); + Message m = mHandler.obtainMessage(0, token); + mHandler.sendMessageAtTime(m, unit.toMillis(uptimeMillis)); + + return () -> mHandler.removeCallbacksAndMessages(token); + } + + @Override + public void removeAll() { + mHandler.removeCallbacksAndMessages(null); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java index b41512c9a935..39ce8c1209ac 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java @@ -36,13 +36,9 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; -import android.os.Handler; -import android.os.Looper; import android.provider.Settings; import android.service.quicksettings.Tile; import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; -import android.testing.TestableLooper.RunWithLooper; import android.text.TextUtils; import android.util.ArraySet; @@ -52,6 +48,8 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.qs.QSTileHost; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; import org.junit.Test; @@ -69,7 +67,6 @@ import java.util.Set; @SmallTest @RunWith(AndroidTestingRunner.class) -@RunWithLooper public class TileQueryHelperTest extends SysuiTestCase { private static final String CURRENT_TILES = "wifi,dnd,nfc"; private static final String ONLY_STOCK_TILES = "wifi,dnd"; @@ -98,14 +95,13 @@ public class TileQueryHelperTest extends SysuiTestCase { private ArgumentCaptor<List<TileQueryHelper.TileInfo>> mCaptor; private QSTile.State mState; - private TestableLooper mBGLooper; private TileQueryHelper mTileQueryHelper; - private Handler mMainHandler; + private FakeExecutor mMainExecutor; + private FakeExecutor mBgExecutor; @Before public void setup() { MockitoAnnotations.initMocks(this); - mBGLooper = TestableLooper.get(this); mContext.setMockPackageManager(mPackageManager); mState = new QSTile.State(); @@ -123,9 +119,11 @@ public class TileQueryHelperTest extends SysuiTestCase { } ).when(mQSTileHost).createTile(anyString()); - mMainHandler = new Handler(Looper.getMainLooper()); - mTileQueryHelper = new TileQueryHelper(mContext, mMainHandler, - new Handler(mBGLooper.getLooper())); + FakeSystemClock clock = new FakeSystemClock(); + clock.setAutoIncrement(false); + mMainExecutor = new FakeExecutor(clock); + mBgExecutor = new FakeExecutor(clock); + mTileQueryHelper = new TileQueryHelper(mContext, mMainExecutor, mBgExecutor); mTileQueryHelper.setListener(mListener); } @@ -138,8 +136,7 @@ public class TileQueryHelperTest extends SysuiTestCase { public void testIsFinished_trueAfterQuerying() { mTileQueryHelper.queryTiles(mQSTileHost); - mBGLooper.processAllMessages(); - waitForIdleSync(mMainHandler); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); assertTrue(mTileQueryHelper.isFinished()); } @@ -148,8 +145,7 @@ public class TileQueryHelperTest extends SysuiTestCase { public void testQueryTiles_callsListenerTwice() { mTileQueryHelper.queryTiles(mQSTileHost); - mBGLooper.processAllMessages(); - waitForIdleSync(mMainHandler); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); verify(mListener, times(2)).onTilesChanged(any()); } @@ -163,8 +159,7 @@ public class TileQueryHelperTest extends SysuiTestCase { mTileQueryHelper.queryTiles(mQSTileHost); - mBGLooper.processAllMessages(); - waitForIdleSync(mMainHandler); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); assertTrue(mTileQueryHelper.isFinished()); } @@ -178,8 +173,7 @@ public class TileQueryHelperTest extends SysuiTestCase { mTileQueryHelper.queryTiles(mQSTileHost); - mBGLooper.processAllMessages(); - waitForIdleSync(mMainHandler); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); verify(mListener, atLeastOnce()).onTilesChanged(mCaptor.capture()); List<String> specs = new ArrayList<>(); @@ -199,8 +193,7 @@ public class TileQueryHelperTest extends SysuiTestCase { mTileQueryHelper.queryTiles(mQSTileHost); - mBGLooper.processAllMessages(); - waitForIdleSync(mMainHandler); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); verify(mListener, atLeastOnce()).onTilesChanged(mCaptor.capture()); List<String> specs = new ArrayList<>(); @@ -220,8 +213,7 @@ public class TileQueryHelperTest extends SysuiTestCase { mTileQueryHelper.queryTiles(mQSTileHost); - mBGLooper.processAllMessages(); - waitForIdleSync(mMainHandler); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); verify(mListener, atLeastOnce()).onTilesChanged(mCaptor.capture()); List<String> specs = new ArrayList<>(); @@ -251,8 +243,7 @@ public class TileQueryHelperTest extends SysuiTestCase { ""); mTileQueryHelper.queryTiles(mQSTileHost); - mBGLooper.processAllMessages(); - waitForIdleSync(mMainHandler); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); verify(mListener, atLeastOnce()).onTilesChanged(mCaptor.capture()); List<TileQueryHelper.TileInfo> tileInfos = mCaptor.getValue(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index a98945f49b99..e0dfe7e66ad5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -85,6 +85,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationRowBin import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.notification.logging.NotificationLogger; +import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.RowInflaterTask; @@ -228,7 +229,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase { mHeadsUpManager, mock(NotificationFilter.class), mNotifLog, - mock(NotificationSectionsFeatureManager.class)), + mock(NotificationSectionsFeatureManager.class), + mock(PeopleNotificationIdentifier.class)), mEnvironment ); Dependency.get(InitController.class).executePostInitTasks(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt index 01b2f8932d9b..cda1538e899d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt @@ -20,7 +20,6 @@ import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager.IMPORTANCE_DEFAULT import android.app.NotificationManager.IMPORTANCE_LOW -import android.app.Person import android.service.notification.NotificationListenerService.RankingMap import android.service.notification.StatusBarNotification import android.testing.AndroidTestingRunner @@ -36,6 +35,7 @@ import com.android.systemui.statusbar.NotificationMediaManager import com.android.systemui.statusbar.notification.NotificationFilter import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager import com.android.systemui.statusbar.notification.logging.NotifLog +import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT import com.android.systemui.statusbar.phone.NotificationGroupManager @@ -52,62 +52,41 @@ import org.mockito.Mockito.mock @SmallTest @RunWith(AndroidTestingRunner::class) -class NotificationRankingManagerTest - : SysuiTestCase() { +class NotificationRankingManagerTest : SysuiTestCase() { - private var lazyMedia: Lazy<NotificationMediaManager> = Lazy { - mock<NotificationMediaManager>(NotificationMediaManager::class.java) + private val lazyMedia: Lazy<NotificationMediaManager> = Lazy { + mock(NotificationMediaManager::class.java) } - - private val rankingManager = TestableNotificationRankingManager( - lazyMedia, - mock<NotificationGroupManager>(NotificationGroupManager::class.java), - mock<HeadsUpManager>(HeadsUpManager::class.java), - mock<NotificationFilter>(NotificationFilter::class.java), - mock<NotifLog>(NotifLog::class.java), - mock<NotificationSectionsFeatureManager>(NotificationSectionsFeatureManager::class.java) - ) + private lateinit var personNotificationIdentifier: PeopleNotificationIdentifier + private lateinit var rankingManager: TestableNotificationRankingManager @Before fun setup() { + personNotificationIdentifier = + mock(PeopleNotificationIdentifier::class.java) + rankingManager = TestableNotificationRankingManager( + lazyMedia, + mock(NotificationGroupManager::class.java), + mock(HeadsUpManager::class.java), + mock(NotificationFilter::class.java), + mock(NotifLog::class.java), + mock(NotificationSectionsFeatureManager::class.java), + personNotificationIdentifier + ) } @Test fun testPeopleNotification_isHighPriority() { - val person = Person.Builder() - .setName("name") - .setKey("abc") - .setUri("uri") - .setBot(true) - .build() - val notification = Notification.Builder(mContext, "test") - .addPerson(person) .build() val sbn = StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, notification, mContext.user, "", 0) - val e = NotificationEntryBuilder() - .setNotification(notification) - .setSbn(sbn) - .build() - - assertTrue(rankingManager.isHighPriority2(e)) - } - - @Test - fun messagingStyleHighPriority() { - - val notif = Notification.Builder(mContext, "test") - .setStyle(Notification.MessagingStyle("")) - .build() - - val sbn = StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, - notif, mContext.getUser(), "", 0) + `when`(personNotificationIdentifier.isPeopleNotification(sbn)).thenReturn(true) val e = NotificationEntryBuilder() - .setNotification(notif) + .setNotification(notification) .setSbn(sbn) .build() @@ -117,7 +96,7 @@ class NotificationRankingManagerTest @Test fun lowForegroundHighPriority() { val notification = mock(Notification::class.java) - `when`<Boolean>(notification.isForegroundService).thenReturn(true) + `when`(notification.isForegroundService).thenReturn(true) val sbn = StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, notification, mContext.user, "", 0) @@ -136,15 +115,7 @@ class NotificationRankingManagerTest @Test fun userChangeTrumpsHighPriorityCharacteristics() { - val person = Person.Builder() - .setName("name") - .setKey("abc") - .setUri("uri") - .setBot(true) - .build() - val notification = Notification.Builder(mContext, "test") - .addPerson(person) .setStyle(Notification.MessagingStyle("")) .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true) .build() @@ -152,6 +123,8 @@ class NotificationRankingManagerTest val sbn = StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, notification, mContext.user, "", 0) + `when`(personNotificationIdentifier.isPeopleNotification(sbn)).thenReturn(true) + val channel = NotificationChannel("a", "a", IMPORTANCE_LOW) channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE) @@ -297,14 +270,16 @@ class NotificationRankingManagerTest headsUpManager: HeadsUpManager, filter: NotificationFilter, notifLog: NotifLog, - sectionsFeatureManager: NotificationSectionsFeatureManager + sectionsFeatureManager: NotificationSectionsFeatureManager, + peopleNotificationIdentifier: PeopleNotificationIdentifier ) : NotificationRankingManager( mediaManager, groupManager, headsUpManager, filter, notifLog, - sectionsFeatureManager + sectionsFeatureManager, + peopleNotificationIdentifier ) { fun isHighPriority2(e: NotificationEntry): Boolean { @@ -315,4 +290,4 @@ class NotificationRankingManagerTest rankingMap = r } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index d20a37a18d2e..d2b4a20ca3a9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -74,6 +74,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter; +import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.FooterView; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; @@ -164,7 +165,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mHeadsUpManager, mock(NotificationFilter.class), mock(NotifLog.class), - mock(NotificationSectionsFeatureManager.class) + mock(NotificationSectionsFeatureManager.class), + mock(PeopleNotificationIdentifier.class) ), mock(NotificationEntryManager.KeyguardEnvironment.class)); mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java index 222fcb696c8b..46f6cfe39e3c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java @@ -85,7 +85,6 @@ public class DozeServiceHostTest extends SysuiTestCase { @Mock private StatusBar mStatusBar; @Mock private NotificationIconAreaController mNotificationIconAreaController; @Mock private StatusBarWindowViewController mStatusBarWindowViewController; - @Mock private StatusBarWindowView mStatusBarWindow; @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @Mock private NotificationPanelView mNotificationPanel; @Mock private View mAmbientIndicationContainer; @@ -101,10 +100,11 @@ public class DozeServiceHostTest extends SysuiTestCase { mKeyguardViewMediator, () -> mAssistManager, mDozeScrimController, mKeyguardUpdateMonitor, mVisualStabilityManager, mPulseExpansionHandler, mStatusBarWindowController, mNotificationWakeUpCoordinator, - mStatusBarWindowViewController, mLockscreenLockIconController); + mLockscreenLockIconController); - mDozeServiceHost.initialize(mStatusBar, mNotificationIconAreaController, mStatusBarWindow, - mStatusBarKeyguardViewManager, mNotificationPanel, mAmbientIndicationContainer); + mDozeServiceHost.initialize(mStatusBar, mNotificationIconAreaController, + mStatusBarKeyguardViewManager, mStatusBarWindowViewController, mNotificationPanel, + mAmbientIndicationContainer); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index 6f5cfbecb5d0..77cdc025bbc9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -102,7 +102,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { @Mock private RemoteInputController mRemoteInputController; @Mock - private ShadeController mShadeController; + private StatusBar mStatusBar; @Mock private KeyguardStateController mKeyguardStateController; @Mock @@ -175,7 +175,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mock(NotificationInterruptionStateProvider.class), mock(MetricsLogger.class), mock(LockPatternUtils.class), mHandler, mHandler, mActivityIntentHelper, mBubbleController, mSuperStatusBarViewFactory)) - .setShadeController(mShadeController) + .setStatusBar(mStatusBar) .setNotificationPresenter(mock(NotificationPresenter.class)) .setActivityLaunchAnimator(mock(ActivityLaunchAnimator.class)) .build(); @@ -186,11 +186,11 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { // set up addAfterKeyguardGoneRunnable to synchronously invoke the Runnable arg doAnswer(answerVoid(Runnable::run)) - .when(mShadeController).addAfterKeyguardGoneRunnable(any(Runnable.class)); + .when(mStatusBar).addAfterKeyguardGoneRunnable(any(Runnable.class)); // set up addPostCollapseAction to synchronously invoke the Runnable arg doAnswer(answerVoid(Runnable::run)) - .when(mShadeController).addPostCollapseAction(any(Runnable.class)); + .when(mStatusBar).addPostCollapseAction(any(Runnable.class)); // set up Handler to synchronously invoke the Runnable arg doAnswer(answerVoid(Runnable::run)) @@ -209,13 +209,13 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { sbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL; when(mKeyguardStateController.isShowing()).thenReturn(true); - when(mShadeController.isOccluded()).thenReturn(true); + when(mStatusBar.isOccluded()).thenReturn(true); // When mNotificationActivityStarter.onNotificationClicked(sbn, mNotificationRow); // Then - verify(mShadeController, atLeastOnce()).collapsePanel(); + verify(mStatusBar, atLeastOnce()).collapsePanel(); verify(mContentIntent).sendAndReturnResult( any(Context.class), @@ -250,7 +250,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { verify(mBubbleController).expandStackAndSelectBubble(eq(sbn.getKey())); // This is called regardless, and simply short circuits when there is nothing to do. - verify(mShadeController, atLeastOnce()).collapsePanel(); + verify(mStatusBar, atLeastOnce()).collapsePanel(); verify(mAssistManager).hideAssist(); @@ -272,7 +272,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { // Given sbn.getNotification().contentIntent = null; when(mKeyguardStateController.isShowing()).thenReturn(true); - when(mShadeController.isOccluded()).thenReturn(true); + when(mStatusBar.isOccluded()).thenReturn(true); // When mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow); @@ -280,7 +280,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { // Then verify(mBubbleController).expandStackAndSelectBubble(eq(sbn.getKey())); - verify(mShadeController, atLeastOnce()).collapsePanel(); + verify(mStatusBar, atLeastOnce()).collapsePanel(); verify(mAssistManager).hideAssist(); @@ -302,7 +302,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { // Given sbn.getNotification().contentIntent = mContentIntent; when(mKeyguardStateController.isShowing()).thenReturn(true); - when(mShadeController.isOccluded()).thenReturn(true); + when(mStatusBar.isOccluded()).thenReturn(true); // When mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow); @@ -310,7 +310,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { // Then verify(mBubbleController).expandStackAndSelectBubble(eq(sbn.getKey())); - verify(mShadeController, atLeastOnce()).collapsePanel(); + verify(mStatusBar, atLeastOnce()).collapsePanel(); verify(mAssistManager).hideAssist(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index 1c02b60902b8..14f5795c39f1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -71,10 +71,11 @@ import java.util.ArrayList; public class StatusBarNotificationPresenterTest extends SysuiTestCase { - private StatusBarNotificationPresenter mStatusBar; + private StatusBarNotificationPresenter mStatusBarNotificationPresenter; private CommandQueue mCommandQueue; private FakeMetricsLogger mMetricsLogger; private ShadeController mShadeController = mock(ShadeController.class); + private StatusBar mStatusBar = mock(StatusBar.class); @Before public void setup() { @@ -105,14 +106,14 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { StatusBarWindowView statusBarWindowView = mock(StatusBarWindowView.class); when(statusBarWindowView.getResources()).thenReturn(mContext.getResources()); - mStatusBar = new StatusBarNotificationPresenter(mContext, + mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(mContext, mock(NotificationPanelView.class), mock(HeadsUpManagerPhone.class), statusBarWindowView, mock(NotificationListContainerViewGroup.class), mock(DozeScrimController.class), mock(ScrimController.class), mock(ActivityLaunchAnimator.class), mock(DynamicPrivacyController.class), mock(NotificationAlertingManager.class), mock(NotificationRowBinderImpl.class), mock(KeyguardStateController.class), - mCommandQueue); + mStatusBar, mCommandQueue); } @Test @@ -129,7 +130,7 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { TestableLooper.get(this).processAllMessages(); assertFalse("The panel shouldn't allow heads up while disabled", - mStatusBar.canHeadsUp(entry, entry.getSbn())); + mStatusBarNotificationPresenter.canHeadsUp(entry, entry.getSbn())); } @Test @@ -146,13 +147,13 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { TestableLooper.get(this).processAllMessages(); assertFalse("The panel shouldn't allow heads up while notitifcation shade disabled", - mStatusBar.canHeadsUp(entry, entry.getSbn())); + mStatusBarNotificationPresenter.canHeadsUp(entry, entry.getSbn())); } @Test public void onActivatedMetrics() { ActivatableNotificationView view = mock(ActivatableNotificationView.class); - mStatusBar.onActivated(view); + mStatusBarNotificationPresenter.onActivated(view); MetricsAsserts.assertHasLog("missing lockscreen note tap log", mMetricsLogger.getLogs(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index e78fb9e521e1..be6809732963 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -145,6 +145,8 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Optional; +import javax.inject.Provider; + import dagger.Lazy; @SmallTest @@ -227,6 +229,9 @@ public class StatusBarTest extends SysuiTestCase { @Mock private VolumeComponent mVolumeComponent; @Mock private CommandQueue mCommandQueue; @Mock private Recents mRecents; + @Mock private Provider<StatusBarComponent.Builder> mStatusBarComponentBuilderProvider; + @Mock private StatusBarComponent.Builder mStatusBarComponentBuilder; + @Mock private StatusBarComponent mStatusBarComponent; @Mock private PluginManager mPluginManager; @Mock private Divider mDivider; @Mock private SuperStatusBarViewFactory mSuperStatusBarViewFactory; @@ -300,6 +305,11 @@ public class StatusBarTest extends SysuiTestCase { when(mLockscreenWallpaperLazy.get()).thenReturn(mLockscreenWallpaper); when(mBiometricUnlockControllerLazy.get()).thenReturn(mBiometricUnlockController); + when(mStatusBarComponentBuilderProvider.get()).thenReturn(mStatusBarComponentBuilder); + when(mStatusBarComponentBuilder.build()).thenReturn(mStatusBarComponent); + when(mStatusBarComponent.getStatusBarWindowViewController()).thenReturn( + mStatusBarWindowViewController); + mStatusBar = new StatusBar( mContext, mFeatureFlags, @@ -354,7 +364,6 @@ public class StatusBarTest extends SysuiTestCase { mNotificationListener, configurationController, mStatusBarWindowController, - mStatusBarWindowViewController, mLockscreenLockIconController, mDozeParameters, mScrimController, @@ -367,6 +376,7 @@ public class StatusBarTest extends SysuiTestCase { mVolumeComponent, mCommandQueue, Optional.of(mRecents), + mStatusBarComponentBuilderProvider, mPluginManager, mRemoteInputUriController, Optional.of(mDivider), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java index ee9ea9f26a95..529333effa2c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java @@ -36,7 +36,6 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.PulseExpansionHandler; -import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -74,7 +73,6 @@ public class StatusBarWindowViewTest extends SysuiTestCase { @Mock private StatusBar mStatusBar; @Mock private DozeLog mDozeLog; @Mock private DozeParameters mDozeParameters; - @Mock private SuperStatusBarViewFactory mSuperStatusBarViewFactory; @Mock private DockManager mDockManager; @Before @@ -85,7 +83,6 @@ public class StatusBarWindowViewTest extends SysuiTestCase { when(mStatusBar.isDozing()).thenReturn(false); mDependency.injectTestDependency(ShadeController.class, mShadeController); - when(mSuperStatusBarViewFactory.getStatusBarWindowView()).thenReturn(mView); when(mDockManager.isDocked()).thenReturn(false); mController = new StatusBarWindowViewController( @@ -105,9 +102,9 @@ public class StatusBarWindowViewTest extends SysuiTestCase { mDozeLog, mDozeParameters, new CommandQueue(mContext), - mSuperStatusBarViewFactory, () -> mShadeController, - mDockManager); + mDockManager, + mView); mController.setupExpandedStatusBar(); mController.setService(mStatusBar); mController.setDragDownHelper(mDragDownHelper); diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java new file mode 100644 index 000000000000..3ed6c5b2b11b --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.concurrency; + +import com.android.systemui.util.time.FakeSystemClock; +import com.android.systemui.util.time.FakeSystemClock.ClockTickListener; + +import java.util.Collections; +import java.util.PriorityQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +public class FakeExecutor implements DelayableExecutor { + private final FakeSystemClock mClock; + private PriorityQueue<QueuedRunnable> mQueuedRunnables = new PriorityQueue<>(); + private boolean mIgnoreClockUpdates; + + private ClockTickListener mClockTickListener = new ClockTickListener() { + @Override + public void onUptimeMillis(long uptimeMillis) { + if (!mIgnoreClockUpdates) { + runAllReady(); + } + } + }; + + /** + * Initializes a fake executor. + * + * @param clock FakeSystemClock allowing control over delayed runnables. It is strongly + * recommended that this clock have its auto-increment setting set to false to + * prevent unexpected advancement of the time. + */ + public FakeExecutor(FakeSystemClock clock) { + mClock = clock; + mClock.addListener(mClockTickListener); + } + + /** + * Runs a single runnable if it's scheduled to run according to the internal clock. + * + * If constructed to advance the clock automatically, this will advance the clock enough to + * run the next pending item. + * + * This method does not advance the clock past the item that was run. + * + * @return Returns true if an item was run. + */ + public boolean runNextReady() { + if (!mQueuedRunnables.isEmpty() && mQueuedRunnables.peek().mWhen <= mClock.uptimeMillis()) { + mQueuedRunnables.poll().mRunnable.run(); + return true; + } + + return false; + } + + /** + * Runs all Runnables that are scheduled to run according to the internal clock. + * + * If constructed to advance the clock automatically, this will advance the clock enough to + * run all the pending items. This method does not advance the clock past items that were + * run. It is equivalent to calling {@link #runNextReady()} in a loop. + * + * @return Returns the number of items that ran. + */ + public int runAllReady() { + int num = 0; + while (runNextReady()) { + num++; + } + + return num; + } + + /** + * Advances the internal clock to the next item to run. + * + * The clock will only move forward. If the next item is set to run in the past or there is no + * next item, the clock does not change. + * + * Note that this will cause one or more items to actually run. + * + * @return The delta in uptimeMillis that the clock advanced, or 0 if the clock did not advance. + */ + public long advanceClockToNext() { + if (mQueuedRunnables.isEmpty()) { + return 0; + } + + long startTime = mClock.uptimeMillis(); + long nextTime = mQueuedRunnables.peek().mWhen; + if (nextTime <= startTime) { + return 0; + } + updateClock(nextTime); + + return nextTime - startTime; + } + + + /** + * Advances the internal clock to the last item to run. + * + * The clock will only move forward. If the last item is set to run in the past or there is no + * next item, the clock does not change. + * + * @return The delta in uptimeMillis that the clock advanced, or 0 if the clock did not advance. + */ + public long advanceClockToLast() { + if (mQueuedRunnables.isEmpty()) { + return 0; + } + + long startTime = mClock.uptimeMillis(); + long nextTime = Collections.max(mQueuedRunnables).mWhen; + if (nextTime <= startTime) { + return 0; + } + + updateClock(nextTime); + + return nextTime - startTime; + } + + /** + * Returns the number of un-executed runnables waiting to run. + */ + public int numPending() { + return mQueuedRunnables.size(); + } + + @Override + public Runnable executeDelayed(Runnable r, long delay, TimeUnit unit) { + if (delay < 0) { + delay = 0; + } + return executeAtTime(r, mClock.uptimeMillis() + unit.toMillis(delay)); + } + + @Override + public Runnable executeAtTime(Runnable r, long uptime, TimeUnit unit) { + long uptimeMillis = unit.toMillis(uptime); + + QueuedRunnable container = new QueuedRunnable(r, uptimeMillis); + + mQueuedRunnables.offer(container); + + return () -> mQueuedRunnables.remove(container); + } + + @Override + public void execute(Runnable command) { + executeDelayed(command, 0); + } + + @Override + public void removeAll() { + mQueuedRunnables.clear(); + } + + /** + * Run all Executors in a loop until they all report they have no ready work to do. + * + * Useful if you have Executors the post work to other Executors, and you simply want to + * run them all until they stop posting work. + */ + public static void exhaustExecutors(FakeExecutor ...executors) { + boolean didAnything; + do { + didAnything = false; + for (FakeExecutor executor : executors) { + didAnything = didAnything || executor.runAllReady() != 0; + } + } while (didAnything); + } + + private void updateClock(long nextTime) { + mIgnoreClockUpdates = true; + mClock.setUptimeMillis(nextTime); + mIgnoreClockUpdates = false; + } + + private static class QueuedRunnable implements Comparable<QueuedRunnable> { + private static AtomicInteger sCounter = new AtomicInteger(); + + Runnable mRunnable; + long mWhen; + private int mCounter; + + private QueuedRunnable(Runnable r, long when) { + mRunnable = r; + mWhen = when; + + // PrioirityQueue orders items arbitrarily when equal. We want to ensure that + // otherwise-equal elements are ordered according to their insertion order. Because this + // class only is constructed right before insertion, we use a static counter to track + // insertion order of otherwise equal elements. + mCounter = sCounter.incrementAndGet(); + } + + @Override + public int compareTo(QueuedRunnable other) { + long diff = mWhen - other.mWhen; + + if (diff == 0) { + return mCounter - other.mCounter; + } + + return diff > 0 ? 1 : -1; + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java new file mode 100644 index 000000000000..7fd694244afa --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.concurrency; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.util.time.FakeSystemClock; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; + +import kotlin.jvm.functions.Function4; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class FakeExecutorTest extends SysuiTestCase { + @Before + public void setUp() throws Exception { + } + + /** + * Test FakeExecutor that receives non-delayed items to execute. + */ + @Test + public void testNoDelay() { + FakeSystemClock clock = new FakeSystemClock(); + clock.setAutoIncrement(false); + FakeExecutor fakeExecutor = new FakeExecutor(clock); + RunnableImpl runnable = new RunnableImpl(); + + assertEquals(0, clock.uptimeMillis()); + assertEquals(0, runnable.mRunCount); + + // Execute two runnables. They should not run and should be left pending. + fakeExecutor.execute(runnable); + assertEquals(0, runnable.mRunCount); + assertEquals(0, clock.uptimeMillis()); + assertEquals(1, fakeExecutor.numPending()); + fakeExecutor.execute(runnable); + assertEquals(0, runnable.mRunCount); + assertEquals(0, clock.uptimeMillis()); + assertEquals(2, fakeExecutor.numPending()); + + // Run one pending runnable. + assertTrue(fakeExecutor.runNextReady()); + assertEquals(1, runnable.mRunCount); + assertEquals(0, clock.uptimeMillis()); + assertEquals(1, fakeExecutor.numPending()); + // Run a second pending runnable. + assertTrue(fakeExecutor.runNextReady()); + assertEquals(2, runnable.mRunCount); + assertEquals(0, clock.uptimeMillis()); + assertEquals(0, fakeExecutor.numPending()); + + // No more runnables to run. + assertFalse(fakeExecutor.runNextReady()); + + // Add two more runnables. + fakeExecutor.execute(runnable); + fakeExecutor.execute(runnable); + assertEquals(2, runnable.mRunCount); + assertEquals(0, clock.uptimeMillis()); + assertEquals(2, fakeExecutor.numPending()); + // Execute all pending runnables in batch. + assertEquals(2, fakeExecutor.runAllReady()); + assertEquals(4, runnable.mRunCount); + assertEquals(0, clock.uptimeMillis()); + assertEquals(0, fakeExecutor.runAllReady()); + } + + /** + * Test FakeExecutor that is told to delay execution on items. + */ + @Test + public void testDelayed() { + FakeSystemClock clock = new FakeSystemClock(); + clock.setAutoIncrement(false); + FakeExecutor fakeExecutor = new FakeExecutor(clock); + RunnableImpl runnable = new RunnableImpl(); + + // Add three delayed runnables. + fakeExecutor.executeDelayed(runnable, 1); + fakeExecutor.executeDelayed(runnable, 50); + fakeExecutor.executeDelayed(runnable, 100); + assertEquals(0, runnable.mRunCount); + assertEquals(0, clock.uptimeMillis()); + assertEquals(3, fakeExecutor.numPending()); + // Delayed runnables should not advance the clock and therefore should not run. + assertFalse(fakeExecutor.runNextReady()); + assertEquals(0, fakeExecutor.runAllReady()); + assertEquals(3, fakeExecutor.numPending()); + + // Advance the clock to the next runnable. One runnable should execute. + assertEquals(1, fakeExecutor.advanceClockToNext()); + assertEquals(1, fakeExecutor.runAllReady()); + assertEquals(2, fakeExecutor.numPending()); + assertEquals(1, runnable.mRunCount); + // Advance the clock to the last runnable. + assertEquals(99, fakeExecutor.advanceClockToLast()); + assertEquals(2, fakeExecutor.runAllReady()); + // Now all remaining runnables should execute. + assertEquals(0, fakeExecutor.numPending()); + assertEquals(3, runnable.mRunCount); + } + + /** + * Test FakeExecutor that is told to delay execution on items. + */ + @Test + public void testDelayed_AdvanceAndRun() { + FakeSystemClock clock = new FakeSystemClock(); + clock.setAutoIncrement(false); + FakeExecutor fakeExecutor = new FakeExecutor(clock); + RunnableImpl runnable = new RunnableImpl(); + + // Add three delayed runnables. + fakeExecutor.executeDelayed(runnable, 1); + fakeExecutor.executeDelayed(runnable, 50); + fakeExecutor.executeDelayed(runnable, 100); + assertEquals(0, runnable.mRunCount); + assertEquals(0, clock.uptimeMillis()); + assertEquals(3, fakeExecutor.numPending()); + // Delayed runnables should not advance the clock and therefore should not run. + assertFalse(fakeExecutor.runNextReady()); + assertEquals(0, fakeExecutor.runAllReady()); + assertEquals(3, fakeExecutor.numPending()); + + // Advance the clock to the next runnable. Check that it is run. + assertEquals(1, fakeExecutor.advanceClockToNext()); + assertEquals(1, fakeExecutor.runAllReady()); + assertEquals(1, clock.uptimeMillis()); + assertEquals(2, fakeExecutor.numPending()); + assertEquals(1, runnable.mRunCount); + assertEquals(49, fakeExecutor.advanceClockToNext()); + assertEquals(1, fakeExecutor.runAllReady()); + assertEquals(50, clock.uptimeMillis()); + assertEquals(1, fakeExecutor.numPending()); + assertEquals(2, runnable.mRunCount); + assertEquals(50, fakeExecutor.advanceClockToNext()); + assertEquals(1, fakeExecutor.runAllReady()); + assertEquals(100, clock.uptimeMillis()); + assertEquals(0, fakeExecutor.numPending()); + assertEquals(3, runnable.mRunCount); + + // Nothing left to do + assertEquals(0, fakeExecutor.advanceClockToNext()); + assertEquals(0, fakeExecutor.runAllReady()); + assertEquals(100, clock.uptimeMillis()); + assertEquals(0, fakeExecutor.numPending()); + assertEquals(3, runnable.mRunCount); + } + + /** + * Test execution order. + */ + @Test + public void testExecutionOrder() { + FakeSystemClock clock = new FakeSystemClock(); + clock.setAutoIncrement(false); + FakeExecutor fakeExecutor = new FakeExecutor(clock); + RunnableImpl runnableA = new RunnableImpl(); + RunnableImpl runnableB = new RunnableImpl(); + RunnableImpl runnableC = new RunnableImpl(); + RunnableImpl runnableD = new RunnableImpl(); + + Function4<Integer, Integer, Integer, Integer, Void> checkRunCounts = + (Integer argA, Integer argB, Integer argC, Integer argD) -> { + assertEquals("RunnableA run count wrong", argA.intValue(), runnableA.mRunCount); + assertEquals("RunnableB run count wrong", argB.intValue(), runnableB.mRunCount); + assertEquals("RunnableC run count wrong", argC.intValue(), runnableC.mRunCount); + assertEquals("RunnableD run count wrong", argD.intValue(), runnableD.mRunCount); + return null; + }; + + assertEquals(0, clock.uptimeMillis()); + checkRunCounts.invoke(0, 0, 0, 0); + + fakeExecutor.execute(runnableA); + fakeExecutor.execute(runnableB); + fakeExecutor.execute(runnableC); + fakeExecutor.execute(runnableD); + + fakeExecutor.runNextReady(); + checkRunCounts.invoke(1, 0, 0, 0); + fakeExecutor.runNextReady(); + checkRunCounts.invoke(1, 1, 0, 0); + fakeExecutor.runNextReady(); + checkRunCounts.invoke(1, 1, 1, 0); + fakeExecutor.runNextReady(); + checkRunCounts.invoke(1, 1, 1, 1); + + fakeExecutor.executeDelayed(runnableA, 100); + fakeExecutor.execute(runnableB); + fakeExecutor.executeDelayed(runnableC, 50); + fakeExecutor.execute(runnableD); + + fakeExecutor.advanceClockToNext(); + fakeExecutor.runAllReady(); + checkRunCounts.invoke(1, 2, 1, 2); + fakeExecutor.advanceClockToNext(); + fakeExecutor.runAllReady(); + checkRunCounts.invoke(1, 2, 2, 2); + fakeExecutor.advanceClockToNext(); + fakeExecutor.runAllReady(); + checkRunCounts.invoke(2, 2, 2, 2); + + fakeExecutor.execute(runnableA); + fakeExecutor.executeAtTime(runnableB, 0); // this is in the past! + fakeExecutor.executeAtTime(runnableC, 1000); + fakeExecutor.executeAtTime(runnableD, 500); + + fakeExecutor.advanceClockToNext(); + fakeExecutor.runAllReady(); + checkRunCounts.invoke(3, 3, 2, 2); + fakeExecutor.advanceClockToNext(); + fakeExecutor.runAllReady(); + checkRunCounts.invoke(3, 3, 2, 3); + fakeExecutor.advanceClockToNext(); + fakeExecutor.runAllReady(); + checkRunCounts.invoke(3, 3, 3, 3); + } + + /** + * Test removing a single item. + */ + @Test + public void testRemoval_single() { + FakeSystemClock clock = new FakeSystemClock(); + clock.setAutoIncrement(false); + FakeExecutor fakeExecutor = new FakeExecutor(clock); + RunnableImpl runnable = new RunnableImpl(); + Runnable removeFunction; + + // Nothing to remove. + assertEquals(0, runnable.mRunCount); + assertEquals(0, fakeExecutor.numPending()); + + // Two pending items that have not yet run. + // We will try to remove the second item. + fakeExecutor.executeDelayed(runnable, 100); + removeFunction = fakeExecutor.executeDelayed(runnable, 200); + assertEquals(2, fakeExecutor.numPending()); + assertEquals(0, runnable.mRunCount); + + // Remove the item. + removeFunction.run(); + assertEquals(1, fakeExecutor.numPending()); + assertEquals(0, runnable.mRunCount); + + // One item to run. + fakeExecutor.advanceClockToLast(); + fakeExecutor.runAllReady(); + assertEquals(0, fakeExecutor.numPending()); + assertEquals(1, runnable.mRunCount); + + // Nothing to remove. + removeFunction.run(); + fakeExecutor.runAllReady(); + assertEquals(0, fakeExecutor.numPending()); + assertEquals(1, runnable.mRunCount); + } + + /** + * Test removing multiple items. + */ + @Test + public void testRemoval_multi() { + FakeSystemClock clock = new FakeSystemClock(); + clock.setAutoIncrement(false); + FakeExecutor fakeExecutor = new FakeExecutor(clock); + List<Runnable> removeFunctions = new ArrayList<>(); + RunnableImpl runnable = new RunnableImpl(); + + // Nothing to remove. + assertEquals(0, runnable.mRunCount); + assertEquals(0, fakeExecutor.numPending()); + + // Three pending items that have not yet run. + // We will try to remove the first and third items. + removeFunctions.add(fakeExecutor.executeDelayed(runnable, 100)); + fakeExecutor.executeDelayed(runnable, 200); + removeFunctions.add(fakeExecutor.executeDelayed(runnable, 300)); + assertEquals(3, fakeExecutor.numPending()); + assertEquals(0, runnable.mRunCount); + + // Remove the items. + removeFunctions.forEach(Runnable::run); + assertEquals(1, fakeExecutor.numPending()); + assertEquals(0, runnable.mRunCount); + + // One item to run. + fakeExecutor.advanceClockToLast(); + fakeExecutor.runAllReady(); + assertEquals(0, fakeExecutor.numPending()); + assertEquals(1, runnable.mRunCount); + + // Nothing to remove. + removeFunctions.forEach(Runnable::run); + assertEquals(0, fakeExecutor.numPending()); + assertEquals(1, runnable.mRunCount); + } + + /** + * Test removing everything + */ + @Test + public void testRemoval_all() { + FakeSystemClock clock = new FakeSystemClock(); + clock.setAutoIncrement(false); + FakeExecutor fakeExecutor = new FakeExecutor(clock); + RunnableImpl runnable = new RunnableImpl(); + + // Nothing to remove. + assertEquals(0, runnable.mRunCount); + assertEquals(0, fakeExecutor.numPending()); + + // Two pending items that have not yet run. + fakeExecutor.executeDelayed(runnable, 100); + fakeExecutor.executeDelayed(runnable, 200); + assertEquals(2, fakeExecutor.numPending()); + assertEquals(0, runnable.mRunCount); + + // Remove the items. + fakeExecutor.removeAll(); + + // Nothing to run + fakeExecutor.advanceClockToLast(); + assertEquals(0, fakeExecutor.runAllReady()); + assertEquals(0, fakeExecutor.numPending()); + assertEquals(0, runnable.mRunCount); + } + + private static class RunnableImpl implements Runnable { + int mRunCount; + + @Override + public void run() { + mRunCount++; + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java b/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java index 7b5417cd5c36..65e5902c84df 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java @@ -16,6 +16,9 @@ package com.android.systemui.util.time; +import java.util.ArrayList; +import java.util.List; + public class FakeSystemClock implements SystemClock { private boolean mAutoIncrement = true; @@ -26,11 +29,13 @@ public class FakeSystemClock implements SystemClock { private long mCurrentThreadTimeMicro; private long mCurrentTimeMicro; + List<ClockTickListener> mListeners = new ArrayList<>(); + @Override public long uptimeMillis() { long value = mUptimeMillis; if (mAutoIncrement) { - mUptimeMillis++; + setUptimeMillis(mUptimeMillis + 1); } return value; } @@ -39,7 +44,7 @@ public class FakeSystemClock implements SystemClock { public long elapsedRealtime() { long value = mElapsedRealtime; if (mAutoIncrement) { - mElapsedRealtime++; + setElapsedRealtime(mElapsedRealtime + 1); } return value; } @@ -48,7 +53,7 @@ public class FakeSystemClock implements SystemClock { public long elapsedRealtimeNanos() { long value = mElapsedRealtimeNanos; if (mAutoIncrement) { - mElapsedRealtimeNanos++; + setElapsedRealtimeNanos(mElapsedRealtimeNanos + 1); } return value; } @@ -57,7 +62,7 @@ public class FakeSystemClock implements SystemClock { public long currentThreadTimeMillis() { long value = mCurrentThreadTimeMillis; if (mAutoIncrement) { - mCurrentThreadTimeMillis++; + setCurrentThreadTimeMillis(mCurrentThreadTimeMillis + 1); } return value; } @@ -66,7 +71,7 @@ public class FakeSystemClock implements SystemClock { public long currentThreadTimeMicro() { long value = mCurrentThreadTimeMicro; if (mAutoIncrement) { - mCurrentThreadTimeMicro++; + setCurrentThreadTimeMicro(mCurrentThreadTimeMicro + 1); } return value; } @@ -75,37 +80,90 @@ public class FakeSystemClock implements SystemClock { public long currentTimeMicro() { long value = mCurrentTimeMicro; if (mAutoIncrement) { - mCurrentTimeMicro++; + setCurrentTimeMicro(mCurrentTimeMicro + 1); } return value; } public void setUptimeMillis(long uptimeMillis) { mUptimeMillis = uptimeMillis; + for (ClockTickListener listener : mListeners) { + listener.onUptimeMillis(mUptimeMillis); + } } public void setElapsedRealtime(long elapsedRealtime) { mElapsedRealtime = elapsedRealtime; + for (ClockTickListener listener : mListeners) { + listener.onElapsedRealtime(mElapsedRealtime); + } } public void setElapsedRealtimeNanos(long elapsedRealtimeNanos) { mElapsedRealtimeNanos = elapsedRealtimeNanos; + for (ClockTickListener listener : mListeners) { + listener.onElapsedRealtimeNanos(mElapsedRealtimeNanos); + } } public void setCurrentThreadTimeMillis(long currentThreadTimeMillis) { mCurrentThreadTimeMillis = currentThreadTimeMillis; + for (ClockTickListener listener : mListeners) { + listener.onCurrentThreadTimeMillis(mCurrentThreadTimeMillis); + } } public void setCurrentThreadTimeMicro(long currentThreadTimeMicro) { mCurrentThreadTimeMicro = currentThreadTimeMicro; + for (ClockTickListener listener : mListeners) { + listener.onCurrentThreadTimeMicro(mCurrentThreadTimeMicro); + } } public void setCurrentTimeMicro(long currentTimeMicro) { mCurrentTimeMicro = currentTimeMicro; + for (ClockTickListener listener : mListeners) { + listener.onCurrentTimeMicro(mCurrentTimeMicro); + } } /** If true, each call to get____ will be one higher than the previous call to that method. */ public void setAutoIncrement(boolean autoIncrement) { mAutoIncrement = autoIncrement; } + + public void addListener(ClockTickListener listener) { + mListeners.add(listener); + } + + public void removeListener(ClockTickListener listener) { + mListeners.remove(listener); + } + + /** Alert all the listeners about the current time. */ + public void synchronizeListeners() { + for (ClockTickListener listener : mListeners) { + listener.onUptimeMillis(mUptimeMillis); + listener.onElapsedRealtime(mElapsedRealtime); + listener.onElapsedRealtimeNanos(mElapsedRealtimeNanos); + listener.onCurrentThreadTimeMillis(mCurrentThreadTimeMillis); + listener.onCurrentThreadTimeMicro(mCurrentThreadTimeMicro); + listener.onCurrentTimeMicro(mCurrentTimeMicro); + } + } + + + public interface ClockTickListener { + default void onUptimeMillis(long uptimeMillis) {} + + default void onElapsedRealtime(long elapsedRealtime) {} + + default void onElapsedRealtimeNanos(long elapsedRealtimeNanos) {} + + default void onCurrentThreadTimeMillis(long currentThreadTimeMillis) {} + + default void onCurrentThreadTimeMicro(long currentThreadTimeMicro) {} + + default void onCurrentTimeMicro(long currentTimeMicro) {} + } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index 339fc963b427..8ce92a3154f3 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -972,6 +972,9 @@ public class AccessibilityWindowManager { if (shouldComputeWindows) { mWindowManagerInternal.computeWindowsForAccessibility(displayId); } + + mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata( + windowToken.asBinder(), windowId); return windowId; } @@ -991,7 +994,7 @@ public class AccessibilityWindowManager { final int removedWindowId = removeAccessibilityInteractionConnectionInternalLocked( token, mGlobalWindowTokens, mGlobalInteractionConnections); if (removedWindowId >= 0) { - onAccessibilityInteractionConnectionRemovedLocked(removedWindowId); + onAccessibilityInteractionConnectionRemovedLocked(removedWindowId, token); if (DEBUG) { Slog.i(LOG_TAG, "Removed global connection for pid:" + Binder.getCallingPid() + " with windowId: " + removedWindowId + " and token: " @@ -1007,7 +1010,8 @@ public class AccessibilityWindowManager { getWindowTokensForUserLocked(userId), getInteractionConnectionsForUserLocked(userId)); if (removedWindowIdForUser >= 0) { - onAccessibilityInteractionConnectionRemovedLocked(removedWindowIdForUser); + onAccessibilityInteractionConnectionRemovedLocked( + removedWindowIdForUser, token); if (DEBUG) { Slog.i(LOG_TAG, "Removed user connection for pid:" + Binder.getCallingPid() + " with windowId: " + removedWindowIdForUser + " and userId:" @@ -1069,18 +1073,21 @@ public class AccessibilityWindowManager { * @param userId The userId to remove */ private void removeAccessibilityInteractionConnectionLocked(int windowId, int userId) { + IBinder window = null; if (userId == UserHandle.USER_ALL) { + window = mGlobalWindowTokens.get(windowId); mGlobalWindowTokens.remove(windowId); mGlobalInteractionConnections.remove(windowId); } else { if (isValidUserForWindowTokensLocked(userId)) { + window = getWindowTokensForUserLocked(userId).get(windowId); getWindowTokensForUserLocked(userId).remove(windowId); } if (isValidUserForInteractionConnectionsLocked(userId)) { getInteractionConnectionsForUserLocked(userId).remove(windowId); } } - onAccessibilityInteractionConnectionRemovedLocked(windowId); + onAccessibilityInteractionConnectionRemovedLocked(windowId, window); if (DEBUG) { Slog.i(LOG_TAG, "Removing interaction connection to windowId: " + windowId); } @@ -1091,12 +1098,17 @@ public class AccessibilityWindowManager { * * @param windowId Removed windowId */ - private void onAccessibilityInteractionConnectionRemovedLocked(int windowId) { + private void onAccessibilityInteractionConnectionRemovedLocked( + int windowId, @Nullable IBinder binder) { // Active window will not update, if windows callback is unregistered. // Update active window to invalid, when its a11y interaction connection is removed. if (!isTrackingWindowsLocked() && windowId >= 0 && mActiveWindowId == windowId) { mActiveWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; } + if (binder != null) { + mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata( + binder, AccessibilityWindowInfo.UNDEFINED_WINDOW_ID); + } } /** diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java index 19ac0d3c1024..17549268503e 100644 --- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java +++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java @@ -17,6 +17,8 @@ package com.android.server.accessibility; import android.accessibilityservice.AccessibilityService; +import android.app.PendingIntent; +import android.app.RemoteAction; import android.app.StatusBarManager; import android.content.Context; import android.hardware.input.InputManager; @@ -25,81 +27,272 @@ import android.os.Handler; import android.os.Looper; import android.os.PowerManager; import android.os.SystemClock; +import android.util.ArrayMap; +import android.util.Slog; import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; +import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; +import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ScreenshotHelper; import com.android.server.LocalServices; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.wm.WindowManagerInternal; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import java.util.function.Supplier; /** - * Handle the back-end of AccessibilityService#performGlobalAction + * Handle the back-end of system AccessibilityAction. + * + * This class should support three use cases with combined usage of new API and legacy API: + * + * Use case 1: SystemUI doesn't use the new system action registration API. Accessibility + * service doesn't use the new system action API to obtain action list. Accessibility + * service uses legacy global action id to perform predefined system actions. + * Use case 2: SystemUI uses the new system action registration API to register available system + * actions. Accessibility service doesn't use the new system action API to obtain action + * list. Accessibility service uses legacy global action id to trigger the system + * actions registered by SystemUI. + * Use case 3: SystemUI doesn't use the new system action registration API.Accessibility service + * obtains the available system actions using new AccessibilityService API and trigger + * the predefined system actions. */ public class SystemActionPerformer { + private static final String TAG = "SystemActionPerformer"; + + interface SystemActionsChangedListener { + void onSystemActionsChanged(); + } + private final SystemActionsChangedListener mListener; + + private final Object mSystemActionLock = new Object(); + // Resource id based ActionId -> RemoteAction + @GuardedBy("mSystemActionLock") + private final Map<Integer, RemoteAction> mRegisteredSystemActions = new ArrayMap<>(); + + // Legacy system actions. + private final AccessibilityAction mLegacyHomeAction; + private final AccessibilityAction mLegacyBackAction; + private final AccessibilityAction mLegacyRecentsAction; + private final AccessibilityAction mLegacyNotificationsAction; + private final AccessibilityAction mLegacyQuickSettingsAction; + private final AccessibilityAction mLegacyPowerDialogAction; + private final AccessibilityAction mLegacyToggleSplitScreenAction; + private final AccessibilityAction mLegacyLockScreenAction; + private final AccessibilityAction mLegacyTakeScreenshotAction; + private final WindowManagerInternal mWindowManagerService; private final Context mContext; private Supplier<ScreenshotHelper> mScreenshotHelperSupplier; - public SystemActionPerformer(Context context, WindowManagerInternal windowManagerInternal) { - mContext = context; - mWindowManagerService = windowManagerInternal; - mScreenshotHelperSupplier = null; + public SystemActionPerformer( + Context context, + WindowManagerInternal windowManagerInternal) { + this(context, windowManagerInternal, null, null); } // Used to mock ScreenshotHelper @VisibleForTesting - public SystemActionPerformer(Context context, WindowManagerInternal windowManagerInternal, + public SystemActionPerformer( + Context context, + WindowManagerInternal windowManagerInternal, Supplier<ScreenshotHelper> screenshotHelperSupplier) { - this(context, windowManagerInternal); + this(context, windowManagerInternal, screenshotHelperSupplier, null); + } + + public SystemActionPerformer( + Context context, + WindowManagerInternal windowManagerInternal, + Supplier<ScreenshotHelper> screenshotHelperSupplier, + SystemActionsChangedListener listener) { + mContext = context; + mWindowManagerService = windowManagerInternal; + mListener = listener; mScreenshotHelperSupplier = screenshotHelperSupplier; + + mLegacyHomeAction = new AccessibilityAction( + AccessibilityService.GLOBAL_ACTION_HOME, + mContext.getResources().getString( + R.string.accessibility_system_action_home_label)); + mLegacyBackAction = new AccessibilityAction( + AccessibilityService.GLOBAL_ACTION_BACK, + mContext.getResources().getString( + R.string.accessibility_system_action_back_label)); + mLegacyRecentsAction = new AccessibilityAction( + AccessibilityService.GLOBAL_ACTION_RECENTS, + mContext.getResources().getString( + R.string.accessibility_system_action_recents_label)); + mLegacyNotificationsAction = new AccessibilityAction( + AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS, + mContext.getResources().getString( + R.string.accessibility_system_action_notifications_label)); + mLegacyQuickSettingsAction = new AccessibilityAction( + AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS, + mContext.getResources().getString( + R.string.accessibility_system_action_quick_settings_label)); + mLegacyPowerDialogAction = new AccessibilityAction( + AccessibilityService.GLOBAL_ACTION_POWER_DIALOG, + mContext.getResources().getString( + R.string.accessibility_system_action_power_dialog_label)); + mLegacyToggleSplitScreenAction = new AccessibilityAction( + AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN, + mContext.getResources().getString( + R.string.accessibility_system_action_toggle_split_screen_label)); + mLegacyLockScreenAction = new AccessibilityAction( + AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN, + mContext.getResources().getString( + R.string.accessibility_system_action_lock_screen_label)); + mLegacyTakeScreenshotAction = new AccessibilityAction( + AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT, + mContext.getResources().getString( + R.string.accessibility_system_action_screenshot_label)); + } + + /** + * This method is called to register a system action. If a system action is already registered + * with the given id, the existing system action will be overwritten. + */ + void registerSystemAction(int id, RemoteAction action) { + synchronized (mSystemActionLock) { + mRegisteredSystemActions.put(id, action); + } + if (mListener != null) { + mListener.onSystemActionsChanged(); + } + } + + /** + * This method is called to unregister a system action previously registered through + * registerSystemAction. + */ + void unregisterSystemAction(int id) { + synchronized (mSystemActionLock) { + mRegisteredSystemActions.remove(id); + } + if (mListener != null) { + mListener.onSystemActionsChanged(); + } } /** - * Performe the system action matching the given action id. + * This method returns the list of available system actions. */ - public boolean performSystemAction(int action) { + List<AccessibilityAction> getSystemActions() { + List<AccessibilityAction> systemActions = new ArrayList<>(); + synchronized (mSystemActionLock) { + for (Map.Entry<Integer, RemoteAction> entry : mRegisteredSystemActions.entrySet()) { + AccessibilityAction systemAction = new AccessibilityAction( + entry.getKey(), + entry.getValue().getTitle()); + systemActions.add(systemAction); + } + + // add AccessibilitySystemAction entry for legacy system actions if not overwritten + addLegacySystemActions(systemActions); + } + return systemActions; + } + + private void addLegacySystemActions(List<AccessibilityAction> systemActions) { + if (!mRegisteredSystemActions.containsKey(AccessibilityService.GLOBAL_ACTION_BACK)) { + systemActions.add(mLegacyBackAction); + } + if (!mRegisteredSystemActions.containsKey(AccessibilityService.GLOBAL_ACTION_HOME)) { + systemActions.add(mLegacyHomeAction); + } + if (!mRegisteredSystemActions.containsKey(AccessibilityService.GLOBAL_ACTION_RECENTS)) { + systemActions.add(mLegacyRecentsAction); + } + if (!mRegisteredSystemActions.containsKey( + AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS)) { + systemActions.add(mLegacyNotificationsAction); + } + if (!mRegisteredSystemActions.containsKey( + AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS)) { + systemActions.add(mLegacyQuickSettingsAction); + } + if (!mRegisteredSystemActions.containsKey( + AccessibilityService.GLOBAL_ACTION_POWER_DIALOG)) { + systemActions.add(mLegacyPowerDialogAction); + } + if (!mRegisteredSystemActions.containsKey( + AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN)) { + systemActions.add(mLegacyToggleSplitScreenAction); + } + if (!mRegisteredSystemActions.containsKey( + AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN)) { + systemActions.add(mLegacyLockScreenAction); + } + if (!mRegisteredSystemActions.containsKey( + AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT)) { + systemActions.add(mLegacyTakeScreenshotAction); + } + } + + /** + * Trigger the registered action by the matching action id. + */ + public boolean performSystemAction(int actionId) { final long identity = Binder.clearCallingIdentity(); try { - switch (action) { + synchronized (mSystemActionLock) { + // If a system action is registered with the given actionId, call the corresponding + // RemoteAction. + RemoteAction registeredAction = mRegisteredSystemActions.get(actionId); + if (registeredAction != null) { + try { + registeredAction.getActionIntent().send(); + return true; + } catch (PendingIntent.CanceledException ex) { + Slog.e(TAG, + "canceled PendingIntent for global action " + + registeredAction.getTitle(), + ex); + } + return false; + } + } + + // No RemoteAction registered with the given actionId, try the default legacy system + // actions. + switch (actionId) { case AccessibilityService.GLOBAL_ACTION_BACK: { sendDownAndUpKeyEvents(KeyEvent.KEYCODE_BACK); + return true; } - return true; case AccessibilityService.GLOBAL_ACTION_HOME: { sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HOME); + return true; } - return true; - case AccessibilityService.GLOBAL_ACTION_RECENTS: { + case AccessibilityService.GLOBAL_ACTION_RECENTS: return openRecents(); - } case AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS: { expandNotifications(); + return true; } - return true; case AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS: { expandQuickSettings(); + return true; } - return true; case AccessibilityService.GLOBAL_ACTION_POWER_DIALOG: { showGlobalActions(); + return true; } - return true; - case AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN: { + case AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN: return toggleSplitScreen(); - } - case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN: { + case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN: return lockScreen(); - } - case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT: { + case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT: return takeScreenshot(); - } + default: + return false; } - return false; } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS index a9c38bcf2532..76e0c137d2f5 100644 --- a/services/core/java/com/android/server/OWNERS +++ b/services/core/java/com/android/server/OWNERS @@ -4,3 +4,6 @@ per-file ConnectivityService.java,NetworkManagementService.java,NsdService.java # Vibrator / Threads per-file VibratorService.java, DisplayThread.java = michaelwr@google.com per-file VibratorService.java, DisplayThread.java = ogunwale@google.com + +# Zram writeback +per-file ZramWriteback.java = minchan@google.com, rajekumar@google.com, srnvs@google.com diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 9875f6db749c..1f7d9eab9675 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -17,6 +17,8 @@ package com.android.server; import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED; +import static android.telephony.TelephonyRegistryManager.SIM_ACTIVATION_TYPE_DATA; +import static android.telephony.TelephonyRegistryManager.SIM_ACTIVATION_TYPE_VOICE; import static java.util.Arrays.copyOf; @@ -1201,10 +1203,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { synchronized (mRecords) { if (validatePhoneId(phoneId)) { switch (activationType) { - case TelephonyManager.SIM_ACTIVATION_TYPE_VOICE: + case SIM_ACTIVATION_TYPE_VOICE: mVoiceActivationState[phoneId] = activationState; break; - case TelephonyManager.SIM_ACTIVATION_TYPE_DATA: + case SIM_ACTIVATION_TYPE_DATA: mDataActivationState[phoneId] = activationState; break; default: @@ -1217,10 +1219,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { + " state=" + activationState); } try { - if ((activationType == TelephonyManager.SIM_ACTIVATION_TYPE_VOICE) && - r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) && - idMatch(r.subId, subId, phoneId)) { + if ((activationType == SIM_ACTIVATION_TYPE_VOICE) + && r.matchPhoneStateListenerEvent( + PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) + && idMatch(r.subId, subId, phoneId)) { if (DBG) { log("notifyVoiceActivationStateForPhoneId: callback.onVASC r=" + r + " subId=" + subId + " phoneId=" + phoneId @@ -1228,10 +1230,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } r.callback.onVoiceActivationStateChanged(activationState); } - if ((activationType == TelephonyManager.SIM_ACTIVATION_TYPE_DATA) && - r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) && - idMatch(r.subId, subId, phoneId)) { + if ((activationType == SIM_ACTIVATION_TYPE_DATA) + && r.matchPhoneStateListenerEvent( + PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) + && idMatch(r.subId, subId, phoneId)) { if (DBG) { log("notifyDataActivationStateForPhoneId: callback.onDASC r=" + r + " subId=" + subId + " phoneId=" + phoneId diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 53a5fc6c203a..663e8a2929a0 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -8365,6 +8365,31 @@ public class ActivityManagerService extends IActivityManager.Stub requestBugReportWithDescription(null, null, BugreportParams.BUGREPORT_MODE_REMOTE); } + /** + * Launches a bugreport-whitelisted app to handle a bugreport. + * + * <p>Allows a bug report handler app to take bugreports on the user's behalf. The handler can + * be predefined in the config, meant to be launched with the primary user. The user can + * override this with a different (or same) handler app on possibly a different user. This is + * useful for capturing bug reports from work profile, for instance. + * + * @return true if there is a bugreport-whitelisted app to handle a bugreport, or false + * otherwise. + */ + @Override + public boolean launchBugReportHandlerApp() { + if (!BugReportHandlerUtil.isBugReportHandlerEnabled(mContext)) { + return false; + } + + // Always log caller, even if it does not have permission to dump. + Slog.i(TAG, "launchBugReportHandlerApp requested by UID " + Binder.getCallingUid()); + enforceCallingPermission(android.Manifest.permission.DUMP, + "launchBugReportHandlerApp"); + + return BugReportHandlerUtil.launchBugReportHandlerApp(mContext); + } + public void registerProcessObserver(IProcessObserver observer) { enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER, "registerProcessObserver()"); diff --git a/services/core/java/com/android/server/am/BugReportHandlerUtil.java b/services/core/java/com/android/server/am/BugReportHandlerUtil.java new file mode 100644 index 000000000000..ba89fce0b3f8 --- /dev/null +++ b/services/core/java/com/android/server/am/BugReportHandlerUtil.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; + +import android.app.BroadcastOptions; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Binder; +import android.os.UserHandle; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.ArraySet; +import android.util.Slog; + +import com.android.server.SystemConfig; + +import java.util.List; + +/** + * Static utility methods related to BugReportHandler. + */ +public final class BugReportHandlerUtil { + private static final String TAG = TAG_WITH_CLASS_NAME ? "BugReportHandlerUtil" : TAG_AM; + private static final String SHELL_APP_PACKAGE = "com.android.shell"; + private static final String INTENT_BUGREPORT_REQUESTED = + "com.android.internal.intent.action.BUGREPORT_REQUESTED"; + + /** + * Check is BugReportHandler enabled on the device. + * + * @param context Context + * @return true if BugReportHandler is enabled, or false otherwise + */ + static boolean isBugReportHandlerEnabled(Context context) { + return context.getResources().getBoolean( + com.android.internal.R.bool.config_bugReportHandlerEnabled); + } + + /** + * Launches a bugreport-whitelisted app to handle a bugreport. + * + * <p>Allows a bug report handler app to take bugreports on the user's behalf. The handler can + * be predefined in the config, meant to be launched with the primary user. The user can + * override this with a different (or same) handler app on possibly a different user. This is + * useful for capturing bug reports from work profile, for instance. + * + * @param context Context + * @return true if there is a bugreport-whitelisted app to handle a bugreport, or false + * otherwise + */ + static boolean launchBugReportHandlerApp(Context context) { + if (!isBugReportHandlerEnabled(context)) { + return false; + } + + String handlerApp = getCustomBugReportHandlerApp(context); + if (isShellApp(handlerApp)) { + return false; + } + + int handlerUser = getCustomBugReportHandlerUser(context); + if (!isValidBugReportHandlerApp(handlerApp)) { + handlerApp = getDefaultBugReportHandlerApp(context); + handlerUser = UserHandle.USER_SYSTEM; + } else if (getBugReportHandlerAppReceivers(context, handlerApp, handlerUser).isEmpty()) { + // It looks like the settings are outdated, reset outdated settings. + // + // i.e. + // If user chooses which profile and which bugreport-whitelisted app in that + // profile to handle a bugreport, then user remove the profile. + // === RESULT === + // The chosen bugreport handler app is outdated because the profile is removed, + // so reset the chosen app and profile + handlerApp = getDefaultBugReportHandlerApp(context); + handlerUser = UserHandle.USER_SYSTEM; + resetCustomBugreportHandlerAppAndUser(context); + } + + if (isShellApp(handlerApp) || !isValidBugReportHandlerApp(handlerApp) + || getBugReportHandlerAppReceivers(context, handlerApp, handlerUser).isEmpty()) { + return false; + } + + Slog.i(TAG, "Launching bug report handler app: " + handlerApp); + Intent intent = new Intent(INTENT_BUGREPORT_REQUESTED); + intent.setPackage(handlerApp); + intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + // Send broadcast to the receiver while allowing starting activity from background + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setBackgroundActivityStartsAllowed(true); + final long identity = Binder.clearCallingIdentity(); + try { + context.sendBroadcastAsUser(intent, UserHandle.of(handlerUser), + android.Manifest.permission.DUMP, + options.toBundle()); + } catch (RuntimeException e) { + Slog.e(TAG, "Error while trying to launch bugreport handler app.", e); + return false; + } finally { + Binder.restoreCallingIdentity(identity); + } + return true; + } + + private static String getCustomBugReportHandlerApp(Context context) { + // Get the package of custom bugreport handler app + return Settings.Global.getString(context.getContentResolver(), + Settings.Global.CUSTOM_BUGREPORT_HANDLER_APP); + } + + private static int getCustomBugReportHandlerUser(Context context) { + return Settings.Global.getInt(context.getContentResolver(), + Settings.Global.CUSTOM_BUGREPORT_HANDLER_USER, UserHandle.USER_NULL); + } + + private static boolean isShellApp(String app) { + return SHELL_APP_PACKAGE.equals(app); + } + + private static boolean isValidBugReportHandlerApp(String app) { + return !TextUtils.isEmpty(app) && isBugreportWhitelistedApp(app); + } + + private static boolean isBugreportWhitelistedApp(String app) { + // Verify the app is bugreport-whitelisted + final ArraySet<String> whitelistedApps = SystemConfig.getInstance() + .getBugreportWhitelistedPackages(); + return whitelistedApps.contains(app); + } + + private static List<ResolveInfo> getBugReportHandlerAppReceivers(Context context, + String handlerApp, int handlerUser) { + // Use the app package and the user id to retrieve the receiver that can handle a + // broadcast of the intent. + Intent intent = new Intent(INTENT_BUGREPORT_REQUESTED); + intent.setPackage(handlerApp); + return context.getPackageManager() + .queryBroadcastReceiversAsUser(intent, PackageManager.MATCH_SYSTEM_ONLY, + handlerUser); + } + + private static String getDefaultBugReportHandlerApp(Context context) { + return context.getResources().getString( + com.android.internal.R.string.config_defaultBugReportHandlerApp); + } + + private static void resetCustomBugreportHandlerAppAndUser(Context context) { + final long identity = Binder.clearCallingIdentity(); + try { + Settings.Global.putString(context.getContentResolver(), + Settings.Global.CUSTOM_BUGREPORT_HANDLER_APP, + getDefaultBugReportHandlerApp(context)); + Settings.Global.putInt(context.getContentResolver(), + Settings.Global.CUSTOM_BUGREPORT_HANDLER_USER, UserHandle.USER_SYSTEM); + } finally { + Binder.restoreCallingIdentity(identity); + } + } +} diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 0591704a0502..12f46569bd20 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -376,7 +376,11 @@ public final class OomAdjuster { ConnectionRecord cr = pr.connections.valueAt(i); ProcessRecord service = (cr.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0 ? cr.binding.service.isolatedProc : cr.binding.service.app; - if (service == null || service == pr || (containsCycle |= service.mReachable)) { + if (service == null || service == pr) { + continue; + } + containsCycle |= service.mReachable; + if (service.mReachable) { continue; } if ((cr.flags & (Context.BIND_WAIVE_PRIORITY @@ -394,6 +398,10 @@ public final class OomAdjuster { if (provider == null || provider == pr || (containsCycle |= provider.mReachable)) { continue; } + containsCycle |= provider.mReachable; + if (provider.mReachable) { + continue; + } queue.offer(provider); provider.mReachable = true; } @@ -482,12 +490,15 @@ public final class OomAdjuster { // need to reset cycle state before calling computeOomAdjLocked because of service conns for (int i = numProc - 1; i >= 0; i--) { ProcessRecord app = activeProcesses.get(i); - app.containsCycle = false; app.mReachable = false; - app.setCurRawProcState(PROCESS_STATE_CACHED_EMPTY); - app.setCurRawAdj(ProcessList.UNKNOWN_ADJ); - app.setCapability = PROCESS_CAPABILITY_NONE; - app.resetCachedInfo(); + // No need to compute again it has been evaluated in previous iteration + if (app.adjSeq != mAdjSeq) { + app.containsCycle = false; + app.setCurRawProcState(PROCESS_STATE_CACHED_EMPTY); + app.setCurRawAdj(ProcessList.UNKNOWN_ADJ); + app.setCapability = PROCESS_CAPABILITY_NONE; + app.resetCachedInfo(); + } } for (int i = numProc - 1; i >= 0; i--) { ProcessRecord app = activeProcesses.get(i); diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java index bc83780ad82d..21795184b1bd 100644 --- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java +++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java @@ -31,7 +31,6 @@ import android.net.NetworkSpecifier; import android.net.StringNetworkSpecifier; import android.net.wifi.WifiInfo; import android.os.UserHandle; -import android.telephony.AccessNetworkConstants.TransportType; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -105,8 +104,7 @@ public class NetworkNotificationManager { return -1; } - // TODO: Remove @TransportType or change it to @Transport. - private static String getTransportName(@TransportType int transportType) { + private static String getTransportName(final int transportType) { Resources r = Resources.getSystem(); String[] networkTypes = r.getStringArray(R.array.network_switch_type_name); try { diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index ec11a97cfc30..14ef2d3a38e1 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -136,8 +136,11 @@ public class AppsFilter { DeviceConfig.addOnPropertiesChangedListener( NAMESPACE_PACKAGE_MANAGER_SERVICE, FgThread.getExecutor(), properties -> { - synchronized (FeatureConfigImpl.this) { - mFeatureEnabled = properties.getBoolean(FILTERING_ENABLED_NAME, false); + if (properties.getKeyset().contains(FILTERING_ENABLED_NAME)) { + synchronized (FeatureConfigImpl.this) { + mFeatureEnabled = properties.getBoolean(FILTERING_ENABLED_NAME, + false); + } } }); } diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java new file mode 100644 index 000000000000..0719797ed85a --- /dev/null +++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.ApplicationInfo; +import android.content.pm.IDataLoader; +import android.content.pm.IDataLoaderManager; +import android.content.pm.IDataLoaderStatusListener; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.Slog; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.SystemService; + +import java.util.List; + +/** + * Data loader manager service manages data loader binder services. + * + * @hide + */ +public class DataLoaderManagerService extends SystemService { + private static final String TAG = "DataLoaderManager"; + private final Context mContext; + private final DataLoaderManagerBinderService mBinderService; + private final Object mLock = new Object(); + @GuardedBy("mLock") + private SparseArray<DataLoaderServiceConnection> mServiceConnections; + + public DataLoaderManagerService(Context context) { + super(context); + mContext = context; + mBinderService = new DataLoaderManagerBinderService(); + } + + @Override + public void onStart() { + publishBinderService(Context.DATA_LOADER_MANAGER_SERVICE, mBinderService); + } + + final class DataLoaderManagerBinderService extends IDataLoaderManager.Stub { + @Override + public boolean initializeDataLoader(int dataLoaderId, Bundle params, + IDataLoaderStatusListener listener) { + synchronized (mLock) { + if (mServiceConnections == null) { + mServiceConnections = new SparseArray<>(); + } + if (mServiceConnections.get(dataLoaderId) != null) { + Slog.e(TAG, "Data loader of ID=" + dataLoaderId + " already exists."); + return false; + } + } + CharSequence packageNameSeq = params.getCharSequence("packageName"); + if (packageNameSeq == null) { + Slog.e(TAG, "Must specify package name."); + return false; + } + String packageName = packageNameSeq.toString(); + ComponentName dataLoaderComponent = getDataLoaderServiceName(packageName); + if (dataLoaderComponent == null) { + return false; + } + // Binds to the specific data loader service + DataLoaderServiceConnection connection = + new DataLoaderServiceConnection(dataLoaderId, params, listener); + Intent intent = new Intent(); + intent.setComponent(dataLoaderComponent); + if (!mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE, + UserHandle.of(UserHandle.getCallingUserId()))) { + Slog.e(TAG, "Failed to bind to data loader binder service."); + mContext.unbindService(connection); + return false; + } + return true; + } + + /** + * Find the ComponentName of the data loader service provider, given its package name. + * + * @param packageName the package name of the provider. + * @return ComponentName of the data loader service provider. Null if provider not found. + */ + private @Nullable ComponentName getDataLoaderServiceName(String packageName) { + final PackageManager pm = mContext.getPackageManager(); + if (pm == null) { + Slog.e(TAG, "PackageManager is not available."); + return null; + } + Intent intent = new Intent(Intent.ACTION_LOAD_DATA); + intent.setPackage(packageName); + List<ResolveInfo> services = + pm.queryIntentServicesAsUser(intent, 0, UserHandle.getCallingUserId()); + if (services == null || services.isEmpty()) { + Slog.e(TAG, + "Failed to find data loader service provider in package " + packageName); + return null; + } + + // TODO(b/136132412): better way to enable privileged data loaders in tests + boolean checkLoader = + android.os.SystemProperties.getBoolean("incremental.check_loader", false); + int numServices = services.size(); + for (int i = 0; i < numServices; i++) { + ResolveInfo ri = services.get(i); + ComponentName componentName = new ComponentName( + ri.serviceInfo.packageName, ri.serviceInfo.name); + // There should only be one matching provider inside the given package. + // If there's more than one, return the first one found. + try { + ApplicationInfo ai = pm.getApplicationInfo(componentName.getPackageName(), 0); + if (checkLoader && !ai.isPrivilegedApp()) { + Slog.w(TAG, + "Data loader: " + componentName.getPackageName() + + " is not a privileged app, skipping."); + continue; + } + return componentName; + } catch (PackageManager.NameNotFoundException ex) { + Slog.w(TAG, + "Privileged data loader: " + componentName.getPackageName() + + " not found, skipping."); + } + + } + Slog.e(TAG, "Didn't find any matching data loader service provider."); + return null; + } + + /** + * Returns the binder object of a data loader, specified by its ID. + */ + @Override + public @Nullable IDataLoader getDataLoader(int dataLoaderId) { + synchronized (mLock) { + if (mServiceConnections == null) { + return null; + } + DataLoaderServiceConnection serviceConnection = mServiceConnections.get( + dataLoaderId, null); + if (serviceConnection == null) { + return null; + } + return serviceConnection.getDataLoader(); + } + } + + /** + * Destroys a data loader binder service, specified by its ID. + */ + @Override + public void destroyDataLoader(int dataLoaderId) { + synchronized (mLock) { + if (mServiceConnections == null) { + return; + } + DataLoaderServiceConnection serviceConnection = mServiceConnections.get( + dataLoaderId, null); + + if (serviceConnection == null) { + return; + } + serviceConnection.destroy(); + } + } + } + + class DataLoaderServiceConnection implements ServiceConnection { + final int mId; + final Bundle mParams; + final IDataLoaderStatusListener mListener; + IDataLoader mDataLoader; + + DataLoaderServiceConnection(int id, Bundle params, IDataLoaderStatusListener listener) { + mId = id; + mParams = params; + mListener = listener; + mDataLoader = null; + } + + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + mDataLoader = IDataLoader.Stub.asInterface(service); + synchronized (mLock) { + mServiceConnections.append(mId, this); + } + try { + mDataLoader.create(mId, mParams, mListener); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to create data loader service.", e); + } + } + + @Override + public void onServiceDisconnected(ComponentName arg0) { + remove(); + } + + IDataLoader getDataLoader() { + return mDataLoader; + } + + void destroy() { + try { + mDataLoader.destroy(); + } catch (RemoteException ignored) { + } + mContext.unbindService(this); + } + + private void remove() { + synchronized (mLock) { + mServiceConnections.remove(mId); + if (mServiceConnections.size() == 0) { + mServiceConnections = null; + } + } + mParams.clear(); + } + } +} diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 4852947619f9..883baf7886af 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -54,6 +54,7 @@ import android.content.IntentSender; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageInstallObserver2; import android.content.pm.IPackageInstallerSession; +import android.content.pm.InstallationFile; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionInfo; @@ -81,6 +82,8 @@ import android.os.Process; import android.os.RevocableFileDescriptor; import android.os.SystemProperties; import android.os.UserHandle; +import android.os.incremental.IncrementalFileStorages; +import android.os.incremental.IncrementalManager; import android.os.storage.StorageManager; import android.stats.devicepolicy.DevicePolicyEnums; import android.system.ErrnoException; @@ -309,6 +312,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private boolean mVerityFound; + private IncrementalFileStorages mIncrementalFileStorages; + private static final FileFilter sAddedFilter = new FileFilter() { @Override public boolean accept(File file) { @@ -471,6 +476,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mStagedSessionErrorCode = stagedSessionErrorCode; mStagedSessionErrorMessage = stagedSessionErrorMessage != null ? stagedSessionErrorMessage : ""; + + // TODO(b/136132412): sanity check if session should not be incremental + if (!params.isStaged && params.incrementalParams != null + && !params.incrementalParams.getPackageName().isEmpty()) { + IncrementalManager incrementalManager = (IncrementalManager) mContext.getSystemService( + Context.INCREMENTAL_SERVICE); + if (incrementalManager != null) { + mIncrementalFileStorages = + new IncrementalFileStorages(mPackageName, stageDir, incrementalManager, + params.incrementalParams); + } + } } public SessionInfo generateInfo() { @@ -874,10 +891,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } + if (mIncrementalFileStorages != null) { + mIncrementalFileStorages.finishSetUp(); + } + mHandler.obtainMessage(MSG_SEAL, statusReceiver).sendToTarget(); } private void handleSeal(@NonNull IntentSender statusReceiver) { + // TODO(b/136132412): update with new APIs + if (mIncrementalFileStorages != null) { + mIncrementalFileStorages.startLoading(); + } if (!markAsCommitted(statusReceiver)) { return; } @@ -1492,6 +1517,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { computeProgressLocked(true); // Unpack native libraries + // TODO(b/136132412): skip for incremental installation extractNativeLibraries(stageDir, params.abiOverride, mayInheritNativeLibs()); } @@ -2182,7 +2208,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } destroyInternal(); } - dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null); } @@ -2268,6 +2293,20 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return mParentSessionId; } + @Override + public void addFile(@NonNull String name, long size, @NonNull byte[] metadata) { + if (mIncrementalFileStorages == null) { + throw new IllegalStateException( + "Cannot add Incremental File to a non-Incremental session."); + } + try { + mIncrementalFileStorages.addFile(new InstallationFile(name, size, metadata)); + } catch (IOException ex) { + throw new IllegalStateException( + "Failed to add and configure Incremental File: " + name, ex); + } + } + private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) { final IntentSender statusReceiver; final String packageName; @@ -2390,6 +2429,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // since these packages are supposed to be read on reboot. // Those dirs are deleted when the staged session has reached a final state. if (stageDir != null && !params.isStaged) { + if (mIncrementalFileStorages != null) { + mIncrementalFileStorages.cleanUp(); + } try { mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath()); } catch (InstallerException ignored) { @@ -2403,6 +2445,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mSessionProvider.getSession(childSessionId).cleanStageDir(); } } else { + if (mIncrementalFileStorages != null) { + mIncrementalFileStorages.cleanUp(); + } try { mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath()); } catch (InstallerException ignored) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 4e0e4ffb3a13..c5e79426a421 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -153,6 +153,7 @@ import android.content.pm.IPackageManager; import android.content.pm.IPackageManagerNative; import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageStatsObserver; +import android.content.pm.InstallSourceInfo; import android.content.pm.InstantAppInfo; import android.content.pm.InstantAppRequest; import android.content.pm.InstrumentationInfo; @@ -20128,17 +20129,93 @@ public class PackageManagerService extends IPackageManager.Stub public String getInstallerPackageName(String packageName) { final int callingUid = Binder.getCallingUid(); synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(packageName); - if (shouldFilterApplicationLocked( - ps, callingUid, UserHandle.getUserId(callingUid))) { - return null; + final InstallSource installSource = getInstallSourceLocked(packageName, callingUid); + if (installSource == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); } - // InstallerPackageName for Apex is not stored in PackageManager - if (ps == null && mApexManager.isApexPackage(packageName)) { + String installerPackageName = installSource.installerPackageName; + if (installerPackageName != null) { + final PackageSetting ps = mSettings.mPackages.get(installerPackageName); + if (ps == null || shouldFilterApplicationLocked(ps, callingUid, + UserHandle.getUserId(callingUid))) { + installerPackageName = null; + } + } + return installerPackageName; + } + } + + @Override + @Nullable + public InstallSourceInfo getInstallSourceInfo(String packageName) { + final int callingUid = Binder.getCallingUid(); + final int userId = UserHandle.getUserId(callingUid); + + String installerPackageName; + String initiatingPackageName; + String originatingPackageName; + + synchronized (mLock) { + final InstallSource installSource = getInstallSourceLocked(packageName, callingUid); + if (installSource == null) { return null; } - return mSettings.getInstallerPackageNameLPr(packageName); + + installerPackageName = installSource.installerPackageName; + if (installerPackageName != null) { + final PackageSetting ps = mSettings.mPackages.get(installerPackageName); + if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) { + installerPackageName = null; + } + } + + // All installSource strings are interned, so == is ok here + if (installSource.initiatingPackageName == installSource.installerPackageName) { + // The installer and initiator will often be the same, and when they are + // we can skip doing the same check again. + initiatingPackageName = installerPackageName; + } else { + initiatingPackageName = installSource.initiatingPackageName; + final PackageSetting ps = mSettings.mPackages.get(initiatingPackageName); + if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) { + initiatingPackageName = null; + } + + } + originatingPackageName = installSource.originatingPackageName; + if (originatingPackageName != null) { + final PackageSetting ps = mSettings.mPackages.get(originatingPackageName); + if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) { + originatingPackageName = null; + } + } + } + + if (originatingPackageName != null && mContext.checkCallingOrSelfPermission( + Manifest.permission.INSTALL_PACKAGES) != PackageManager.PERMISSION_GRANTED) { + originatingPackageName = null; } + + return new InstallSourceInfo(initiatingPackageName, originatingPackageName, + installerPackageName); + } + + @GuardedBy("mLock") + @Nullable + private InstallSource getInstallSourceLocked(String packageName, int callingUid) { + final PackageSetting ps = mSettings.mPackages.get(packageName); + + // Installer info for Apex is not stored in PackageManager + if (ps == null && mApexManager.isApexPackage(packageName)) { + return InstallSource.EMPTY; + } + + if (ps == null || shouldFilterApplicationLocked(ps, callingUid, + UserHandle.getUserId(callingUid))) { + return null; + } + + return ps.installSource; } public boolean isOrphaned(String packageName) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index f67d3c0f7523..6e67687ae6d7 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -4257,14 +4257,6 @@ public final class Settings { return userState.isMatch(componentInfo, flags); } - String getInstallerPackageNameLPr(String packageName) { - final PackageSetting pkg = mPackages.get(packageName); - if (pkg == null) { - throw new IllegalArgumentException("Unknown package: " + packageName); - } - return pkg.installSource.installerPackageName; - } - boolean isOrphaned(String packageName) { final PackageSetting pkg = mPackages.get(packageName); if (pkg == null) { diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index 3c4e3f64a52e..9b9f93f7b5c4 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -29,10 +29,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.AppOpsManager; -import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -171,23 +169,6 @@ public final class PermissionPolicyService extends SystemService { } catch (RemoteException doesNotHappen) { Slog.wtf(LOG_TAG, "Cannot set up app-ops listener"); } - - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); - intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); - intentFilter.addDataScheme("package"); - - getContext().registerReceiverAsUser(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - UserHandle user = - UserHandle.getUserHandleForUid(intent.getIntExtra(Intent.EXTRA_UID, -1)); - new PermissionControllerManager( - getUserContext(getContext(), user), FgThread.getHandler()) - .updateUserSensitive(); - } - }, UserHandle.ALL, intentFilter, null, null); - } /** diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 114a16a96f07..14617d38739e 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -203,8 +203,11 @@ public final class PowerManagerService extends SystemService // System Property indicating that retail demo mode is currently enabled. private static final String SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED = "sys.retaildemo.enabled"; - // Possible reasons for shutting down or reboot for use in REBOOT_PROPERTY(sys.boot.reason) - // which is set by bootstat + // System property for last reboot reason + private static final String SYSTEM_PROPERTY_REBOOT_REASON = "sys.boot.reason"; + + // Possible reasons for shutting down or reboot for use in + // SYSTEM_PROPERTY_REBOOT_REASON(sys.boot.reason) which is set by bootstat private static final String REASON_SHUTDOWN = "shutdown"; private static final String REASON_REBOOT = "reboot"; private static final String REASON_USERREQUESTED = "shutdown,userrequested"; @@ -225,9 +228,6 @@ public final class PowerManagerService extends SystemService private static final int HALT_MODE_REBOOT = 1; private static final int HALT_MODE_REBOOT_SAFE_MODE = 2; - // property for last reboot reason - private static final String REBOOT_PROPERTY = "sys.boot.reason"; - private final Context mContext; private final ServiceThread mHandlerThread; private final PowerManagerHandler mHandler; @@ -240,6 +240,7 @@ public final class PowerManagerService extends SystemService private final BinderService mBinderService; private final LocalService mLocalService; private final NativeWrapper mNativeWrapper; + private final SystemPropertiesWrapper mSystemProperties; private final Injector mInjector; private LightsManager mLightsManager; @@ -756,6 +757,20 @@ public final class PowerManagerService extends SystemService InattentiveSleepWarningController createInattentiveSleepWarningController() { return new InattentiveSleepWarningController(); } + + public SystemPropertiesWrapper createSystemPropertiesWrapper() { + return new SystemPropertiesWrapper() { + @Override + public String get(String key, String def) { + return SystemProperties.get(key, def); + } + + @Override + public void set(String key, String val) { + SystemProperties.set(key, val); + } + }; + } } final Constants mConstants; @@ -781,6 +796,7 @@ public final class PowerManagerService extends SystemService mBinderService = new BinderService(); mLocalService = new LocalService(); mNativeWrapper = injector.createNativeWrapper(); + mSystemProperties = injector.createSystemPropertiesWrapper(); mInjector = injector; mHandlerThread = new ServiceThread(TAG, @@ -816,7 +832,7 @@ public final class PowerManagerService extends SystemService mHalInteractiveModeEnabled = true; mWakefulness = WAKEFULNESS_AWAKE; - sQuiescent = SystemProperties.get(SYSTEM_PROPERTY_QUIESCENT, "0").equals("1"); + sQuiescent = mSystemProperties.get(SYSTEM_PROPERTY_QUIESCENT, "0").equals("1"); mNativeWrapper.nativeInit(this); mNativeWrapper.nativeSetAutoSuspend(false); @@ -1067,8 +1083,9 @@ public final class PowerManagerService extends SystemService } final String retailDemoValue = UserManager.isDeviceInDemoMode(mContext) ? "1" : "0"; - if (!retailDemoValue.equals(SystemProperties.get(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED))) { - SystemProperties.set(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED, retailDemoValue); + if (!retailDemoValue.equals( + mSystemProperties.get(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED, null))) { + mSystemProperties.set(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED, retailDemoValue); } mScreenBrightnessModeSetting = Settings.System.getIntForUser(resolver, @@ -4820,7 +4837,7 @@ public final class PowerManagerService extends SystemService final long ident = Binder.clearCallingIdentity(); try { - return getLastShutdownReasonInternal(REBOOT_PROPERTY); + return getLastShutdownReasonInternal(); } finally { Binder.restoreCallingIdentity(ident); } @@ -5054,12 +5071,8 @@ public final class PowerManagerService extends SystemService } @VisibleForTesting - // lastRebootReasonProperty argument to permit testing - int getLastShutdownReasonInternal(String lastRebootReasonProperty) { - String line = SystemProperties.get(lastRebootReasonProperty); - if (line == null) { - return PowerManager.SHUTDOWN_REASON_UNKNOWN; - } + int getLastShutdownReasonInternal() { + String line = mSystemProperties.get(SYSTEM_PROPERTY_REBOOT_REASON, null); switch (line) { case REASON_SHUTDOWN: return PowerManager.SHUTDOWN_REASON_SHUTDOWN; diff --git a/services/core/java/com/android/server/power/SystemPropertiesWrapper.java b/services/core/java/com/android/server/power/SystemPropertiesWrapper.java new file mode 100644 index 000000000000..1acf798eb099 --- /dev/null +++ b/services/core/java/com/android/server/power/SystemPropertiesWrapper.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.power; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.SystemProperties; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * Wrapper interface to access {@link SystemProperties}. + * + * @hide + */ +@VisibleForTesting +interface SystemPropertiesWrapper { + /** + * Get the String value for the given {@code key}. + * + * @param key the key to lookup + * @param def the default value in case the property is not set or empty + * @return if the {@code key} isn't found, return {@code def} if it isn't null, or an empty + * string otherwise + */ + @NonNull + String get(@NonNull String key, @Nullable String def); + + /** + * Set the value for the given {@code key} to {@code val}. + * + * @throws IllegalArgumentException if the {@code val} exceeds 91 characters + * @throws RuntimeException if the property cannot be set, for example, if it was blocked by + * SELinux. libc will log the underlying reason. + */ + void set(@NonNull String key, @Nullable String val); +} diff --git a/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java b/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java index 03c96e99a5ad..6bdb5cea7f9c 100644 --- a/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java +++ b/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java @@ -55,7 +55,16 @@ public final class TimingsTraceAndSlog extends TimingsTraceLog { * Default constructor using {@code system_server} tags. */ public TimingsTraceAndSlog() { - this(SYSTEM_SERVER_TIMING_TAG, Trace.TRACE_TAG_SYSTEM_SERVER); + this(SYSTEM_SERVER_TIMING_TAG); + } + + /** + * Custom constructor using {@code system_server} trace tag. + * + * @param tag {@code logcat} tag + */ + public TimingsTraceAndSlog(@NonNull String tag) { + this(tag, Trace.TRACE_TAG_SYSTEM_SERVER); } /** diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index d66aa18950d0..03139d2e5e03 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -104,6 +104,7 @@ import com.android.server.EventLogTags; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.utils.TimingsTraceAndSlog; import com.android.server.wm.WindowManagerInternal; import libcore.io.IoUtils; @@ -1785,25 +1786,26 @@ public class WallpaperManagerService extends IWallpaperManager.Stub @Override public void onUnlockUser(final int userId) { - synchronized (mLock) { - if (mCurrentUserId == userId) { - if (mWaitingForUnlock) { - // the desired wallpaper is not direct-boot aware, load it now - final WallpaperData systemWallpaper = - getWallpaperSafeLocked(userId, FLAG_SYSTEM); - switchWallpaper(systemWallpaper, null); - notifyCallbacksLocked(systemWallpaper); - } - - // Make sure that the SELinux labeling of all the relevant files is correct. - // This corrects for mislabeling bugs that might have arisen from move-to - // operations involving the wallpaper files. This isn't timing-critical, - // so we do it in the background to avoid holding up the user unlock operation. - if (!mUserRestorecon.get(userId)) { - mUserRestorecon.put(userId, true); - Runnable relabeler = new Runnable() { - @Override - public void run() { + TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG); + t.traceBegin("on-unlock-user-" + userId); + try { + synchronized (mLock) { + if (mCurrentUserId == userId) { + if (mWaitingForUnlock) { + // the desired wallpaper is not direct-boot aware, load it now + final WallpaperData systemWallpaper = + getWallpaperSafeLocked(userId, FLAG_SYSTEM); + switchWallpaper(systemWallpaper, null); + notifyCallbacksLocked(systemWallpaper); + } + + // Make sure that the SELinux labeling of all the relevant files is correct. + // This corrects for mislabeling bugs that might have arisen from move-to + // operations involving the wallpaper files. This isn't timing-critical, + // so we do it in the background to avoid holding up the user unlock operation. + if (!mUserRestorecon.get(userId)) { + mUserRestorecon.put(userId, true); + Runnable relabeler = () -> { final File wallpaperDir = getWallpaperDir(userId); for (String filename : sPerUserFiles) { File f = new File(wallpaperDir, filename); @@ -1811,11 +1813,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub SELinux.restorecon(f); } } - } - }; - BackgroundThread.getHandler().post(relabeler); + }; + BackgroundThread.getHandler().post(relabeler); + } } } + } finally { + t.traceEnd(); } } @@ -1833,31 +1837,37 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } void switchUser(int userId, IRemoteCallback reply) { - final WallpaperData systemWallpaper; - final WallpaperData lockWallpaper; - synchronized (mLock) { - if (mCurrentUserId == userId) { - return; - } - mCurrentUserId = userId; - systemWallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM); - final WallpaperData tmpLockWallpaper = mLockWallpaperMap.get(userId); - lockWallpaper = tmpLockWallpaper == null ? systemWallpaper : tmpLockWallpaper; - // Not started watching yet, in case wallpaper data was loaded for other reasons. - if (systemWallpaper.wallpaperObserver == null) { - systemWallpaper.wallpaperObserver = new WallpaperObserver(systemWallpaper); - systemWallpaper.wallpaperObserver.startWatching(); + TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG); + t.traceBegin("switch-user-" + userId); + try { + final WallpaperData systemWallpaper; + final WallpaperData lockWallpaper; + synchronized (mLock) { + if (mCurrentUserId == userId) { + return; + } + mCurrentUserId = userId; + systemWallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM); + final WallpaperData tmpLockWallpaper = mLockWallpaperMap.get(userId); + lockWallpaper = tmpLockWallpaper == null ? systemWallpaper : tmpLockWallpaper; + // Not started watching yet, in case wallpaper data was loaded for other reasons. + if (systemWallpaper.wallpaperObserver == null) { + systemWallpaper.wallpaperObserver = new WallpaperObserver(systemWallpaper); + systemWallpaper.wallpaperObserver.startWatching(); + } + switchWallpaper(systemWallpaper, reply); } - switchWallpaper(systemWallpaper, reply); - } - // Offload color extraction to another thread since switchUser will be called - // from the main thread. - FgThread.getHandler().post(() -> { - notifyWallpaperColorsChanged(systemWallpaper, FLAG_SYSTEM); - notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK); - notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM); - }); + // Offload color extraction to another thread since switchUser will be called + // from the main thread. + FgThread.getHandler().post(() -> { + notifyWallpaperColorsChanged(systemWallpaper, FLAG_SYSTEM); + notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK); + notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM); + }); + } finally { + t.traceEnd(); + } } void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) { diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 77e557b87648..2283041367ef 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -2215,9 +2215,22 @@ public class DisplayPolicy { } private void offsetInputMethodWindowLw(WindowState win, DisplayFrames displayFrames) { + final int rotation = displayFrames.mRotation; + final int navBarPosition = navigationBarPosition(displayFrames.mDisplayWidth, + displayFrames.mDisplayHeight, rotation); + int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top); top += win.getGivenContentInsetsLw().top; displayFrames.mContent.bottom = Math.min(displayFrames.mContent.bottom, top); + if (navBarPosition == NAV_BAR_BOTTOM) { + // Always account for the nav bar frame height on the bottom since in all navigation + // modes we make room to show the dismiss-ime button, even if the IME does not report + // insets (ie. when floating) + final int uimode = mService.mPolicy.getUiMode(); + final int navFrameHeight = getNavigationBarFrameHeight(rotation, uimode); + displayFrames.mContent.bottom = Math.min(displayFrames.mContent.bottom, + displayFrames.mUnrestricted.bottom - navFrameHeight); + } displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top); top = win.getVisibleFrameLw().top; top += win.getGivenVisibleInsetsLw().top; diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 88a38e02de6a..6e8d0b71353f 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -968,6 +968,11 @@ class Task extends WindowContainer<WindowContainer> { final ActivityStack oldStack = ((ActivityStack) oldParent); final ActivityStack newStack = ((ActivityStack) newParent); + // Task is going to be removed, clean it up before detaching from hierarchy. + if (oldParent != null && newParent == null) { + cleanUpResourcesForDestroy(); + } + mStack = newStack; super.onParentChanged(newParent, oldParent); @@ -1012,12 +1017,6 @@ class Task extends WindowContainer<WindowContainer> { updateOverrideConfigurationFromLaunchBounds(); } - // Task is being removed. - if (oldParent != null && newParent == null) { - cleanUpResourcesForDestroy(); - } - - // Update task bounds if needed. adjustBoundsForDisplayChangeIfNeeded(getDisplayContent()); diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 46faf3b40f85..ea90e49d879e 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -550,4 +550,10 @@ public abstract class WindowManagerInternal { * the next time the activities are opened. */ public abstract void clearSnapshotCache(); + + /** + * Assigns accessibility ID a window surface as a layer metadata. + */ + public abstract void setAccessibilityIdToSurfaceMetadata( + IBinder windowToken, int accessibilityWindowId); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 0a87fdaed3a1..51221dde3101 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7398,6 +7398,27 @@ public class WindowManagerService extends IWindowManager.Stub public @Nullable KeyInterceptionInfo getKeyInterceptionInfoFromToken(IBinder inputToken) { return mKeyInterceptionInfoForToken.get(inputToken); } + + @Override + public void setAccessibilityIdToSurfaceMetadata( + IBinder windowToken, int accessibilityWindowId) { + synchronized (mGlobalLock) { + final WindowState state = mWindowMap.get(windowToken); + if (state == null) { + Slog.w(TAG, "Cannot find window which accessibility connection is added to"); + return; + } + try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) { + t.setMetadata( + state.mSurfaceControl, + SurfaceControl.METADATA_ACCESSIBILITY_ID, + accessibilityWindowId); + t.apply(); + } finally { + SurfaceControl.closeTransaction(); + } + } + } } void registerAppFreezeListener(AppFreezeListener listener) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 8cc0736b7537..dcf4b38db8ab 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -35,6 +35,7 @@ import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_B import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON; +import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; @@ -2162,12 +2163,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return false; } - // Can be an IME target only if: - // 1. FLAG_NOT_FOCUSABLE is not set - // 2. FLAG_ALT_FOCUSABLE_IM is not set - // 3. not a starting window. - if (!WindowManager.LayoutParams.mayUseInputMethod(mAttrs.flags) - || mAttrs.type == TYPE_APPLICATION_STARTING) { + final int fl = mAttrs.flags & (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM); + final int type = mAttrs.type; + + // Can only be an IME target if both FLAG_NOT_FOCUSABLE and FLAG_ALT_FOCUSABLE_IM are set or + // both are cleared...and not a starting window. + if (fl != 0 && fl != (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM) + && type != TYPE_APPLICATION_STARTING) { return false; } diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp index 612a1e7074f9..dcff5a11aca0 100644 --- a/services/core/jni/com_android_server_VibratorService.cpp +++ b/services/core/jni/com_android_server_VibratorService.cpp @@ -338,7 +338,7 @@ static jlong vibratorPerformEffect(JNIEnv* env, jclass, jlong effect, jlong stre if (auto hal = getHal<aidl::IVibrator>()) { int32_t lengthMs; sp<AidlVibratorCallback> effectCallback = new AidlVibratorCallback(env, vibration); - aidl::Effect effectType(static_cast<aidl::Effect>(strength)); + aidl::Effect effectType(static_cast<aidl::Effect>(effect)); aidl::EffectStrength effectStrength(static_cast<aidl::EffectStrength>(strength)); auto status = hal->call(&aidl::IVibrator::perform, effectType, effectStrength, effectCallback, &lengthMs); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 2499ad816b2d..cb599be82aa6 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -139,6 +139,8 @@ import android.app.admin.SystemUpdatePolicy; import android.app.backup.IBackupManager; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.TimeDetector; +import android.app.timezonedetector.ManualTimeZoneSuggestion; +import android.app.timezonedetector.TimeZoneDetector; import android.app.trust.TrustManager; import android.app.usage.UsageStatsManagerInternal; import android.compat.annotation.ChangeId; @@ -1962,6 +1964,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return mContext.getSystemService(TimeDetector.class); } + TimeZoneDetector getTimeZoneDetector() { + return mContext.getSystemService(TimeZoneDetector.class); + } + ConnectivityManager getConnectivityManager() { return mContext.getSystemService(ConnectivityManager.class); } @@ -2748,6 +2754,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return null; } + // Code for handling failure from getActiveAdminWithPolicyForUidLocked to find an admin + // that satisfies the required policy. + // Throws a security exception with the right error message. if (who != null) { final int userId = UserHandle.getUserId(callingUid); final DevicePolicyData policy = getUserData(userId); @@ -2763,6 +2772,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { throw new SecurityException("Admin " + admin.info.getComponent() + " does not own the profile"); } + if (reqPolicy == DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER) { + throw new SecurityException("Admin " + admin.info.getComponent() + + " is not the profile owner on organization-owned device"); + } if (DA_DISALLOWED_POLICIES.contains(reqPolicy) && !isDeviceOwner && !isProfileOwner) { throw new SecurityException("Admin " + admin.info.getComponent() + " is not a device owner or profile owner, so may not use policy: " @@ -2869,12 +2882,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { ensureLocked(); final boolean ownsDevice = isDeviceOwner(admin.info.getComponent(), userId); final boolean ownsProfile = isProfileOwner(admin.info.getComponent(), userId); + final boolean ownsProfileOnOrganizationOwnedDevice = + isProfileOwnerOfOrganizationOwnedDevice(admin.info.getComponent(), userId); if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) { return ownsDevice; + } else if (reqPolicy == DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER) { + return ownsDevice || ownsProfileOnOrganizationOwnedDevice; } else if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) { // DO always has the PO power. - return ownsDevice || ownsProfile; + return ownsDevice || ownsProfileOnOrganizationOwnedDevice || ownsProfile; } else { boolean allowedToUsePolicy = ownsDevice || ownsProfile || !DA_DISALLOWED_POLICIES.contains(reqPolicy) @@ -5574,6 +5591,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + private void enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(ComponentName who) { + synchronized (getLockObject()) { + getActiveAdminForCallerLocked( + who, DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER); + } + } + private void enforceProfileOwnerOfOrganizationOwnedDevice(ActiveAdmin admin) { if (!isProfileOwnerOfOrganizationOwnedDevice(admin)) { throw new SecurityException(String.format("Provided admin %s is either not a profile " @@ -8071,21 +8095,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } - final int adminUserId = admin.getUserHandle().getIdentifier(); - - if (!isProfileOwner(admin.info.getComponent(), adminUserId)) { - Slog.w(LOG_TAG, String.format("%s is not profile owner of user %d", - admin.info.getComponent(), adminUserId)); - return false; - } - - if (!canProfileOwnerAccessDeviceIds(adminUserId)) { - Slog.w(LOG_TAG, String.format("Profile owner of user %d does not own the device.", - adminUserId)); - return false; - } + return isProfileOwnerOfOrganizationOwnedDevice( + admin.info.getComponent(), admin.getUserHandle().getIdentifier()); + } - return true; + private boolean isProfileOwnerOfOrganizationOwnedDevice(ComponentName who, int userId) { + return isProfileOwner(who, userId) && canProfileOwnerAccessDeviceIds(userId); } @Override @@ -11123,8 +11138,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) == 1) { return false; } + ManualTimeZoneSuggestion manualTimeZoneSuggestion = + TimeZoneDetector.createManualTimeZoneSuggestion( + timeZone, "DevicePolicyManagerService: setTimeZone"); mInjector.binderWithCleanCallingIdentity(() -> - mInjector.getAlarmManager().setTimeZone(timeZone)); + mInjector.getTimeZoneDetector().suggestManualTimeZone(manualTimeZoneSuggestion)); return true; } @@ -12377,7 +12395,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public String getWifiMacAddress(ComponentName admin) { // Make sure caller has DO. - enforceDeviceOwner(admin); + enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(admin); final long ident = mInjector.binderClearCallingIdentity(); try { diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java index 6c09239251bd..8e6114aaeffe 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -1053,6 +1053,35 @@ public class MockingOomAdjusterTests { @SuppressWarnings("GuardedBy") @Test + public void testUpdateOomAdj_DoOne_Service_Chain_BoundByFgService_Cycle_2() { + ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, + MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); + ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, + MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); + bindService(app, client, null, 0, mock(IBinder.class)); + bindService(client, app, null, 0, mock(IBinder.class)); + ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, + MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); + bindService(client2, client, null, 0, mock(IBinder.class)); + client.setHasForegroundServices(true, 0); + ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses; + lru.clear(); + lru.add(app); + lru.add(client); + lru.add(client2); + sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mOomAdjuster.updateOomAdjLocked(app, true, OomAdjuster.OOM_ADJ_REASON_NONE); + + assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, + SCHED_GROUP_DEFAULT); + assertProcStates(client, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, + SCHED_GROUP_DEFAULT); + assertProcStates(client2, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, + SCHED_GROUP_DEFAULT); + } + + @SuppressWarnings("GuardedBy") + @Test public void testUpdateOomAdj_DoOne_Service_Chain_BoundByFgService_Cycle_Branch() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java index 37f5b87ac115..335217719cc9 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java @@ -16,18 +16,39 @@ package com.android.server.accessibility; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import android.accessibilityservice.AccessibilityService; +import android.app.PendingIntent; +import android.app.PendingIntent.CanceledException; +import android.app.RemoteAction; import android.app.StatusBarManager; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.graphics.drawable.Icon; import android.os.Handler; +import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; + +import androidx.test.InstrumentationRegistry; import com.android.internal.util.ScreenshotHelper; +import com.android.server.LocalServices; +import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.wm.WindowManagerInternal; import org.junit.Before; @@ -35,55 +56,290 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + /** * Tests for SystemActionPerformer */ public class SystemActionPerformerTest { - SystemActionPerformer mSystemActionPerformer; + private static final int LATCH_TIMEOUT_MS = 500; + private static final int LEGACY_SYSTEM_ACTION_COUNT = 9; + private static final int NEW_ACTION_ID = 20; + private static final String LABEL_1 = "label1"; + private static final String LABEL_2 = "label2"; + private static final String INTENT_ACTION1 = "TESTACTION1"; + private static final String INTENT_ACTION2 = "TESTACTION2"; + private static final String DESCRIPTION1 = "description1"; + private static final String DESCRIPTION2 = "description2"; + private static final PendingIntent TEST_PENDING_INTENT_1 = PendingIntent.getBroadcast( + InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION1), 0); + private static final RemoteAction NEW_TEST_ACTION_1 = new RemoteAction( + Icon.createWithContentUri("content://test"), + LABEL_1, + DESCRIPTION1, + TEST_PENDING_INTENT_1); + private static final PendingIntent TEST_PENDING_INTENT_2 = PendingIntent.getBroadcast( + InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION2), 0); + private static final RemoteAction NEW_TEST_ACTION_2 = new RemoteAction( + Icon.createWithContentUri("content://test"), + LABEL_2, + DESCRIPTION2, + TEST_PENDING_INTENT_2); + + private static final AccessibilityAction NEW_ACCESSIBILITY_ACTION = + new AccessibilityAction(NEW_ACTION_ID, LABEL_1); + private static final AccessibilityAction LEGACY_NOTIFICATIONS_ACCESSIBILITY_ACTION = + new AccessibilityAction(AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS, LABEL_1); + private static final AccessibilityAction LEGACY_HOME_ACCESSIBILITY_ACTION = + new AccessibilityAction(AccessibilityService.GLOBAL_ACTION_HOME, LABEL_2); - @Mock Context mMockContext; - @Mock WindowManagerInternal mMockWindowManagerInternal; - @Mock StatusBarManager mMockStatusBarManager; - @Mock ScreenshotHelper mMockScreenshotHelper; + private SystemActionPerformer mSystemActionPerformer; + + @Mock private Context mMockContext; + @Mock private StatusBarManagerInternal mMockStatusBarManagerInternal; + @Mock private WindowManagerInternal mMockWindowManagerInternal; + @Mock private StatusBarManager mMockStatusBarManager; + @Mock private ScreenshotHelper mMockScreenshotHelper; + @Mock private SystemActionPerformer.SystemActionsChangedListener mMockListener; @Before public void setup() { MockitoAnnotations.initMocks(this); + LocalServices.removeServiceForTest(StatusBarManagerInternal.class); + LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal); + } + + private void setupWithMockContext() { + doReturn(mMockStatusBarManager).when( + mMockContext).getSystemService(android.app.Service.STATUS_BAR_SERVICE); + doReturn(InstrumentationRegistry.getContext().getResources()).when( + mMockContext).getResources(); + mSystemActionPerformer = new SystemActionPerformer( + mMockContext, + mMockWindowManagerInternal, + () -> mMockScreenshotHelper, + mMockListener); + } + + private void setupWithRealContext() { + mSystemActionPerformer = new SystemActionPerformer( + InstrumentationRegistry.getContext(), + mMockWindowManagerInternal, + () -> mMockScreenshotHelper, + mMockListener); + } + + // We need below two help functions because AccessbilityAction.equals function only compares + // action ids. To verify the test result here, we are also looking at action labels. + private void assertHasLegacyAccessibilityAction( + List<AccessibilityAction> actions, AccessibilityAction action) { + boolean foundAction = false; + for (AccessibilityAction a : actions) { + if ((a.getId() == action.getId()) && (a.getLabel().equals(action.getLabel()))) { + foundAction = true; + break; + } + } + assertTrue(foundAction); + } + + private void assertHasNoLegacyAccessibilityAction( + List<AccessibilityAction> actions, AccessibilityAction action) { + boolean foundAction = false; + for (AccessibilityAction a : actions) { + if ((a.getId() == action.getId()) && (a.getLabel().equals(action.getLabel()))) { + foundAction = true; + break; + } + } + assertFalse(foundAction); + } + + @Test + public void testRegisterSystemAction_addedIntoAvailableSystemActions() { + setupWithRealContext(); + // Before any new system action is registered, getSystemActions returns all legacy actions + List<AccessibilityAction> actions = mSystemActionPerformer.getSystemActions(); + assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size()); + // Register a new system action + mSystemActionPerformer.registerSystemAction(NEW_ACTION_ID, NEW_TEST_ACTION_1); + actions = mSystemActionPerformer.getSystemActions(); + assertEquals(LEGACY_SYSTEM_ACTION_COUNT + 1, actions.size()); + assertThat(actions, hasItem(NEW_ACCESSIBILITY_ACTION)); + } + + @Test + public void testRegisterSystemAction_overrideLegacyAction() { + setupWithRealContext(); + // Before any new system action is registered, getSystemActions returns all legacy actions + List<AccessibilityAction> actions = mSystemActionPerformer.getSystemActions(); + assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size()); + // Overriding a legacy system action using legacy notification action id + mSystemActionPerformer.registerSystemAction( + AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS, NEW_TEST_ACTION_1); + actions = mSystemActionPerformer.getSystemActions(); + assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size()); + assertHasLegacyAccessibilityAction(actions, LEGACY_NOTIFICATIONS_ACCESSIBILITY_ACTION); + } + + @Test + public void testUnregisterSystemAction_removeFromAvailableSystemActions() { + setupWithRealContext(); + // Before any new system action is registered, getSystemActions returns all legacy actions + List<AccessibilityAction> actions = mSystemActionPerformer.getSystemActions(); + assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size()); + // Register a new system action + mSystemActionPerformer.registerSystemAction(NEW_ACTION_ID, NEW_TEST_ACTION_1); + actions = mSystemActionPerformer.getSystemActions(); + assertEquals(LEGACY_SYSTEM_ACTION_COUNT + 1, actions.size()); + + mSystemActionPerformer.unregisterSystemAction(NEW_ACTION_ID); + actions = mSystemActionPerformer.getSystemActions(); + assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size()); + assertThat(actions, is(not(hasItem(NEW_ACCESSIBILITY_ACTION)))); + } - when(mMockContext.getSystemService(android.app.Service.STATUS_BAR_SERVICE)) - .thenReturn(mMockStatusBarManager); + @Test + public void testUnregisterSystemAction_removeOverrideForLegacyAction() { + setupWithRealContext(); + + // Overriding a legacy system action + mSystemActionPerformer.registerSystemAction( + AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS, NEW_TEST_ACTION_1); + List<AccessibilityAction> actions = mSystemActionPerformer.getSystemActions(); + assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size()); + assertHasLegacyAccessibilityAction(actions, LEGACY_NOTIFICATIONS_ACCESSIBILITY_ACTION); + + // Remove the overriding action using legacy action id + mSystemActionPerformer.unregisterSystemAction( + AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS); + actions = mSystemActionPerformer.getSystemActions(); + assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size()); + assertHasNoLegacyAccessibilityAction(actions, LEGACY_NOTIFICATIONS_ACCESSIBILITY_ACTION); + } + + @Test + public void testPerformSystemActionNewAction() throws CanceledException { + setupWithRealContext(); - mSystemActionPerformer = - new SystemActionPerformer(mMockContext, mMockWindowManagerInternal, - () -> mMockScreenshotHelper); + final CountDownLatch latch = new CountDownLatch(1); + mSystemActionPerformer.registerSystemAction(NEW_ACTION_ID, NEW_TEST_ACTION_1); + TestBroadcastReceiver br = new TestBroadcastReceiver(latch); + br.register(InstrumentationRegistry.getTargetContext()); + mSystemActionPerformer.performSystemAction(NEW_ACTION_ID); + try { + latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + fail("RemoteAction should be triggered."); + } finally { + br.unregister(InstrumentationRegistry.getTargetContext()); + } } @Test - public void testNotifications_expandsNotificationPanel() { + public void testPerformSystemActionOverrideLegacyActionUsingLegacyActionId() + throws CanceledException { + setupWithRealContext(); + + final CountDownLatch latch = new CountDownLatch(1); + mSystemActionPerformer.registerSystemAction( + AccessibilityService.GLOBAL_ACTION_RECENTS, NEW_TEST_ACTION_1); + TestBroadcastReceiver br = new TestBroadcastReceiver(latch); + br.register(InstrumentationRegistry.getTargetContext()); + mSystemActionPerformer.performSystemAction(AccessibilityService.GLOBAL_ACTION_RECENTS); + try { + latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + fail("RemoteAction should be triggered."); + } finally { + br.unregister(InstrumentationRegistry.getTargetContext()); + } + verify(mMockStatusBarManagerInternal, never()).toggleRecentApps(); + // Now revert to legacy action + mSystemActionPerformer.unregisterSystemAction(AccessibilityService.GLOBAL_ACTION_RECENTS); + mSystemActionPerformer.performSystemAction(AccessibilityService.GLOBAL_ACTION_RECENTS); + verify(mMockStatusBarManagerInternal).toggleRecentApps(); + } + + @Test + public void testNotifications_expandsNotificationPanel_legacy() { + setupWithMockContext(); mSystemActionPerformer .performSystemAction(AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS); verify(mMockStatusBarManager).expandNotificationsPanel(); } @Test - public void testQuickSettings_requestsQuickSettingsPanel() { + public void testQuickSettings_requestsQuickSettingsPanel_legacy() { + setupWithMockContext(); mSystemActionPerformer .performSystemAction(AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS); verify(mMockStatusBarManager).expandSettingsPanel(); } @Test - public void testPowerDialog_requestsFromWindowManager() { + public void testRecentApps_legacy() { + setupWithRealContext(); + mSystemActionPerformer.performSystemAction(AccessibilityService.GLOBAL_ACTION_RECENTS); + verify(mMockStatusBarManagerInternal).toggleRecentApps(); + } + + @Test + public void testPowerDialog_requestsFromWindowManager_legacy() { + setupWithMockContext(); mSystemActionPerformer.performSystemAction(AccessibilityService.GLOBAL_ACTION_POWER_DIALOG); verify(mMockWindowManagerInternal).showGlobalActions(); } @Test - public void testScreenshot_requestsFromScreenshotHelper() { + public void testToggleSplitScreen_legacy() { + setupWithRealContext(); + mSystemActionPerformer.performSystemAction( + AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN); + verify(mMockStatusBarManagerInternal).toggleSplitScreen(); + } + + @Test + public void testScreenshot_requestsFromScreenshotHelper_legacy() { + setupWithMockContext(); mSystemActionPerformer.performSystemAction( AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT); verify(mMockScreenshotHelper).takeScreenshot( eq(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN), anyBoolean(), anyBoolean(), any(Handler.class), any()); } + + // PendingIntent is a final class and cannot be mocked. So we are using this + // Broadcast receiver to verify the registered remote action is called correctly. + private static final class TestBroadcastReceiver extends BroadcastReceiver { + private CountDownLatch mLatch; + private boolean mRegistered; + private final IntentFilter mFilter; + + TestBroadcastReceiver(CountDownLatch latch) { + mLatch = latch; + mRegistered = false; + mFilter = new IntentFilter(INTENT_ACTION1); + } + + @Override + public void onReceive(Context context, Intent intent) { + mLatch.countDown(); + } + + void register(Context context) { + if (!mRegistered) { + context.registerReceiver(this, mFilter); + mRegistered = true; + } + } + + void unregister(Context context) { + if (mRegistered) { + context.unregisterReceiver(this); + } + } + } } diff --git a/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java b/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java index adef02ee55d7..6f2de7f50379 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java @@ -21,6 +21,7 @@ import androidx.test.runner.AndroidJUnit4; import com.google.android.icing.proto.DocumentProto; import com.google.android.icing.proto.PropertyProto; +import com.google.android.icing.proto.SearchResultProto; import org.junit.Test; import org.junit.runner.RunWith; @@ -115,8 +116,9 @@ public class FakeIcingTest { private static List<String> queryGetUris(FakeIcing icing, String term) { List<String> uris = new ArrayList<>(); - for (DocumentProto result : icing.query(term)) { - uris.add(result.getUri()); + SearchResultProto results = icing.query(term); + for (SearchResultProto.ResultProto result : results.getResultsList()) { + uris.add(result.getDocument().getUri()); } return uris; } 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 f86bacf67901..ac555fda2204 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java @@ -23,6 +23,7 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.app.backup.IBackupManager; import android.app.timedetector.TimeDetector; +import android.app.timezonedetector.TimeZoneDetector; import android.app.usage.UsageStatsManagerInternal; import android.content.Context; import android.content.Intent; @@ -235,6 +236,11 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi } @Override + TimeZoneDetector getTimeZoneDetector() { + return services.timeZoneDetector; + } + + @Override LockPatternUtils newLockPatternUtils() { return services.lockPatternUtils; } 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 059002094bc0..06b8716c0926 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -64,6 +64,8 @@ import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManagerInternal; import android.app.admin.PasswordMetrics; import android.app.timedetector.ManualTimeSuggestion; +import android.app.timezonedetector.ManualTimeZoneSuggestion; +import android.app.timezonedetector.TimeZoneDetector; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Intent; @@ -138,6 +140,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { permission.MANAGE_USERS, permission.INTERACT_ACROSS_USERS_FULL); public static final String NOT_DEVICE_OWNER_MSG = "does not own the device"; public static final String NOT_PROFILE_OWNER_MSG = "does not own the profile"; + public static final String NOT_ORG_OWNED_PROFILE_OWNER_MSG = + "not the profile owner on organization-owned device"; public static final String ONGOING_CALL_MSG = "ongoing call on the device"; // TODO replace all instances of this with explicit {@link #mServiceContext}. @@ -2114,12 +2118,14 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertTrue(dpm.isAdminActive(admin1)); // Test 2. Caller has DA, but not DO. - assertExpectException(SecurityException.class, /* messageRegex= */ NOT_DEVICE_OWNER_MSG, + assertExpectException(SecurityException.class, + /* messageRegex= */ NOT_ORG_OWNED_PROFILE_OWNER_MSG, () -> dpm.getWifiMacAddress(admin1)); // Test 3. Caller has PO, but not DO. assertTrue(dpm.setProfileOwner(admin1, null, UserHandle.USER_SYSTEM)); - assertExpectException(SecurityException.class, /* messageRegex= */ NOT_DEVICE_OWNER_MSG, + assertExpectException(SecurityException.class, + /* messageRegex= */ NOT_ORG_OWNED_PROFILE_OWNER_MSG, () -> dpm.getWifiMacAddress(admin1)); // Remove PO. @@ -2141,6 +2147,15 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertEquals("11:22:33:44:55:66", dpm.getWifiMacAddress(admin1)); } + public void testGetMacAddressByOrgOwnedPO() throws Exception { + setupProfileOwner(); + configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE); + + final String[] macAddresses = new String[]{"11:22:33:44:55:66"}; + when(getServices().wifiManager.getFactoryMacAddresses()).thenReturn(macAddresses); + assertEquals("11:22:33:44:55:66", dpm.getWifiMacAddress(admin1)); + } + public void testReboot() throws Exception { mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS); mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); @@ -3694,7 +3709,9 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); dpm.setTimeZone(admin1, "Asia/Shanghai"); - verify(getServices().alarmManager).setTimeZone("Asia/Shanghai"); + ManualTimeZoneSuggestion suggestion = + TimeZoneDetector.createManualTimeZoneSuggestion("Asia/Shanghai", "Test debug info"); + verify(getServices().timeZoneDetector).suggestManualTimeZone(suggestion); } public void testSetTimeZoneFailWithPO() throws Exception { diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java index c9273642635e..6a0d9265f594 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java @@ -32,6 +32,7 @@ import android.app.IActivityTaskManager; import android.app.NotificationManager; import android.app.backup.IBackupManager; import android.app.timedetector.TimeDetector; +import android.app.timezonedetector.TimeZoneDetector; import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; import android.content.ContentValues; @@ -113,6 +114,7 @@ public class MockSystemServices { public final AccountManager accountManager; public final AlarmManager alarmManager; public final TimeDetector timeDetector; + public final TimeZoneDetector timeZoneDetector; public final KeyChain.KeyChainConnection keyChainConnection; /** Note this is a partial mock, not a real mock. */ public final PackageManager packageManager; @@ -155,6 +157,7 @@ public class MockSystemServices { accountManager = mock(AccountManager.class); alarmManager = mock(AlarmManager.class); timeDetector = mock(TimeDetector.class); + timeZoneDetector = mock(TimeZoneDetector.class); keyChainConnection = mock(KeyChain.KeyChainConnection.class, RETURNS_DEEP_STUBS); // Package manager is huge, so we use a partial mock instead. diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index 0ca62e2dcdff..81fb0ec3bf53 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -61,7 +61,6 @@ import android.os.Looper; import android.os.PowerManager; import android.os.PowerSaveState; import android.os.SystemClock; -import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; import android.test.mock.MockContentResolver; @@ -97,10 +96,12 @@ import java.util.Map; * Tests for {@link com.android.server.power.PowerManagerService} */ public class PowerManagerServiceTest { + private static final String SYSTEM_PROPERTY_QUIESCENT = "ro.boot.quiescent"; + private static final String SYSTEM_PROPERTY_REBOOT_REASON = "sys.boot.reason"; + private static final float PRECISION = 0.001f; private static final float BRIGHTNESS_FACTOR = 0.7f; private static final boolean BATTERY_SAVER_ENABLED = true; - private static final String TEST_LAST_REBOOT_PROPERTY = "test.sys.boot.reason"; @Mock private BatterySaverPolicy mBatterySaverPolicyMock; @Mock private LightsManager mLightsManagerMock; @@ -112,6 +113,7 @@ public class PowerManagerServiceTest { @Mock private Notifier mNotifierMock; @Mock private WirelessChargerDetector mWirelessChargerDetectorMock; @Mock private AmbientDisplayConfiguration mAmbientDisplayConfigurationMock; + @Mock private SystemPropertiesWrapper mSystemPropertiesMock; @Mock private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock; @@ -159,6 +161,7 @@ public class PowerManagerServiceTest { when(mBatteryManagerInternalMock.isPowered(anyInt())).thenReturn(false); when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(false); when(mDisplayManagerInternalMock.requestPowerState(any(), anyBoolean())).thenReturn(true); + when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn(""); mDisplayPowerRequest = new DisplayPowerRequest(); addLocalServiceMock(LightsManager.class, mLightsManagerMock); @@ -218,6 +221,11 @@ public class PowerManagerServiceTest { InattentiveSleepWarningController createInattentiveSleepWarningController() { return mInattentiveSleepWarningControllerMock; } + + @Override + public SystemPropertiesWrapper createSystemPropertiesWrapper() { + return mSystemPropertiesMock; + } }); return mService; } @@ -228,12 +236,6 @@ public class PowerManagerServiceTest { LocalServices.removeServiceForTest(DisplayManagerInternal.class); LocalServices.removeServiceForTest(BatteryManagerInternal.class); LocalServices.removeServiceForTest(ActivityManagerInternal.class); - - Settings.Global.putInt( - mContextSpy.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0); - setAttentiveTimeout(-1); - Settings.Global.putInt(mContextSpy.getContentResolver(), - Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0); } /** @@ -322,10 +324,10 @@ public class PowerManagerServiceTest { @Test public void testGetLastShutdownReasonInternal() { + when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_REBOOT_REASON), any())).thenReturn( + "shutdown,thermal"); createService(); - SystemProperties.set(TEST_LAST_REBOOT_PROPERTY, "shutdown,thermal"); - int reason = mService.getLastShutdownReasonInternal(TEST_LAST_REBOOT_PROPERTY); - SystemProperties.set(TEST_LAST_REBOOT_PROPERTY, ""); + int reason = mService.getLastShutdownReasonInternal(); assertThat(reason).isEqualTo(PowerManager.SHUTDOWN_REASON_THERMAL_SHUTDOWN); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index eed5ef52c8e1..72baedb5ed66 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -222,7 +222,8 @@ public class WindowStateTests extends WindowTestsBase { final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow"); final WindowState imeWindow = createWindow(null, TYPE_INPUT_METHOD, "imeWindow"); - // Setting FLAG_NOT_FOCUSABLE prevents the window from being an IME target. + // Setting FLAG_NOT_FOCUSABLE without FLAG_ALT_FOCUSABLE_IM prevents the window from being + // an IME target. appWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE; imeWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE; @@ -230,7 +231,7 @@ public class WindowStateTests extends WindowTestsBase { appWindow.setHasSurface(true); imeWindow.setHasSurface(true); - // Windows with FLAG_NOT_FOCUSABLE can't be IME targets + // Windows without flags (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM) can't be IME targets assertFalse(appWindow.canBeImeTarget()); assertFalse(imeWindow.canBeImeTarget()); @@ -238,16 +239,10 @@ public class WindowStateTests extends WindowTestsBase { appWindow.mAttrs.flags |= (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM); imeWindow.mAttrs.flags |= (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM); - // Visible app window with flags FLAG_NOT_FOCUSABLE or FLAG_ALT_FOCUSABLE_IM can't be IME - // target while an IME window can never be an IME target regardless of its visibility - // or flags. - assertFalse(appWindow.canBeImeTarget()); - assertFalse(imeWindow.canBeImeTarget()); - - appWindow.mAttrs.flags &= ~FLAG_ALT_FOCUSABLE_IM; - assertFalse(appWindow.canBeImeTarget()); - appWindow.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE; + // Visible app window with flags can be IME target while an IME window can never be an IME + // target regardless of its visibility or flags. assertTrue(appWindow.canBeImeTarget()); + assertFalse(imeWindow.canBeImeTarget()); // Make windows invisible appWindow.hideLw(false /* doAnimation */); diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index a0739c49fb76..86ad795b9ea2 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -291,7 +291,6 @@ public final class Call { */ public static final int DIRECTION_OUTGOING = 1; - /** Call can currently be put on hold or unheld. */ public static final int CAPABILITY_HOLD = 0x00000001; @@ -571,6 +570,7 @@ public final class Call { private final Bundle mIntentExtras; private final long mCreationTimeMillis; private final @CallDirection int mCallDirection; + private final @Connection.VerificationStatus int mCallerNumberVerificationStatus; /** * Whether the supplied capabilities supports the specified capability. @@ -880,6 +880,15 @@ public final class Call { return mCallDirection; } + /** + * Gets the verification status for the phone number of an incoming call as identified in + * ATIS-1000082. + * @return the verification status. + */ + public @Connection.VerificationStatus int getCallerNumberVerificationStatus() { + return mCallerNumberVerificationStatus; + } + @Override public boolean equals(Object o) { if (o instanceof Details) { @@ -901,7 +910,9 @@ public final class Call { areBundlesEqual(mExtras, d.mExtras) && areBundlesEqual(mIntentExtras, d.mIntentExtras) && Objects.equals(mCreationTimeMillis, d.mCreationTimeMillis) && - Objects.equals(mCallDirection, d.mCallDirection); + Objects.equals(mCallDirection, d.mCallDirection) && + Objects.equals(mCallerNumberVerificationStatus, + d.mCallerNumberVerificationStatus); } return false; } @@ -923,7 +934,8 @@ public final class Call { mExtras, mIntentExtras, mCreationTimeMillis, - mCallDirection); + mCallDirection, + mCallerNumberVerificationStatus); } /** {@hide} */ @@ -944,7 +956,8 @@ public final class Call { Bundle extras, Bundle intentExtras, long creationTimeMillis, - int callDirection) { + int callDirection, + int callerNumberVerificationStatus) { mTelecomCallId = telecomCallId; mHandle = handle; mHandlePresentation = handlePresentation; @@ -962,6 +975,7 @@ public final class Call { mIntentExtras = intentExtras; mCreationTimeMillis = creationTimeMillis; mCallDirection = callDirection; + mCallerNumberVerificationStatus = callerNumberVerificationStatus; } /** {@hide} */ @@ -983,7 +997,8 @@ public final class Call { parcelableCall.getExtras(), parcelableCall.getIntentExtras(), parcelableCall.getCreationTimeMillis(), - parcelableCall.getCallDirection()); + parcelableCall.getCallDirection(), + parcelableCall.getCallerNumberVerificationStatus()); } @Override diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index ae20aed2d3c7..c06327995bc0 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -16,6 +16,7 @@ package android.telecom; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -48,6 +49,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.nio.channels.Channels; import java.util.ArrayList; import java.util.Arrays; @@ -151,6 +154,32 @@ public abstract class Connection extends Conferenceable { public static final int STATE_PULLING_CALL = 7; /** + * Indicates that the network could not perform verification. + */ + public static final int VERIFICATION_STATUS_NOT_VERIFIED = 0; + + /** + * Indicates that verification by the network passed. This indicates there is a high likelihood + * that the call originated from a valid source. + */ + public static final int VERIFICATION_STATUS_PASSED = 1; + + /** + * Indicates that verification by the network failed. This indicates there is a high likelihood + * that the call did not originate from a valid source. + */ + public static final int VERIFICATION_STATUS_FAILED = 2; + + /**@hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "VERIFICATION_STATUS_", value = { + VERIFICATION_STATUS_NOT_VERIFIED, + VERIFICATION_STATUS_PASSED, + VERIFICATION_STATUS_FAILED + }) + public @interface VerificationStatus {} + + /** * Connection can currently be put on hold or unheld. This is distinct from * {@link #CAPABILITY_SUPPORT_HOLD} in that although a connection may support 'hold' most times, * it does not at the moment support the function. This can be true while the call is in the @@ -1854,6 +1883,12 @@ public abstract class Connection extends Conferenceable { private Set<String> mPreviousExtraKeys; /** + * The verification status for an incoming call's phone number. + */ + private @VerificationStatus int mCallerNumberVerificationStatus; + + + /** * Create a new Connection. */ public Connection() {} @@ -3355,4 +3390,26 @@ public abstract class Connection extends Conferenceable { public void setCallDirection(@Call.Details.CallDirection int callDirection) { mCallDirection = callDirection; } + + /** + * Gets the verification status for the phone number of an incoming call as identified in + * ATIS-1000082. + * @return the verification status. + */ + public @VerificationStatus int getCallerNumberVerificationStatus() { + return mCallerNumberVerificationStatus; + } + + /** + * Sets the verification status for the phone number of an incoming call as identified in + * ATIS-1000082. + * <p> + * This property can only be set at the time of creation of a {@link Connection} being returned + * by + * {@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)}. + */ + public void setCallerNumberVerificationStatus( + @VerificationStatus int callerNumberVerificationStatus) { + mCallerNumberVerificationStatus = callerNumberVerificationStatus; + } } diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index 812b805675e5..3a0494e17db9 100644 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -1621,7 +1621,8 @@ public abstract class ConnectionService extends Service { connection.getStatusHints(), connection.getDisconnectCause(), createIdList(connection.getConferenceables()), - connection.getExtras())); + connection.getExtras(), + connection.getCallerNumberVerificationStatus())); if (isIncoming && request.shouldShowIncomingCallUi() && isSelfManaged) { // Tell ConnectionService to show its incoming call UX. @@ -2156,7 +2157,8 @@ public abstract class ConnectionService extends Service { emptyList, connection.getExtras(), conferenceId, - connection.getCallDirection()); + connection.getCallDirection(), + Connection.VERIFICATION_STATUS_NOT_VERIFIED); mAdapter.addExistingConnection(id, parcelableConnection); } } diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java index fdc324308d7a..a234bb0af8fa 100644 --- a/telecomm/java/android/telecom/ParcelableCall.java +++ b/telecomm/java/android/telecom/ParcelableCall.java @@ -16,7 +16,6 @@ package android.telecom; -import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.net.Uri; import android.os.Build; @@ -66,6 +65,7 @@ public final class ParcelableCall implements Parcelable { private final Bundle mExtras; private final long mCreationTimeMillis; private final int mCallDirection; + private final int mCallerNumberVerificationStatus; public ParcelableCall( String id, @@ -94,7 +94,8 @@ public final class ParcelableCall implements Parcelable { Bundle intentExtras, Bundle extras, long creationTimeMillis, - int callDirection) { + int callDirection, + int callerNumberVerificationStatus) { mId = id; mState = state; mDisconnectCause = disconnectCause; @@ -122,6 +123,7 @@ public final class ParcelableCall implements Parcelable { mExtras = extras; mCreationTimeMillis = creationTimeMillis; mCallDirection = callDirection; + mCallerNumberVerificationStatus = callerNumberVerificationStatus; } /** The unique ID of the call. */ @@ -322,6 +324,15 @@ public final class ParcelableCall implements Parcelable { return mCallDirection; } + /** + * Gets the verification status for the phone number of an incoming call as identified in + * ATIS-1000082. + * @return the verification status. + */ + public @Connection.VerificationStatus int getCallerNumberVerificationStatus() { + return mCallerNumberVerificationStatus; + } + /** Responsible for creating ParcelableCall objects for deserialized Parcels. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public static final @android.annotation.NonNull Parcelable.Creator<ParcelableCall> CREATOR = @@ -360,6 +371,7 @@ public final class ParcelableCall implements Parcelable { ParcelableRttCall rttCall = source.readParcelable(classLoader); long creationTimeMillis = source.readLong(); int callDirection = source.readInt(); + int callerNumberVerificationStatus = source.readInt(); return new ParcelableCall( id, state, @@ -387,7 +399,8 @@ public final class ParcelableCall implements Parcelable { intentExtras, extras, creationTimeMillis, - callDirection); + callDirection, + callerNumberVerificationStatus); } @Override @@ -433,6 +446,7 @@ public final class ParcelableCall implements Parcelable { destination.writeParcelable(mRttCall, 0); destination.writeLong(mCreationTimeMillis); destination.writeInt(mCallDirection); + destination.writeInt(mCallerNumberVerificationStatus); } @Override diff --git a/telecomm/java/android/telecom/ParcelableConnection.java b/telecomm/java/android/telecom/ParcelableConnection.java index 4734af6371d4..2b9ce9b46ad7 100644 --- a/telecomm/java/android/telecom/ParcelableConnection.java +++ b/telecomm/java/android/telecom/ParcelableConnection.java @@ -54,6 +54,7 @@ public final class ParcelableConnection implements Parcelable { private final Bundle mExtras; private String mParentCallId; private @Call.Details.CallDirection int mCallDirection; + private @Connection.VerificationStatus int mCallerNumberVerificationStatus; /** @hide */ public ParcelableConnection( @@ -77,12 +78,13 @@ public final class ParcelableConnection implements Parcelable { List<String> conferenceableConnectionIds, Bundle extras, String parentCallId, - @Call.Details.CallDirection int callDirection) { + @Call.Details.CallDirection int callDirection, + @Connection.VerificationStatus int callerNumberVerificationStatus) { this(phoneAccount, state, capabilities, properties, supportedAudioRoutes, address, addressPresentation, callerDisplayName, callerDisplayNamePresentation, videoProvider, videoState, ringbackRequested, isVoipAudioMode, connectTimeMillis, connectElapsedTimeMillis, statusHints, disconnectCause, conferenceableConnectionIds, - extras); + extras, callerNumberVerificationStatus); mParentCallId = parentCallId; mCallDirection = callDirection; } @@ -107,7 +109,8 @@ public final class ParcelableConnection implements Parcelable { StatusHints statusHints, DisconnectCause disconnectCause, List<String> conferenceableConnectionIds, - Bundle extras) { + Bundle extras, + @Connection.VerificationStatus int callerNumberVerificationStatus) { mPhoneAccount = phoneAccount; mState = state; mConnectionCapabilities = capabilities; @@ -129,6 +132,7 @@ public final class ParcelableConnection implements Parcelable { mExtras = extras; mParentCallId = null; mCallDirection = Call.Details.DIRECTION_UNKNOWN; + mCallerNumberVerificationStatus = callerNumberVerificationStatus; } public PhoneAccountHandle getPhoneAccount() { @@ -227,6 +231,10 @@ public final class ParcelableConnection implements Parcelable { return mCallDirection; } + public @Connection.VerificationStatus int getCallerNumberVerificationStatus() { + return mCallerNumberVerificationStatus; + } + @Override public String toString() { return new StringBuilder() @@ -276,6 +284,7 @@ public final class ParcelableConnection implements Parcelable { String parentCallId = source.readString(); long connectElapsedTimeMillis = source.readLong(); int callDirection = source.readInt(); + int callerNumberVerificationStatus = source.readInt(); return new ParcelableConnection( phoneAccount, @@ -298,7 +307,8 @@ public final class ParcelableConnection implements Parcelable { conferenceableConnectionIds, extras, parentCallId, - callDirection); + callDirection, + callerNumberVerificationStatus); } @Override @@ -338,5 +348,6 @@ public final class ParcelableConnection implements Parcelable { destination.writeString(mParentCallId); destination.writeLong(mConnectElapsedTimeMillis); destination.writeInt(mCallDirection); + destination.writeInt(mCallerNumberVerificationStatus); } } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index fc26122912dc..3bc3e49e6433 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -4723,17 +4723,6 @@ public class TelephonyManager { } /** - * Sim activation type: voice - * @hide - */ - public static final int SIM_ACTIVATION_TYPE_VOICE = 0; - /** - * Sim activation type: data - * @hide - */ - public static final int SIM_ACTIVATION_TYPE_DATA = 1; - - /** * Initial SIM activation state, unknown. Not set by any carrier apps. * @hide */ diff --git a/tools/processors/unsupportedappusage/Android.bp b/tools/processors/unsupportedappusage/Android.bp deleted file mode 100644 index 1e96234543c8..000000000000 --- a/tools/processors/unsupportedappusage/Android.bp +++ /dev/null @@ -1,34 +0,0 @@ - -java_library_host { - name: "unsupportedappusage-annotation-processor-lib", - srcs: [ - "src/**/*.java", - ], - static_libs: [ - "guava", - "unsupportedappusage-annotation" - ], - openjdk9: { - javacflags: [ - "--add-modules=jdk.compiler", - "--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", - "--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED", - "--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", - "--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", - ], - }, -} - -java_plugin { - name: "unsupportedappusage-annotation-processor", - processor_class: "android.processor.unsupportedappusage.UnsupportedAppUsageProcessor", - - java_resources: [ - "META-INF/**/*", - ], - static_libs: [ - "unsupportedappusage-annotation-processor-lib" - ], - - use_tools_jar: true, -} diff --git a/tools/processors/unsupportedappusage/META-INF/services/javax.annotation.processing.Processor b/tools/processors/unsupportedappusage/META-INF/services/javax.annotation.processing.Processor deleted file mode 100644 index 4a969d319070..000000000000 --- a/tools/processors/unsupportedappusage/META-INF/services/javax.annotation.processing.Processor +++ /dev/null @@ -1 +0,0 @@ -android.processor.unsupportedappusage.UnsupportedAppUsageProcessor diff --git a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java deleted file mode 100644 index 65fc733fa364..000000000000 --- a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.processor.unsupportedappusage; - -import static javax.lang.model.element.ElementKind.PACKAGE; -import static javax.tools.Diagnostic.Kind.ERROR; -import static javax.tools.Diagnostic.Kind.WARNING; - -import com.google.common.base.Strings; -import com.google.common.collect.ImmutableMap; -import com.sun.tools.javac.code.Type; - -import java.lang.annotation.Annotation; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import javax.annotation.processing.Messager; -import javax.lang.model.element.Element; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.PackageElement; -import javax.lang.model.element.TypeElement; -import javax.lang.model.element.VariableElement; -import javax.lang.model.type.ArrayType; -import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeKind; -import javax.lang.model.type.TypeMirror; - -/** - * Builds a dex signature for a given method or field. - */ -public class SignatureBuilder { - - private static final Map<TypeKind, String> TYPE_MAP = ImmutableMap.<TypeKind, String>builder() - .put(TypeKind.BOOLEAN, "Z") - .put(TypeKind.BYTE, "B") - .put(TypeKind.CHAR, "C") - .put(TypeKind.DOUBLE, "D") - .put(TypeKind.FLOAT, "F") - .put(TypeKind.INT, "I") - .put(TypeKind.LONG, "J") - .put(TypeKind.SHORT, "S") - .put(TypeKind.VOID, "V") - .build(); - - private final Messager mMessager; - - /** - * Exception used internally when we can't build a signature. Whenever this is thrown, an error - * will also be written to the Messager. - */ - private class SignatureBuilderException extends Exception { - public SignatureBuilderException(String message) { - super(message); - } - - public void report(Element offendingElement) { - mMessager.printMessage(ERROR, getMessage(), offendingElement); - } - } - - public SignatureBuilder(Messager messager) { - mMessager = messager; - } - - /** - * Returns a list of enclosing elements for the given element, with the package first, and - * excluding the element itself. - */ - private List<Element> getEnclosingElements(Element e) { - List<Element> enclosing = new ArrayList<>(); - e = e.getEnclosingElement(); // don't include the element itself. - while (e != null) { - enclosing.add(e); - e = e.getEnclosingElement(); - } - Collections.reverse(enclosing); - return enclosing; - } - - /** - * Get the dex signature for a clazz, in format "Lpackage/name/Outer$Inner;" - */ - private String getClassSignature(TypeElement clazz) { - StringBuilder sb = new StringBuilder("L"); - for (Element enclosing : getEnclosingElements(clazz)) { - switch (enclosing.getKind()) { - case MODULE: - // ignore this. - break; - case PACKAGE: - sb.append(((PackageElement) enclosing) - .getQualifiedName() - .toString() - .replace('.', '/')); - sb.append('/'); - break; - default: - sb.append(enclosing.getSimpleName()).append('$'); - break; - } - - } - return sb - .append(clazz.getSimpleName()) - .append(";") - .toString(); - } - - /** - * Returns the type signature for a given type. For primitive types, a single character. - * For classes, the class signature. For arrays, a "[" preceeding the component type. - */ - private String getTypeSignature(TypeMirror type) throws SignatureBuilderException { - String sig = TYPE_MAP.get(type.getKind()); - if (sig != null) { - return sig; - } - switch (type.getKind()) { - case ARRAY: - return "[" + getTypeSignature(((ArrayType) type).getComponentType()); - case DECLARED: - Element declaring = ((DeclaredType) type).asElement(); - if (!(declaring instanceof TypeElement)) { - throw new SignatureBuilderException( - "Can't handle declared type of kind " + declaring.getKind()); - } - return getClassSignature((TypeElement) declaring); - case TYPEVAR: - Type.TypeVar typeVar = (Type.TypeVar) type; - if (typeVar.getLowerBound().getKind() != TypeKind.NULL) { - return getTypeSignature(typeVar.getLowerBound()); - } else if (typeVar.getUpperBound().getKind() != TypeKind.NULL) { - return getTypeSignature(typeVar.getUpperBound()); - } else { - throw new SignatureBuilderException("Can't handle typevar with no bound"); - } - - default: - throw new SignatureBuilderException("Can't handle type of kind " + type.getKind()); - } - } - - /** - * Get the signature for an executable, either a method or a constructor. - * - * @param name "<init>" for constructor, else the method name - * @param method The executable element in question. - */ - private String getExecutableSignature(CharSequence name, ExecutableElement method) - throws SignatureBuilderException { - StringBuilder sig = new StringBuilder(); - sig.append(getClassSignature((TypeElement) method.getEnclosingElement())) - .append("->") - .append(name) - .append("("); - for (VariableElement param : method.getParameters()) { - sig.append(getTypeSignature(param.asType())); - } - sig.append(")") - .append(getTypeSignature(method.getReturnType())); - return sig.toString(); - } - - private String buildMethodSignature(ExecutableElement method) throws SignatureBuilderException { - return getExecutableSignature(method.getSimpleName(), method); - } - - private String buildConstructorSignature(ExecutableElement cons) - throws SignatureBuilderException { - return getExecutableSignature("<init>", cons); - } - - private String buildFieldSignature(VariableElement field) throws SignatureBuilderException { - StringBuilder sig = new StringBuilder(); - sig.append(getClassSignature((TypeElement) field.getEnclosingElement())) - .append("->") - .append(field.getSimpleName()) - .append(":") - .append(getTypeSignature(field.asType())) - ; - return sig.toString(); - } - - /** - * Creates the signature for an annotated element. - * - * @param annotationType type of annotation being processed. - * @param element element for which we want to create a signature. - */ - public String buildSignature(Class<? extends Annotation> annotationType, Element element) { - try { - String signature; - switch (element.getKind()) { - case METHOD: - signature = buildMethodSignature((ExecutableElement) element); - break; - case CONSTRUCTOR: - signature = buildConstructorSignature((ExecutableElement) element); - break; - case FIELD: - signature = buildFieldSignature((VariableElement) element); - break; - default: - return null; - } - // Obtain annotation objects - Annotation annotation = element.getAnnotation(annotationType); - if (annotation == null) { - throw new IllegalStateException( - "Element doesn't have any UnsupportedAppUsage annotation"); - } - try { - Method expectedSignatureMethod = annotationType.getMethod("expectedSignature"); - // If we have an expected signature on the annotation, warn if it doesn't match. - String expectedSignature = expectedSignatureMethod.invoke(annotation).toString(); - if (!Strings.isNullOrEmpty(expectedSignature)) { - if (!signature.equals(expectedSignature)) { - mMessager.printMessage( - WARNING, - String.format( - "Expected signature doesn't match generated signature.\n" - + " Expected: %s\n Generated: %s", - expectedSignature, signature), - element); - } - } - return signature; - } catch (NoSuchMethodException e) { - throw new IllegalStateException( - "Annotation type does not have expectedSignature parameter", e); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new IllegalStateException( - "Could not get expectedSignature parameter for annotation", e); - } - } catch (SignatureBuilderException problem) { - problem.report(element); - return null; - } - } -} diff --git a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java deleted file mode 100644 index ca2c2759d702..000000000000 --- a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.processor.unsupportedappusage; - -import static javax.tools.StandardLocation.CLASS_OUTPUT; - -import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableSet; -import com.sun.tools.javac.model.JavacElements; -import com.sun.tools.javac.tree.JCTree; -import com.sun.tools.javac.util.Pair; -import com.sun.tools.javac.util.Position; - -import java.io.IOException; -import java.io.PrintStream; -import java.lang.annotation.Annotation; -import java.net.URLEncoder; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.stream.Stream; - -import javax.annotation.processing.AbstractProcessor; -import javax.annotation.processing.RoundEnvironment; -import javax.annotation.processing.SupportedAnnotationTypes; -import javax.lang.model.SourceVersion; -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.AnnotationValue; -import javax.lang.model.element.Element; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.TypeElement; -import javax.tools.Diagnostic; - -/** - * Annotation processor for {@link UnsupportedAppUsage} annotations. - * - * This processor currently outputs a CSV file with a mapping of dex signatures to corresponding - * source positions. - * - * This is used for automating updates to the annotations themselves. - */ -@SupportedAnnotationTypes({"android.annotation.UnsupportedAppUsage", - "dalvik.annotation.compat.UnsupportedAppUsage" -}) -public class UnsupportedAppUsageProcessor extends AbstractProcessor { - - // Package name for writing output. Output will be written to the "class output" location within - // this package. - private static final String PACKAGE = "unsupportedappusage"; - private static final String INDEX_CSV = "unsupportedappusage_index.csv"; - - private static final ImmutableSet<Class<? extends Annotation>> SUPPORTED_ANNOTATIONS = - ImmutableSet.of(android.annotation.UnsupportedAppUsage.class, - dalvik.annotation.compat.UnsupportedAppUsage.class); - private static final ImmutableSet<String> SUPPORTED_ANNOTATION_NAMES = - SUPPORTED_ANNOTATIONS.stream().map(annotation -> annotation.getCanonicalName()).collect( - ImmutableSet.toImmutableSet()); - private static final String OVERRIDE_SOURCE_POSITION_PROPERTY = "overrideSourcePosition"; - - @Override - public SourceVersion getSupportedSourceVersion() { - return SourceVersion.latest(); - } - - /** - * Write the contents of a stream to a text file, with one line per item. - */ - private void writeToFile(String name, - String headerLine, - Stream<?> contents) throws IOException { - PrintStream out = new PrintStream(processingEnv.getFiler().createResource( - CLASS_OUTPUT, - PACKAGE, - name) - .openOutputStream()); - out.println(headerLine); - contents.forEach(o -> out.println(o)); - if (out.checkError()) { - throw new IOException("Error when writing to " + name); - } - out.close(); - } - - /** - * Find the annotation mirror for the @UnsupportedAppUsage annotation on the given element. - */ - private AnnotationMirror getUnsupportedAppUsageAnnotationMirror(Element e) { - for (AnnotationMirror m : e.getAnnotationMirrors()) { - TypeElement type = (TypeElement) m.getAnnotationType().asElement(); - if (SUPPORTED_ANNOTATION_NAMES.contains(type.getQualifiedName().toString())) { - return m; - } - } - return null; - } - - /** - * Returns a CSV header line for the columns returned by - * {@link #getAnnotationIndex(String, Element)}. - */ - private String getCsvHeaders() { - return Joiner.on(',').join( - "signature", - "file", - "startline", - "startcol", - "endline", - "endcol", - "properties" - ); - } - - private String encodeAnnotationProperties(AnnotationMirror annotation) { - StringBuilder sb = new StringBuilder(); - for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> e - : annotation.getElementValues().entrySet()) { - if (e.getKey().getSimpleName().toString().equals(OVERRIDE_SOURCE_POSITION_PROPERTY)) { - continue; - } - if (sb.length() > 0) { - sb.append("&"); - } - sb.append(e.getKey().getSimpleName()) - .append("=") - .append(URLEncoder.encode(e.getValue().toString())); - } - return sb.toString(); - } - - private SourcePosition getSourcePositionOverride( - Element annotatedElement, AnnotationMirror annotation) { - for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> e - : annotation.getElementValues().entrySet()) { - if (e.getKey().getSimpleName().toString().equals(OVERRIDE_SOURCE_POSITION_PROPERTY)) { - String[] position = e.getValue().getValue().toString().split(":"); - if (position.length != 5) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, String.format( - "Expected %s to have format file:startLine:startCol:endLine:endCol", - OVERRIDE_SOURCE_POSITION_PROPERTY), annotatedElement, annotation); - return null; - } - try { - return new SourcePosition(position[0], Integer.parseInt(position[1]), - Integer.parseInt(position[2]), Integer.parseInt(position[3]), - Integer.parseInt(position[4])); - } catch (NumberFormatException nfe) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, String.format( - "Expected %s to have format file:startLine:startCol:endLine:endCol; " - + "error parsing integer: %s", OVERRIDE_SOURCE_POSITION_PROPERTY, - nfe.getMessage()), annotatedElement, annotation); - return null; - } - } - } - return null; - } - - /** - * Maps an annotated element to the source position of the @UnsupportedAppUsage annotation - * attached to it. It returns CSV in the format: - * dex-signature,filename,start-line,start-col,end-line,end-col - * - * The positions refer to the annotation itself, *not* the annotated member. This can therefore - * be used to read just the annotation from the file, and to perform in-place edits on it. - * - * @param signature the dex signature for the element. - * @param annotatedElement The annotated element - * @return A single line of CSV text - */ - private String getAnnotationIndex(String signature, Element annotatedElement) { - JavacElements javacElem = (JavacElements) processingEnv.getElementUtils(); - AnnotationMirror unsupportedAppUsage = - getUnsupportedAppUsageAnnotationMirror(annotatedElement); - SourcePosition position = getSourcePositionOverride(annotatedElement, unsupportedAppUsage); - if (position == null) { - Pair<JCTree, JCTree.JCCompilationUnit> pair = - javacElem.getTreeAndTopLevel(annotatedElement, unsupportedAppUsage, null); - Position.LineMap lines = pair.snd.lineMap; - position = new SourcePosition( - pair.snd.getSourceFile().getName(), - lines.getLineNumber(pair.fst.pos().getStartPosition()), - lines.getColumnNumber(pair.fst.pos().getStartPosition()), - lines.getLineNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions)), - lines.getColumnNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions))); - } - return Joiner.on(",").join( - signature, - position.filename, - position.startLine, - position.startCol, - position.endLine, - position.endCol, - encodeAnnotationProperties(unsupportedAppUsage)); - } - - /** - * This is the main entry point in the processor, called by the compiler. - */ - @Override - public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { - Map<String, Element> signatureMap = new TreeMap<>(); - SignatureBuilder sb = new SignatureBuilder(processingEnv.getMessager()); - for (Class<? extends Annotation> supportedAnnotation : SUPPORTED_ANNOTATIONS) { - Set<? extends Element> annotated = roundEnv.getElementsAnnotatedWith( - supportedAnnotation); - if (annotated.size() == 0) { - continue; - } - // Build signatures for each annotated member and put them in a map from signature to - // member. - for (Element e : annotated) { - String sig = sb.buildSignature(supportedAnnotation, e); - if (sig != null) { - signatureMap.put(sig, e); - } - } - } - - if (!signatureMap.isEmpty()) { - try { - writeToFile(INDEX_CSV, - getCsvHeaders(), - signatureMap.entrySet() - .stream() - .map(e -> getAnnotationIndex(e.getKey(), e.getValue()))); - } catch (IOException e) { - throw new RuntimeException("Failed to write output", e); - } - } - return true; - } -} diff --git a/tools/processors/unsupportedappusage/test/Android.bp b/tools/processors/unsupportedappusage/test/Android.bp deleted file mode 100644 index e10fd47e8db1..000000000000 --- a/tools/processors/unsupportedappusage/test/Android.bp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -java_test_host { - name: "unsupportedappusage-processor-test", - - srcs: ["src/**/*.java"], - - static_libs: [ - "compile-testing-prebuilt", - "unsupportedappusage-annotation-processor-lib", - "truth-host-prebuilt", - "mockito-host", - "junit-host", - "objenesis", - ], -} diff --git a/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/CsvReader.java b/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/CsvReader.java deleted file mode 100644 index 23db99e81194..000000000000 --- a/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/CsvReader.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.processor.unsupportedappusage; - -import com.google.common.base.Splitter; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class CsvReader { - - private final Splitter mSplitter; - private final List<String> mColumns; - private final List<Map<String, String>> mContents; - - public CsvReader(InputStream in) throws IOException { - mSplitter = Splitter.on(","); - BufferedReader br = new BufferedReader(new InputStreamReader(in)); - mColumns = mSplitter.splitToList(br.readLine()); - mContents = new ArrayList<>(); - String line = br.readLine(); - while (line != null) { - List<String> contents = mSplitter.splitToList(line); - Map<String, String> contentMap = new HashMap<>(); - for (int i = 0; i < Math.min(contents.size(), mColumns.size()); ++i) { - contentMap.put(mColumns.get(i), contents.get(i)); - } - mContents.add(contentMap); - line = br.readLine(); - } - br.close(); - } - - public List<String> getColumns() { - return mColumns; - } - - public List<Map<String, String>> getContents() { - return mContents; - } -} diff --git a/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessorTest.java b/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessorTest.java deleted file mode 100644 index 75158eebdfdd..000000000000 --- a/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessorTest.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.processor.unsupportedappusage; - -import static com.google.common.truth.Truth.assertThat; - -import com.google.testing.compile.Compilation; -import com.google.testing.compile.CompilationSubject; -import com.google.testing.compile.Compiler; -import com.google.testing.compile.JavaFileObjects; - -import org.junit.Test; - -import java.io.IOException; -import java.util.Map; -import java.util.Optional; - -import javax.tools.JavaFileObject; -import javax.tools.StandardLocation; - -public class UnsupportedAppUsageProcessorTest { - - private static final JavaFileObject ANNOTATION = JavaFileObjects.forSourceLines( - "dalvik.dalvik.annotation.compat.UnsupportedAppUsage", - "package dalvik.annotation.compat;", - "public @interface UnsupportedAppUsage {", - " String expectedSignature() default \"\";\n", - " String someProperty() default \"\";", - " String overrideSourcePosition() default \"\";", - "}"); - - private CsvReader compileAndReadCsv(JavaFileObject source) throws IOException { - Compilation compilation = - Compiler.javac().withProcessors(new UnsupportedAppUsageProcessor()) - .compile(ANNOTATION, source); - CompilationSubject.assertThat(compilation).succeeded(); - Optional<JavaFileObject> csv = compilation.generatedFile(StandardLocation.CLASS_OUTPUT, - "unsupportedappusage/unsupportedappusage_index.csv"); - assertThat(csv.isPresent()).isTrue(); - - return new CsvReader(csv.get().openInputStream()); - } - - @Test - public void testSignatureFormat() throws Exception { - JavaFileObject src = JavaFileObjects.forSourceLines("a.b.Class", - "package a.b;", - "import dalvik.annotation.compat.UnsupportedAppUsage;", - "public class Class {", - " @UnsupportedAppUsage", - " public void method() {}", - "}"); - assertThat(compileAndReadCsv(src).getContents().get(0)).containsEntry( - "signature", "La/b/Class;->method()V" - ); - } - - @Test - public void testSourcePosition() throws Exception { - JavaFileObject src = JavaFileObjects.forSourceLines("a.b.Class", - "package a.b;", // 1 - "import dalvik.annotation.compat.UnsupportedAppUsage;", // 2 - "public class Class {", // 3 - " @UnsupportedAppUsage", // 4 - " public void method() {}", // 5 - "}"); - Map<String, String> row = compileAndReadCsv(src).getContents().get(0); - assertThat(row).containsEntry("startline", "4"); - assertThat(row).containsEntry("startcol", "3"); - assertThat(row).containsEntry("endline", "4"); - assertThat(row).containsEntry("endcol", "23"); - } - - @Test - public void testAnnotationProperties() throws Exception { - JavaFileObject src = JavaFileObjects.forSourceLines("a.b.Class", - "package a.b;", // 1 - "import dalvik.annotation.compat.UnsupportedAppUsage;", // 2 - "public class Class {", // 3 - " @UnsupportedAppUsage(someProperty=\"value\")", // 4 - " public void method() {}", // 5 - "}"); - assertThat(compileAndReadCsv(src).getContents().get(0)).containsEntry( - "properties", "someProperty=%22value%22"); - } - - @Test - public void testSourcePositionOverride() throws Exception { - JavaFileObject src = JavaFileObjects.forSourceLines("a.b.Class", - "package a.b;", // 1 - "import dalvik.annotation.compat.UnsupportedAppUsage;", // 2 - "public class Class {", // 3 - " @UnsupportedAppUsage(overrideSourcePosition=\"otherfile.aidl:30:10:31:20\")", - " public void method() {}", // 5 - "}"); - Map<String, String> row = compileAndReadCsv(src).getContents().get(0); - assertThat(row).containsEntry("file", "otherfile.aidl"); - assertThat(row).containsEntry("startline", "30"); - assertThat(row).containsEntry("startcol", "10"); - assertThat(row).containsEntry("endline", "31"); - assertThat(row).containsEntry("endcol", "20"); - assertThat(row).containsEntry("properties", ""); - } - - @Test - public void testSourcePositionOverrideWrongFormat() throws Exception { - JavaFileObject src = JavaFileObjects.forSourceLines("a.b.Class", - "package a.b;", // 1 - "import dalvik.annotation.compat.UnsupportedAppUsage;", // 2 - "public class Class {", // 3 - " @UnsupportedAppUsage(overrideSourcePosition=\"invalid\")", // 4 - " public void method() {}", // 5 - "}"); - Compilation compilation = - Compiler.javac().withProcessors(new UnsupportedAppUsageProcessor()) - .compile(ANNOTATION, src); - CompilationSubject.assertThat(compilation).failed(); - CompilationSubject.assertThat(compilation).hadErrorContaining( - "Expected overrideSourcePosition to have format " - + "file:startLine:startCol:endLine:endCol").inFile(src).onLine(4); - } - - @Test - public void testSourcePositionOverrideInvalidInt() throws Exception { - JavaFileObject src = JavaFileObjects.forSourceLines("a.b.Class", - "package a.b;", // 1 - "import dalvik.annotation.compat.UnsupportedAppUsage;", // 2 - "public class Class {", // 3 - " @UnsupportedAppUsage(overrideSourcePosition=\"otherfile.aidl:a:b:c:d\")", // 4 - " public void method() {}", // 5 - "}"); - Compilation compilation = - Compiler.javac().withProcessors(new UnsupportedAppUsageProcessor()) - .compile(ANNOTATION, src); - CompilationSubject.assertThat(compilation).failed(); - CompilationSubject.assertThat(compilation).hadErrorContaining( - "error parsing integer").inFile(src).onLine(4); - } - -} diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java index 9956901434d0..729ef61f0e4d 100644 --- a/wifi/java/android/net/wifi/ScanResult.java +++ b/wifi/java/android/net/wifi/ScanResult.java @@ -499,6 +499,13 @@ public class ScanResult implements Parcelable { /** * @hide + */ + public boolean is6GHz() { + return ScanResult.is6GHz(frequency); + } + + /** + * @hide * TODO: makes real freq boundaries */ public static boolean is5GHz(int freq) { @@ -506,6 +513,13 @@ public class ScanResult implements Parcelable { } /** + * @hide + */ + public static boolean is6GHz(int freq) { + return freq > 5925 && freq < 7125; + } + + /** * @hide * anqp lines from supplicant BSS response */ diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java index c743b63483d9..760497b727cd 100644 --- a/wifi/java/android/net/wifi/WifiScanner.java +++ b/wifi/java/android/net/wifi/WifiScanner.java @@ -740,6 +740,8 @@ public class WifiScanner { public int min5GHzRssi; /** Minimum 2.4GHz RSSI for a BSSID to be considered */ public int min24GHzRssi; + /** Minimum 6GHz RSSI for a BSSID to be considered */ + public int min6GHzRssi; /** Maximum score that a network can have before bonuses */ public int initialScoreMax; /** @@ -753,6 +755,8 @@ public class WifiScanner { public int secureBonus; /** 5GHz RSSI score bonus (applied to all 5GHz networks) */ public int band5GHzBonus; + /** 6GHz RSSI score bonus (applied to all 5GHz networks) */ + public int band6GHzBonus; /** Pno Network filter list */ public PnoNetwork[] networkList; @@ -766,11 +770,13 @@ public class WifiScanner { dest.writeInt(isConnected ? 1 : 0); dest.writeInt(min5GHzRssi); dest.writeInt(min24GHzRssi); + dest.writeInt(min6GHzRssi); dest.writeInt(initialScoreMax); dest.writeInt(currentConnectionBonus); dest.writeInt(sameNetworkBonus); dest.writeInt(secureBonus); dest.writeInt(band5GHzBonus); + dest.writeInt(band6GHzBonus); if (networkList != null) { dest.writeInt(networkList.length); for (int i = 0; i < networkList.length; i++) { @@ -792,11 +798,13 @@ public class WifiScanner { settings.isConnected = in.readInt() == 1; settings.min5GHzRssi = in.readInt(); settings.min24GHzRssi = in.readInt(); + settings.min6GHzRssi = in.readInt(); settings.initialScoreMax = in.readInt(); settings.currentConnectionBonus = in.readInt(); settings.sameNetworkBonus = in.readInt(); settings.secureBonus = in.readInt(); settings.band5GHzBonus = in.readInt(); + settings.band6GHzBonus = in.readInt(); int numNetworks = in.readInt(); settings.networkList = new PnoNetwork[numNetworks]; for (int i = 0; i < numNetworks; i++) { diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java index b1436c90f5dd..fa4f711056db 100644 --- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java @@ -79,15 +79,17 @@ public class WifiScannerTest { private static final boolean TEST_PNOSETTINGS_IS_CONNECTED = false; private static final int TEST_PNOSETTINGS_MIN_5GHZ_RSSI = -60; private static final int TEST_PNOSETTINGS_MIN_2GHZ_RSSI = -70; + private static final int TEST_PNOSETTINGS_MIN_6GHZ_RSSI = -55; private static final int TEST_PNOSETTINGS_INITIAL_SCORE_MAX = 50; private static final int TEST_PNOSETTINGS_CURRENT_CONNECTION_BONUS = 10; private static final int TEST_PNOSETTINGS_SAME_NETWORK_BONUS = 11; private static final int TEST_PNOSETTINGS_SECURE_BONUS = 12; private static final int TEST_PNOSETTINGS_BAND_5GHZ_BONUS = 13; + private static final int TEST_PNOSETTINGS_BAND_6GHZ_BONUS = 15; private static final String TEST_SSID_1 = "TEST1"; private static final String TEST_SSID_2 = "TEST2"; private static final int[] TEST_FREQUENCIES_1 = {}; - private static final int[] TEST_FREQUENCIES_2 = {2500, 5124}; + private static final int[] TEST_FREQUENCIES_2 = {2500, 5124, 6245}; private static final String DESCRIPTION_NOT_AUTHORIZED = "Not authorized"; private WifiScanner mWifiScanner; @@ -183,11 +185,13 @@ public class WifiScannerTest { pnoSettings.isConnected = TEST_PNOSETTINGS_IS_CONNECTED; pnoSettings.min5GHzRssi = TEST_PNOSETTINGS_MIN_5GHZ_RSSI; pnoSettings.min24GHzRssi = TEST_PNOSETTINGS_MIN_2GHZ_RSSI; + pnoSettings.min6GHzRssi = TEST_PNOSETTINGS_MIN_6GHZ_RSSI; pnoSettings.initialScoreMax = TEST_PNOSETTINGS_INITIAL_SCORE_MAX; pnoSettings.currentConnectionBonus = TEST_PNOSETTINGS_CURRENT_CONNECTION_BONUS; pnoSettings.sameNetworkBonus = TEST_PNOSETTINGS_SAME_NETWORK_BONUS; pnoSettings.secureBonus = TEST_PNOSETTINGS_SECURE_BONUS; pnoSettings.band5GHzBonus = TEST_PNOSETTINGS_BAND_5GHZ_BONUS; + pnoSettings.band6GHzBonus = TEST_PNOSETTINGS_BAND_6GHZ_BONUS; Parcel parcel = Parcel.obtain(); pnoSettings.writeToParcel(parcel, 0); @@ -200,6 +204,7 @@ public class WifiScannerTest { assertEquals(TEST_PNOSETTINGS_IS_CONNECTED, pnoSettingsDeserialized.isConnected); assertEquals(TEST_PNOSETTINGS_MIN_5GHZ_RSSI, pnoSettingsDeserialized.min5GHzRssi); assertEquals(TEST_PNOSETTINGS_MIN_2GHZ_RSSI, pnoSettingsDeserialized.min24GHzRssi); + assertEquals(TEST_PNOSETTINGS_MIN_6GHZ_RSSI, pnoSettingsDeserialized.min6GHzRssi); assertEquals(TEST_PNOSETTINGS_INITIAL_SCORE_MAX, pnoSettingsDeserialized.initialScoreMax); assertEquals(TEST_PNOSETTINGS_CURRENT_CONNECTION_BONUS, pnoSettingsDeserialized.currentConnectionBonus); @@ -207,6 +212,7 @@ public class WifiScannerTest { pnoSettingsDeserialized.sameNetworkBonus); assertEquals(TEST_PNOSETTINGS_SECURE_BONUS, pnoSettingsDeserialized.secureBonus); assertEquals(TEST_PNOSETTINGS_BAND_5GHZ_BONUS, pnoSettingsDeserialized.band5GHzBonus); + assertEquals(TEST_PNOSETTINGS_BAND_6GHZ_BONUS, pnoSettingsDeserialized.band6GHzBonus); // Test parsing of PnoNetwork assertEquals(pnoSettings.networkList.length, pnoSettingsDeserialized.networkList.length); |