diff options
217 files changed, 10689 insertions, 2250 deletions
diff --git a/Android.bp b/Android.bp index fcea5d02558b..42b7fd9eb6af 100644 --- a/Android.bp +++ b/Android.bp @@ -104,7 +104,6 @@ java_defaults { "core/java/android/app/timedetector/ITimeDetectorService.aidl", "core/java/android/app/timezone/ICallback.aidl", "core/java/android/app/timezone/IRulesManager.aidl", - "core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl", "core/java/android/app/usage/ICacheQuotaService.aidl", "core/java/android/app/usage/IStorageStatsManager.aidl", "core/java/android/app/usage/IUsageStatsManager.aidl", @@ -681,7 +680,6 @@ java_defaults { static_libs: [ "apex_aidl_interface-java", - "networkstack-aidl-interfaces-java", "framework-protos", "android.hidl.base-V1.0-java", "android.hardware.cas-V1.0-java", @@ -833,6 +831,7 @@ aidl_interface { "core/java/android/net/ProxyInfoParcelable.aidl", "core/java/android/net/RouteInfoParcelable.aidl", "core/java/android/net/StaticIpConfigurationParcelable.aidl", + "core/java/android/net/TcpKeepalivePacketDataParcelable.aidl", "core/java/android/net/dhcp/DhcpServingParamsParcel.aidl", "core/java/android/net/dhcp/IDhcpServer.aidl", "core/java/android/net/dhcp/IDhcpServerCallbacks.aidl", @@ -1110,58 +1109,12 @@ packages_to_document = [ "org/apache/http/params", ] -// The since flag (-since N.xml API_LEVEL) is used to add API Level information -// to the reference documentation. Must be in order of oldest to newest. -// -// Conscrypt (com.android.org.conscrypt) is an implementation detail and should -// not be referenced in the documentation. -framework_docs_args = "-android -manifest $(location core/res/AndroidManifest.xml) " + - "-hidePackage com.android.internal " + - "-hidePackage com.android.internal.util " + - "-hidePackage com.android.okhttp " + - "-hidePackage com.android.org.conscrypt " + - "-hidePackage com.android.server " + - "-since $(location 1/public/api/android.xml) 1 " + - "-since $(location 2/public/api/android.xml) 2 " + - "-since $(location 3/public/api/android.xml) 3 " + - "-since $(location 4/public/api/android.xml) 4 " + - "-since $(location 5/public/api/android.xml) 5 " + - "-since $(location 6/public/api/android.xml) 6 " + - "-since $(location 7/public/api/android.xml) 7 " + - "-since $(location 8/public/api/android.xml) 8 " + - "-since $(location 9/public/api/android.xml) 9 " + - "-since $(location 10/public/api/android.xml) 10 " + - "-since $(location 11/public/api/android.xml) 11 " + - "-since $(location 12/public/api/android.xml) 12 " + - "-since $(location 13/public/api/android.xml) 13 " + - "-since $(location 14/public/api/android.txt) 14 " + - "-since $(location 15/public/api/android.txt) 15 " + - "-since $(location 16/public/api/android.txt) 16 " + - "-since $(location 17/public/api/android.txt) 17 " + - "-since $(location 18/public/api/android.txt) 18 " + - "-since $(location 19/public/api/android.txt) 19 " + - "-since $(location 20/public/api/android.txt) 20 " + - "-since $(location 21/public/api/android.txt) 21 " + - "-since $(location 22/public/api/android.txt) 22 " + - "-since $(location 23/public/api/android.txt) 23 " + - "-since $(location 24/public/api/android.txt) 24 " + - "-since $(location 25/public/api/android.txt) 25 " + - "-since $(location 26/public/api/android.txt) 26 " + - "-since $(location 27/public/api/android.txt) 27 " + - "-since $(location 28/public/api/android.txt) 28 " + - "-since $(location api/current.txt) Q " + - "-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " + - "-overview $(location core/java/overview.html) " + - // Federate Support Library references against local API file. - "-federate SupportLib https://developer.android.com " + - "-federationapi SupportLib $(location current/support-api.txt) " - framework_docs_only_args = " -android -manifest $(location core/res/AndroidManifest.xml) " + "-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " + "-overview $(location core/java/overview.html) " + // Federate Support Library references against local API file. "-federate SupportLib https://developer.android.com " + - "-federationapi SupportLib $(location current/support-api.txt) " + "-federationapi SupportLib $(location :current-support-api) " framework_docs_only_libs = [ "voip-common", diff --git a/Android.mk b/Android.mk index 65d4d240fdcd..9a91dd1c491a 100644 --- a/Android.mk +++ b/Android.mk @@ -72,6 +72,9 @@ $(OUT_DOCS)/offline-sdk-timestamp: $(OUT_DOCS)/offline-sdk-docs-docs.zip $(hide) mkdir -p $(OUT_DOCS)/offline-sdk ( unzip -qo $< -d $(OUT_DOCS)/offline-sdk && touch -f $@ ) || exit 1 +.PHONY: docs offline-sdk-docs +docs offline-sdk-docs: $(OUT_DOCS)/offline-sdk-timestamp + # Run this for checkbuild checkbuild: doc-comment-check-docs # Check comment when you are updating the API @@ -79,34 +82,6 @@ update-api: doc-comment-check-docs # ==== hiddenapi lists ======================================= ifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true) -.KATI_RESTAT: $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS) -$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \ - PRIVATE_FLAGS_INPUTS := $(PRIVATE_FLAGS_INPUTS) $(SOONG_HIDDENAPI_FLAGS) -$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \ - frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \ - frameworks/base/config/hiddenapi-greylist.txt \ - frameworks/base/config/hiddenapi-greylist-max-p.txt \ - frameworks/base/config/hiddenapi-greylist-max-o.txt \ - frameworks/base/config/hiddenapi-force-blacklist.txt \ - $(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS) \ - $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \ - $(SOONG_HIDDENAPI_FLAGS) - frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \ - --csv $(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS) $(PRIVATE_FLAGS_INPUTS) \ - --greylist frameworks/base/config/hiddenapi-greylist.txt \ - --greylist-ignore-conflicts $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \ - --greylist-max-p frameworks/base/config/hiddenapi-greylist-max-p.txt \ - --greylist-max-o-ignore-conflicts \ - frameworks/base/config/hiddenapi-greylist-max-o.txt \ - --blacklist frameworks/base/config/hiddenapi-force-blacklist.txt \ - --output $@.tmp - $(call commit-change-for-toc,$@) - -$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA): \ - frameworks/base/tools/hiddenapi/merge_csv.py \ - $(PRIVATE_METADATA_INPUTS) - frameworks/base/tools/hiddenapi/merge_csv.py $(PRIVATE_METADATA_INPUTS) > $@ - $(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS)) $(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA)) endif # UNSAFE_DISABLE_HIDDENAPI_FLAGS diff --git a/api/TEST_MAPPING b/api/TEST_MAPPING new file mode 100644 index 000000000000..8a676e994081 --- /dev/null +++ b/api/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "CtsCurrentApiSignatureTestCases" + } + ] +} diff --git a/api/current.txt b/api/current.txt index 655aad3cab35..e8512486c7fc 100755 --- a/api/current.txt +++ b/api/current.txt @@ -18656,9 +18656,9 @@ package android.icu.text { method public static android.icu.text.BreakIterator getSentenceInstance(java.util.Locale); method public static android.icu.text.BreakIterator getSentenceInstance(android.icu.util.ULocale); method public abstract java.text.CharacterIterator getText(); - method public static android.icu.text.BreakIterator getTitleInstance(); - method public static android.icu.text.BreakIterator getTitleInstance(java.util.Locale); - method public static android.icu.text.BreakIterator getTitleInstance(android.icu.util.ULocale); + method @Deprecated public static android.icu.text.BreakIterator getTitleInstance(); + method @Deprecated public static android.icu.text.BreakIterator getTitleInstance(java.util.Locale); + method @Deprecated public static android.icu.text.BreakIterator getTitleInstance(android.icu.util.ULocale); method public static android.icu.text.BreakIterator getWordInstance(); method public static android.icu.text.BreakIterator getWordInstance(java.util.Locale); method public static android.icu.text.BreakIterator getWordInstance(android.icu.util.ULocale); @@ -18675,7 +18675,7 @@ package android.icu.text { field public static final int KIND_CHARACTER = 0; // 0x0 field public static final int KIND_LINE = 2; // 0x2 field public static final int KIND_SENTENCE = 3; // 0x3 - field public static final int KIND_TITLE = 4; // 0x4 + field @Deprecated public static final int KIND_TITLE = 4; // 0x4 field public static final int KIND_WORD = 1; // 0x1 field public static final int WORD_IDEO = 400; // 0x190 field public static final int WORD_IDEO_LIMIT = 500; // 0x1f4 @@ -27167,7 +27167,7 @@ package android.net { field public static final String EXTRA_NETWORK = "android.net.extra.NETWORK"; field @Deprecated public static final String EXTRA_NETWORK_INFO = "networkInfo"; field public static final String EXTRA_NETWORK_REQUEST = "android.net.extra.NETWORK_REQUEST"; - field public static final String EXTRA_NETWORK_TYPE = "networkType"; + field @Deprecated public static final String EXTRA_NETWORK_TYPE = "networkType"; field public static final String EXTRA_NO_CONNECTIVITY = "noConnectivity"; field @Deprecated public static final String EXTRA_OTHER_NETWORK_INFO = "otherNetwork"; field public static final String EXTRA_REASON = "reason"; @@ -40410,6 +40410,7 @@ package android.system { method public static java.io.FileDescriptor accept(java.io.FileDescriptor, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException; method public static boolean access(String, int) throws android.system.ErrnoException; method public static void bind(java.io.FileDescriptor, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException; + method public static void bind(java.io.FileDescriptor, java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException; method public static void chmod(String, int) throws android.system.ErrnoException; method public static void chown(String, int, int) throws android.system.ErrnoException; method public static void close(java.io.FileDescriptor) throws android.system.ErrnoException; @@ -40478,6 +40479,7 @@ package android.system { method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException; method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException; method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException; + method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException; method @Deprecated public static void setegid(int) throws android.system.ErrnoException; method public static void setenv(String, String, boolean) throws android.system.ErrnoException; method @Deprecated public static void seteuid(int) throws android.system.ErrnoException; @@ -41285,6 +41287,15 @@ package android.telecom { method public abstract void onScreenCall(@NonNull android.telecom.Call.Details); method public final void provideCallIdentification(@NonNull android.telecom.Call.Details, @NonNull android.telecom.CallIdentification); method public final void respondToCall(@NonNull android.telecom.Call.Details, @NonNull android.telecom.CallScreeningService.CallResponse); + field public static final String ACTION_NUISANCE_CALL_STATUS_CHANGED = "android.telecom.action.NUISANCE_CALL_STATUS_CHANGED"; + field public static final int CALL_DURATION_LONG = 4; // 0x4 + field public static final int CALL_DURATION_MEDIUM = 3; // 0x3 + field public static final int CALL_DURATION_SHORT = 2; // 0x2 + field public static final int CALL_DURATION_VERY_SHORT = 1; // 0x1 + field public static final String EXTRA_CALL_DURATION = "android.telecom.extra.CALL_DURATION"; + field public static final String EXTRA_CALL_HANDLE = "android.telecom.extra.CALL_HANDLE"; + field public static final String EXTRA_CALL_TYPE = "android.telecom.extra.CALL_TYPE"; + field public static final String EXTRA_IS_NUISANCE = "android.telecom.extra.IS_NUISANCE"; field public static final String SERVICE_INTERFACE = "android.telecom.CallScreeningService"; } @@ -41890,6 +41901,7 @@ package android.telecom { method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, String); method @RequiresPermission(anyOf={android.Manifest.permission.CALL_PHONE, android.Manifest.permission.MANAGE_OWN_CALLS}) public void placeCall(android.net.Uri, android.os.Bundle); method public void registerPhoneAccount(android.telecom.PhoneAccount); + method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void reportNuisanceCallStatus(@NonNull android.net.Uri, boolean); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void showInCallScreen(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void silenceRinger(); method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle); @@ -42324,6 +42336,7 @@ package android.telephony { method public int getChannelNumber(); method public String getMccString(); method public String getMncString(); + method public long getNci(); method public int getPci(); method public int getTac(); method public void writeToParcel(android.os.Parcel, int); @@ -42337,6 +42350,7 @@ package android.telephony { method public String getMccString(); method public String getMncString(); method @Nullable public String getMobileNetworkOperator(); + method public int getUarfcn(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityTdscdma> CREATOR; } @@ -43540,6 +43554,483 @@ package android.telephony.gsm { } +package android.telephony.ims { + + public class Rcs1To1Thread extends android.telephony.ims.RcsThread { + method @WorkerThread public long getFallbackThreadId() throws android.telephony.ims.RcsMessageStoreException; + method @NonNull @WorkerThread public android.telephony.ims.RcsParticipant getRecipient() throws android.telephony.ims.RcsMessageStoreException; + method public boolean isGroup(); + method @WorkerThread public void setFallbackThreadId(long) throws android.telephony.ims.RcsMessageStoreException; + } + + public abstract class RcsEvent { + ctor protected RcsEvent(long); + method public long getTimestamp(); + } + + public final class RcsEventQueryParams implements android.os.Parcelable { + method public int describeContents(); + method @android.telephony.ims.RcsEventQueryParams.EventType public int getEventType(); + method public int getLimit(); + method public boolean getSortDirection(); + method public int getSortingProperty(); + method public void writeToParcel(android.os.Parcel, int); + field public static final int ALL_EVENTS = -1; // 0xffffffff + field public static final int ALL_GROUP_THREAD_EVENTS = 0; // 0x0 + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsEventQueryParams> CREATOR; + field public static final int GROUP_THREAD_ICON_CHANGED_EVENT = 8; // 0x8 + field public static final int GROUP_THREAD_NAME_CHANGED_EVENT = 16; // 0x10 + field public static final int GROUP_THREAD_PARTICIPANT_JOINED_EVENT = 2; // 0x2 + field public static final int GROUP_THREAD_PARTICIPANT_LEFT_EVENT = 4; // 0x4 + field public static final int PARTICIPANT_ALIAS_CHANGED_EVENT = 1; // 0x1 + field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0 + field public static final int SORT_BY_TIMESTAMP = 1; // 0x1 + } + + public static class RcsEventQueryParams.Builder { + ctor public RcsEventQueryParams.Builder(); + method public android.telephony.ims.RcsEventQueryParams build(); + method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setEventType(@android.telephony.ims.RcsEventQueryParams.EventType int); + method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setGroupThread(@NonNull android.telephony.ims.RcsGroupThread); + method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException; + method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setSortDirection(boolean); + method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setSortProperty(@android.telephony.ims.RcsEventQueryParams.SortingProperty int); + } + + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsEventQueryParams.ALL_EVENTS, android.telephony.ims.RcsEventQueryParams.ALL_GROUP_THREAD_EVENTS, android.telephony.ims.RcsEventQueryParams.PARTICIPANT_ALIAS_CHANGED_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_PARTICIPANT_JOINED_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_PARTICIPANT_LEFT_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_NAME_CHANGED_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_ICON_CHANGED_EVENT}) public static @interface RcsEventQueryParams.EventType { + } + + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsEventQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsEventQueryParams.SORT_BY_TIMESTAMP}) public static @interface RcsEventQueryParams.SortingProperty { + } + + public final class RcsEventQueryResult implements android.os.Parcelable { + method public int describeContents(); + method public android.telephony.ims.RcsQueryContinuationToken getContinuationToken(); + method public java.util.List<android.telephony.ims.RcsEvent> getEvents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsEventQueryResult> CREATOR; + } + + public final class RcsFileTransferCreationParams implements android.os.Parcelable { + method public int describeContents(); + method public String getContentMimeType(); + method public android.net.Uri getContentUri(); + method public long getFileSize(); + method @android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus public int getFileTransferStatus(); + method public int getHeight(); + method public long getMediaDuration(); + method public String getPreviewMimeType(); + method public android.net.Uri getPreviewUri(); + method public String getRcsFileTransferSessionId(); + method public long getTransferOffset(); + method public int getWidth(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsFileTransferCreationParams> CREATOR; + } + + public class RcsFileTransferCreationParams.Builder { + ctor public RcsFileTransferCreationParams.Builder(); + method public android.telephony.ims.RcsFileTransferCreationParams build(); + method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setContentMimeType(String); + method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setContentUri(android.net.Uri); + method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setFileSize(long); + method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setFileTransferSessionId(String); + method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setFileTransferStatus(@android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus int); + method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setHeight(int); + method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setMediaDuration(long); + method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setPreviewMimeType(String); + method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setPreviewUri(android.net.Uri); + method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setTransferOffset(long); + method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setWidth(int); + } + + public class RcsFileTransferPart { + method @WorkerThread @Nullable public String getContentMimeType() throws android.telephony.ims.RcsMessageStoreException; + method @Nullable @WorkerThread public android.net.Uri getContentUri() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public long getFileSize() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public String getFileTransferSessionId() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus public int getFileTransferStatus() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public int getHeight() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public long getLength() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public String getPreviewMimeType() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public android.net.Uri getPreviewUri() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public long getTransferOffset() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public int getWidth() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setContentMimeType(String) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setContentUri(android.net.Uri) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setFileSize(long) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setFileTransferSessionId(String) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setFileTransferStatus(@android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus int) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setHeight(int) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setLength(long) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setPreviewMimeType(String) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setPreviewUri(android.net.Uri) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setTransferOffset(long) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setWidth(int) throws android.telephony.ims.RcsMessageStoreException; + field public static final int DOWNLOADING = 6; // 0x6 + field public static final int DOWNLOADING_CANCELLED = 9; // 0x9 + field public static final int DOWNLOADING_FAILED = 8; // 0x8 + field public static final int DOWNLOADING_PAUSED = 7; // 0x7 + field public static final int DRAFT = 1; // 0x1 + field public static final int NOT_SET = 0; // 0x0 + field public static final int SENDING = 2; // 0x2 + field public static final int SENDING_CANCELLED = 5; // 0x5 + field public static final int SENDING_FAILED = 4; // 0x4 + field public static final int SENDING_PAUSED = 3; // 0x3 + field public static final int SUCCEEDED = 10; // 0xa + } + + @IntDef({android.telephony.ims.RcsFileTransferPart.DRAFT, android.telephony.ims.RcsFileTransferPart.SENDING, android.telephony.ims.RcsFileTransferPart.SENDING_PAUSED, android.telephony.ims.RcsFileTransferPart.SENDING_FAILED, android.telephony.ims.RcsFileTransferPart.SENDING_CANCELLED, android.telephony.ims.RcsFileTransferPart.DOWNLOADING, android.telephony.ims.RcsFileTransferPart.DOWNLOADING_PAUSED, android.telephony.ims.RcsFileTransferPart.DOWNLOADING_FAILED, android.telephony.ims.RcsFileTransferPart.DOWNLOADING_CANCELLED, android.telephony.ims.RcsFileTransferPart.SUCCEEDED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RcsFileTransferPart.RcsFileTransferStatus { + } + + public class RcsGroupThread extends android.telephony.ims.RcsThread { + method @WorkerThread public void addParticipant(@NonNull android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException; + method @Nullable @WorkerThread public android.net.Uri getConferenceUri() throws android.telephony.ims.RcsMessageStoreException; + method @Nullable public android.net.Uri getGroupIcon() throws android.telephony.ims.RcsMessageStoreException; + method @Nullable @WorkerThread public String getGroupName() throws android.telephony.ims.RcsMessageStoreException; + method @Nullable @WorkerThread public android.telephony.ims.RcsParticipant getOwner() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public java.util.Set<android.telephony.ims.RcsParticipant> getParticipants() throws android.telephony.ims.RcsMessageStoreException; + method public boolean isGroup(); + method @WorkerThread public void removeParticipant(@NonNull android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException; + method @Nullable @WorkerThread public void setConferenceUri(android.net.Uri) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setGroupIcon(@Nullable android.net.Uri) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setGroupName(String) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setOwner(@Nullable android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException; + } + + public abstract class RcsGroupThreadEvent extends android.telephony.ims.RcsEvent { + method @NonNull public android.telephony.ims.RcsParticipant getOriginatingParticipant(); + method @NonNull public android.telephony.ims.RcsGroupThread getRcsGroupThread(); + } + + public final class RcsGroupThreadIconChangedEvent extends android.telephony.ims.RcsGroupThreadEvent implements android.os.Parcelable { + ctor public RcsGroupThreadIconChangedEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @Nullable android.net.Uri); + method public int describeContents(); + method @Nullable public android.net.Uri getNewIcon(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsGroupThreadIconChangedEvent> CREATOR; + } + + public final class RcsGroupThreadNameChangedEvent extends android.telephony.ims.RcsGroupThreadEvent implements android.os.Parcelable { + ctor public RcsGroupThreadNameChangedEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @Nullable String); + method public int describeContents(); + method @Nullable public String getNewName(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsGroupThreadNameChangedEvent> CREATOR; + } + + public final class RcsGroupThreadParticipantJoinedEvent extends android.telephony.ims.RcsGroupThreadEvent implements android.os.Parcelable { + ctor public RcsGroupThreadParticipantJoinedEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @NonNull android.telephony.ims.RcsParticipant); + method public int describeContents(); + method public android.telephony.ims.RcsParticipant getJoinedParticipant(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsGroupThreadParticipantJoinedEvent> CREATOR; + } + + public final class RcsGroupThreadParticipantLeftEvent extends android.telephony.ims.RcsGroupThreadEvent implements android.os.Parcelable { + ctor public RcsGroupThreadParticipantLeftEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @NonNull android.telephony.ims.RcsParticipant); + method public int describeContents(); + method @NonNull public android.telephony.ims.RcsParticipant getLeavingParticipantId(); + method public void persist() throws android.telephony.ims.RcsMessageStoreException; + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsGroupThreadParticipantLeftEvent> CREATOR; + } + + public class RcsIncomingMessage extends android.telephony.ims.RcsMessage { + method @WorkerThread public long getArrivalTimestamp() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public long getSeenTimestamp() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public android.telephony.ims.RcsParticipant getSenderParticipant() throws android.telephony.ims.RcsMessageStoreException; + method public boolean isIncoming(); + method @WorkerThread public void setArrivalTimestamp(long) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setSeenTimestamp(long) throws android.telephony.ims.RcsMessageStoreException; + } + + public final class RcsIncomingMessageCreationParams extends android.telephony.ims.RcsMessageCreationParams implements android.os.Parcelable { + method public int describeContents(); + method public long getArrivalTimestamp(); + method public long getSeenTimestamp(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsIncomingMessageCreationParams> CREATOR; + } + + public static class RcsIncomingMessageCreationParams.Builder extends android.telephony.ims.RcsMessageCreationParams.Builder { + ctor public RcsIncomingMessageCreationParams.Builder(long, long, int); + method public android.telephony.ims.RcsIncomingMessageCreationParams build(); + method @CheckResult public android.telephony.ims.RcsIncomingMessageCreationParams.Builder setArrivalTimestamp(long); + method @CheckResult public android.telephony.ims.RcsIncomingMessageCreationParams.Builder setSeenTimestamp(long); + method @CheckResult public android.telephony.ims.RcsIncomingMessageCreationParams.Builder setSenderParticipant(android.telephony.ims.RcsParticipant); + } + + public class RcsManager { + method public android.telephony.ims.RcsMessageStore getRcsMessageStore(); + } + + public abstract class RcsMessage { + method @NonNull @WorkerThread public java.util.Set<android.telephony.ims.RcsFileTransferPart> getFileTransferParts() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public double getLatitude() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public double getLongitude() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public long getOriginationTimestamp() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public String getRcsMessageId() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @android.telephony.ims.RcsMessage.RcsMessageStatus public int getStatus() throws android.telephony.ims.RcsMessageStoreException; + method public int getSubscriptionId() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public String getText() throws android.telephony.ims.RcsMessageStoreException; + method @NonNull @WorkerThread public android.telephony.ims.RcsFileTransferPart insertFileTransfer(android.telephony.ims.RcsFileTransferCreationParams) throws android.telephony.ims.RcsMessageStoreException; + method public abstract boolean isIncoming(); + method @WorkerThread public void removeFileTransferPart(@NonNull android.telephony.ims.RcsFileTransferPart) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setLatitude(double) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setLongitude(double) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setOriginationTimestamp(long) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setRcsMessageId(String) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setStatus(@android.telephony.ims.RcsMessage.RcsMessageStatus int) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setSubscriptionId(int) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setText(String) throws android.telephony.ims.RcsMessageStoreException; + field public static final int DRAFT = 1; // 0x1 + field public static final int FAILED = 6; // 0x6 + field public static final double LOCATION_NOT_SET = 4.9E-324; + field public static final int NOT_SET = 0; // 0x0 + field public static final int QUEUED = 2; // 0x2 + field public static final int RECEIVED = 7; // 0x7 + field public static final int RETRYING = 5; // 0x5 + field public static final int SEEN = 9; // 0x9 + field public static final int SENDING = 3; // 0x3 + field public static final int SENT = 4; // 0x4 + } + + @IntDef({android.telephony.ims.RcsMessage.DRAFT, android.telephony.ims.RcsMessage.QUEUED, android.telephony.ims.RcsMessage.SENDING, android.telephony.ims.RcsMessage.SENT, android.telephony.ims.RcsMessage.RETRYING, android.telephony.ims.RcsMessage.FAILED, android.telephony.ims.RcsMessage.RECEIVED, android.telephony.ims.RcsMessage.SEEN}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RcsMessage.RcsMessageStatus { + } + + public class RcsMessageCreationParams { + ctor protected RcsMessageCreationParams(android.telephony.ims.RcsMessageCreationParams.Builder); + method public double getLatitude(); + method public double getLongitude(); + method public int getMessageStatus(); + method public long getOriginationTimestamp(); + method @Nullable public String getRcsMessageGlobalId(); + method public int getSubId(); + method @Nullable public String getText(); + } + + public static class RcsMessageCreationParams.Builder { + method public android.telephony.ims.RcsMessageCreationParams build(); + method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setLatitude(double); + method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setLongitude(double); + method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setRcsMessageId(String); + method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setStatus(@android.telephony.ims.RcsMessage.RcsMessageStatus int); + method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setText(String); + } + + public final class RcsMessageQueryParams implements android.os.Parcelable { + method public int describeContents(); + method public int getFileTransferPresence(); + method public int getLimit(); + method public String getMessageLike(); + method public int getMessageType(); + method public boolean getSortDirection(); + method @android.telephony.ims.RcsMessageQueryParams.SortingProperty public int getSortingProperty(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsMessageQueryParams> CREATOR; + field public static final int MESSAGES_WITHOUT_FILE_TRANSFERS = 8; // 0x8 + field public static final int MESSAGES_WITH_FILE_TRANSFERS = 4; // 0x4 + field public static final int MESSAGE_TYPE_INCOMING = 1; // 0x1 + field public static final int MESSAGE_TYPE_OUTGOING = 2; // 0x2 + field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0 + field public static final int SORT_BY_TIMESTAMP = 1; // 0x1 + } + + public static class RcsMessageQueryParams.Builder { + ctor public RcsMessageQueryParams.Builder(); + method public android.telephony.ims.RcsMessageQueryParams build(); + method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setFileTransferPresence(int); + method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setMessageLike(String); + method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setMessageType(int); + method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException; + method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setSortDirection(boolean); + method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setSortProperty(@android.telephony.ims.RcsMessageQueryParams.SortingProperty int); + method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setThread(@Nullable android.telephony.ims.RcsThread); + } + + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsMessageQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsMessageQueryParams.SORT_BY_TIMESTAMP}) public static @interface RcsMessageQueryParams.SortingProperty { + } + + public final class RcsMessageQueryResult implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public android.telephony.ims.RcsQueryContinuationToken getContinuationToken(); + method @NonNull public java.util.List<android.telephony.ims.RcsMessage> getMessages(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsMessageQueryResult> CREATOR; + } + + public final class RcsMessageSnippet implements android.os.Parcelable { + method public int describeContents(); + method @android.telephony.ims.RcsMessage.RcsMessageStatus public int getSnippetStatus(); + method @Nullable public String getSnippetText(); + method public long getSnippetTimestamp(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsMessageSnippet> CREATOR; + } + + public class RcsMessageStore { + ctor public RcsMessageStore(); + method @WorkerThread @NonNull public android.telephony.ims.RcsGroupThread createGroupThread(@Nullable java.util.List<android.telephony.ims.RcsParticipant>, @Nullable String, @Nullable android.net.Uri) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.Rcs1To1Thread createRcs1To1Thread(@NonNull android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsParticipant createRcsParticipant(String, @Nullable String) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void deleteThread(@NonNull android.telephony.ims.RcsThread) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsEventQueryResult getRcsEvents(@Nullable android.telephony.ims.RcsEventQueryParams) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsEventQueryResult getRcsEvents(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsMessageQueryResult getRcsMessages(@Nullable android.telephony.ims.RcsMessageQueryParams) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsMessageQueryResult getRcsMessages(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsParticipantQueryResult getRcsParticipants(@Nullable android.telephony.ims.RcsParticipantQueryParams) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsParticipantQueryResult getRcsParticipants(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsThreadQueryResult getRcsThreads(@Nullable android.telephony.ims.RcsThreadQueryParams) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsThreadQueryResult getRcsThreads(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public void persistRcsEvent(android.telephony.ims.RcsEvent) throws android.telephony.ims.RcsMessageStoreException; + } + + public class RcsMessageStoreException extends java.lang.Exception { + ctor public RcsMessageStoreException(String); + } + + public class RcsOutgoingMessage extends android.telephony.ims.RcsMessage { + method @NonNull @WorkerThread public java.util.List<android.telephony.ims.RcsOutgoingMessageDelivery> getOutgoingDeliveries() throws android.telephony.ims.RcsMessageStoreException; + method public boolean isIncoming(); + } + + public final class RcsOutgoingMessageCreationParams extends android.telephony.ims.RcsMessageCreationParams implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsOutgoingMessageCreationParams> CREATOR; + } + + public static class RcsOutgoingMessageCreationParams.Builder extends android.telephony.ims.RcsMessageCreationParams.Builder { + ctor public RcsOutgoingMessageCreationParams.Builder(long, int); + method public android.telephony.ims.RcsOutgoingMessageCreationParams build(); + } + + public class RcsOutgoingMessageDelivery { + method @WorkerThread public long getDeliveredTimestamp() throws android.telephony.ims.RcsMessageStoreException; + method @NonNull public android.telephony.ims.RcsOutgoingMessage getMessage(); + method @NonNull public android.telephony.ims.RcsParticipant getRecipient(); + method @WorkerThread public long getSeenTimestamp() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @android.telephony.ims.RcsMessage.RcsMessageStatus public int getStatus() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setDeliveredTimestamp(long) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setSeenTimestamp(long) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setStatus(@android.telephony.ims.RcsMessage.RcsMessageStatus int) throws android.telephony.ims.RcsMessageStoreException; + } + + public class RcsParticipant { + method @Nullable @WorkerThread public String getAlias() throws android.telephony.ims.RcsMessageStoreException; + method @Nullable @WorkerThread public String getCanonicalAddress() throws android.telephony.ims.RcsMessageStoreException; + method @Nullable @WorkerThread public String getContactId() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setAlias(String) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void setContactId(String) throws android.telephony.ims.RcsMessageStoreException; + } + + public final class RcsParticipantAliasChangedEvent extends android.telephony.ims.RcsEvent implements android.os.Parcelable { + ctor public RcsParticipantAliasChangedEvent(long, @NonNull android.telephony.ims.RcsParticipant, @Nullable String); + method public int describeContents(); + method @Nullable public String getNewAlias(); + method @NonNull public android.telephony.ims.RcsParticipant getParticipantId(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsParticipantAliasChangedEvent> CREATOR; + } + + public final class RcsParticipantQueryParams implements android.os.Parcelable { + method public int describeContents(); + method public String getAliasLike(); + method public String getCanonicalAddressLike(); + method public int getLimit(); + method public boolean getSortDirection(); + method public int getSortingProperty(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsParticipantQueryParams> CREATOR; + field public static final int SORT_BY_ALIAS = 1; // 0x1 + field public static final int SORT_BY_CANONICAL_ADDRESS = 2; // 0x2 + field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0 + } + + public static class RcsParticipantQueryParams.Builder { + ctor public RcsParticipantQueryParams.Builder(); + method public android.telephony.ims.RcsParticipantQueryParams build(); + method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setAliasLike(String); + method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setCanonicalAddressLike(String); + method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException; + method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setSortDirection(boolean); + method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setSortProperty(@android.telephony.ims.RcsParticipantQueryParams.SortingProperty int); + method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setThread(android.telephony.ims.RcsThread); + } + + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsParticipantQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsParticipantQueryParams.SORT_BY_ALIAS, android.telephony.ims.RcsParticipantQueryParams.SORT_BY_CANONICAL_ADDRESS}) public static @interface RcsParticipantQueryParams.SortingProperty { + } + + public final class RcsParticipantQueryResult implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public android.telephony.ims.RcsQueryContinuationToken getContinuationToken(); + method @NonNull public java.util.List<android.telephony.ims.RcsParticipant> getParticipants(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsParticipantQueryResult> CREATOR; + } + + public final class RcsQueryContinuationToken implements android.os.Parcelable { + method public int describeContents(); + method @android.telephony.ims.RcsQueryContinuationToken.ContinuationTokenType public int getQueryType(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsQueryContinuationToken> CREATOR; + field public static final int EVENT_QUERY_CONTINUATION_TOKEN_TYPE = 0; // 0x0 + field public static final int MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE = 1; // 0x1 + field public static final int PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE = 2; // 0x2 + field public static final int THREAD_QUERY_CONTINUATION_TOKEN_TYPE = 3; // 0x3 + } + + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsQueryContinuationToken.EVENT_QUERY_CONTINUATION_TOKEN_TYPE, android.telephony.ims.RcsQueryContinuationToken.MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE, android.telephony.ims.RcsQueryContinuationToken.PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE, android.telephony.ims.RcsQueryContinuationToken.THREAD_QUERY_CONTINUATION_TOKEN_TYPE}) public static @interface RcsQueryContinuationToken.ContinuationTokenType { + } + + public abstract class RcsThread { + method @WorkerThread @NonNull public android.telephony.ims.RcsIncomingMessage addIncomingMessage(@NonNull android.telephony.ims.RcsIncomingMessageCreationParams) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsOutgoingMessage addOutgoingMessage(@NonNull android.telephony.ims.RcsOutgoingMessageCreationParams) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread public void deleteMessage(@NonNull android.telephony.ims.RcsMessage) throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsMessageQueryResult getMessages() throws android.telephony.ims.RcsMessageStoreException; + method @WorkerThread @NonNull public android.telephony.ims.RcsMessageSnippet getSnippet() throws android.telephony.ims.RcsMessageStoreException; + method public abstract boolean isGroup(); + } + + public final class RcsThreadQueryParams implements android.os.Parcelable { + method public int describeContents(); + method public int getLimit(); + method public boolean getSortDirection(); + method @android.telephony.ims.RcsThreadQueryParams.SortingProperty public int getSortingProperty(); + method public int getThreadType(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsThreadQueryParams> CREATOR; + field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0 + field public static final int SORT_BY_TIMESTAMP = 1; // 0x1 + field public static final int THREAD_TYPE_1_TO_1 = 2; // 0x2 + field public static final int THREAD_TYPE_GROUP = 1; // 0x1 + } + + public static class RcsThreadQueryParams.Builder { + ctor public RcsThreadQueryParams.Builder(); + method public android.telephony.ims.RcsThreadQueryParams build(); + method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setParticipant(@NonNull android.telephony.ims.RcsParticipant); + method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setParticipants(@NonNull java.util.List<android.telephony.ims.RcsParticipant>); + method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException; + method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setSortDirection(boolean); + method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setSortProperty(@android.telephony.ims.RcsThreadQueryParams.SortingProperty int); + method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setThreadType(int); + } + + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsThreadQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsThreadQueryParams.SORT_BY_TIMESTAMP}) public static @interface RcsThreadQueryParams.SortingProperty { + } + + public final class RcsThreadQueryResult implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public android.telephony.ims.RcsQueryContinuationToken getContinuationToken(); + method @NonNull public java.util.List<android.telephony.ims.RcsThread> getThreads(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsThreadQueryResult> CREATOR; + } + +} + package android.telephony.mbms { public class DownloadProgressListener { @@ -58473,9 +58964,9 @@ package java.lang.invoke { method public String getName(); method public int getReferenceKind(); method public default boolean isVarArgs(); - method public static boolean refKindIsField(int); - method public static boolean refKindIsValid(int); - method public static String refKindName(int); + method @Deprecated public static boolean refKindIsField(int); + method @Deprecated public static boolean refKindIsValid(int); + method @Deprecated public static String refKindName(int); method public static String referenceKindToString(int); method public <T extends java.lang.reflect.Member> T reflectAs(Class<T>, java.lang.invoke.MethodHandles.Lookup); method public static String toString(int, Class<?>, String, java.lang.invoke.MethodType); diff --git a/api/system-current.txt b/api/system-current.txt index 76cfa9907ca9..33cc4162ca54 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -862,6 +862,32 @@ package android.content { method public void sendOrderedBroadcast(android.content.Intent, String, android.os.Bundle, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle); } + public class DynamicAndroidClient { + ctor public DynamicAndroidClient(@NonNull android.content.Context); + method public void bind(); + method public void setOnStatusChangedListener(@NonNull android.content.DynamicAndroidClient.OnStatusChangedListener, @NonNull java.util.concurrent.Executor); + method public void setOnStatusChangedListener(@NonNull android.content.DynamicAndroidClient.OnStatusChangedListener); + method public void start(String, long); + method public void start(String, long, long); + method public void unbind(); + field public static final int CAUSE_ERROR_EXCEPTION = 6; // 0x6 + field public static final int CAUSE_ERROR_INVALID_URL = 4; // 0x4 + field public static final int CAUSE_ERROR_IO = 3; // 0x3 + field public static final int CAUSE_ERROR_IPC = 5; // 0x5 + field public static final int CAUSE_INSTALL_CANCELLED = 2; // 0x2 + field public static final int CAUSE_INSTALL_COMPLETED = 1; // 0x1 + field public static final int CAUSE_NOT_SPECIFIED = 0; // 0x0 + field public static final int STATUS_IN_PROGRESS = 2; // 0x2 + field public static final int STATUS_IN_USE = 4; // 0x4 + field public static final int STATUS_NOT_STARTED = 1; // 0x1 + field public static final int STATUS_READY = 3; // 0x3 + field public static final int STATUS_UNKNOWN = 0; // 0x0 + } + + public static interface DynamicAndroidClient.OnStatusChangedListener { + method public void onStatusChanged(int, int, long); + } + public class Intent implements java.lang.Cloneable android.os.Parcelable { field public static final String ACTION_BATTERY_LEVEL_CHANGED = "android.intent.action.BATTERY_LEVEL_CHANGED"; field public static final String ACTION_CALL_EMERGENCY = "android.intent.action.CALL_EMERGENCY"; @@ -3069,11 +3095,13 @@ package android.net { public class ConnectivityManager { method @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull java.io.FileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback); + method @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull java.net.Socket, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback); method public boolean getAvoidBadWifi(); method @RequiresPermission(android.Manifest.permission.LOCAL_MAC_ADDRESS) public String getCaptivePortalServerUrl(); method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementValue(int, boolean, @NonNull android.net.ConnectivityManager.TetheringEntitlementValueListener, @Nullable android.os.Handler); method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported(); method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void setAirplaneMode(boolean); + method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.os.Bundle); method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback); method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler); method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int); @@ -5527,6 +5555,7 @@ package android.telephony { method @NonNull public java.util.List<android.service.carrier.CarrierIdentifier> getExcludedCarriers(); method public int getMultiSimPolicy(); method public boolean isAllCarriersAllowed(); + method public java.util.List<java.lang.Boolean> isCarrierIdentifiersAllowed(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>); method public void writeToParcel(android.os.Parcel, int); field public static final int CARRIER_RESTRICTION_DEFAULT_ALLOWED = 1; // 0x1 field public static final int CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED = 0; // 0x0 @@ -6250,10 +6279,13 @@ package android.telephony { public class SubscriptionManager { method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList(); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSubscriptionEnabled(int); method public void requestEmbeddedSubscriptionInfoListRefresh(); method public void requestEmbeddedSubscriptionInfoListRefresh(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultDataSubId(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultSmsSubId(int); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setSubscriptionEnabled(int, boolean); field public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI; field public static final int PROFILE_CLASS_DEFAULT = -1; // 0xffffffff field public static final int PROFILE_CLASS_OPERATIONAL = 2; // 0x2 @@ -6320,11 +6352,13 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst(); + method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.util.Pair<java.lang.Integer,java.lang.Integer>> getLogicalToPhysicalSlotMapping(); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getPreferredNetworkTypeBitmap(); method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public int getRadioPowerState(); method public int getSimApplicationState(); method public int getSimCardState(); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getSimLocale(); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSupportedRadioAccessFamily(); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getSupportedRadioAccessFamily(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.UiccSlotInfo[] getUiccSlotsInfo(); method @Nullable public android.os.Bundle getVisualVoicemailSettings(); @@ -6337,6 +6371,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMultisimCarrierRestricted(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRadioOn(); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isRebootRequiredForModemConfigChange(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging(); method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isVideoCallingEnabled(); method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle); @@ -6352,6 +6387,7 @@ package android.telephony { method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultisimCarrierRestriction(boolean); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmap(long); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadio(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadioPower(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerState(int); @@ -6379,25 +6415,26 @@ package android.telephony { field public static final String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL"; field public static final String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING"; field public static final long MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS = 60000L; // 0xea60L - field public static final int NETWORK_TYPE_BITMASK_1xRTT = 128; // 0x80 - field public static final int NETWORK_TYPE_BITMASK_CDMA = 16; // 0x10 - field public static final int NETWORK_TYPE_BITMASK_EDGE = 4; // 0x4 - field public static final int NETWORK_TYPE_BITMASK_EHRPD = 16384; // 0x4000 - field public static final int NETWORK_TYPE_BITMASK_EVDO_0 = 32; // 0x20 - field public static final int NETWORK_TYPE_BITMASK_EVDO_A = 64; // 0x40 - field public static final int NETWORK_TYPE_BITMASK_EVDO_B = 4096; // 0x1000 - field public static final int NETWORK_TYPE_BITMASK_GPRS = 2; // 0x2 - field public static final int NETWORK_TYPE_BITMASK_GSM = 65536; // 0x10000 - field public static final int NETWORK_TYPE_BITMASK_HSDPA = 256; // 0x100 - field public static final int NETWORK_TYPE_BITMASK_HSPA = 1024; // 0x400 - field public static final int NETWORK_TYPE_BITMASK_HSPAP = 32768; // 0x8000 - field public static final int NETWORK_TYPE_BITMASK_HSUPA = 512; // 0x200 - field public static final int NETWORK_TYPE_BITMASK_LTE = 8192; // 0x2000 - field public static final int NETWORK_TYPE_BITMASK_LTE_CA = 524288; // 0x80000 - field public static final int NETWORK_TYPE_BITMASK_NR = 1048576; // 0x100000 - field public static final int NETWORK_TYPE_BITMASK_TD_SCDMA = 131072; // 0x20000 - field public static final int NETWORK_TYPE_BITMASK_UMTS = 8; // 0x8 - field public static final int NETWORK_TYPE_BITMASK_UNKNOWN = 1; // 0x1 + field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L + field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L + field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L + field public static final long NETWORK_TYPE_BITMASK_EHRPD = 8192L; // 0x2000L + field public static final long NETWORK_TYPE_BITMASK_EVDO_0 = 16L; // 0x10L + field public static final long NETWORK_TYPE_BITMASK_EVDO_A = 32L; // 0x20L + field public static final long NETWORK_TYPE_BITMASK_EVDO_B = 2048L; // 0x800L + field public static final long NETWORK_TYPE_BITMASK_GPRS = 1L; // 0x1L + field public static final long NETWORK_TYPE_BITMASK_GSM = 32768L; // 0x8000L + field public static final long NETWORK_TYPE_BITMASK_HSDPA = 128L; // 0x80L + field public static final long NETWORK_TYPE_BITMASK_HSPA = 512L; // 0x200L + field public static final long NETWORK_TYPE_BITMASK_HSPAP = 16384L; // 0x4000L + field public static final long NETWORK_TYPE_BITMASK_HSUPA = 256L; // 0x100L + field public static final long NETWORK_TYPE_BITMASK_IWLAN = 131072L; // 0x20000L + field public static final long NETWORK_TYPE_BITMASK_LTE = 4096L; // 0x1000L + field public static final long NETWORK_TYPE_BITMASK_LTE_CA = 262144L; // 0x40000L + field public static final long NETWORK_TYPE_BITMASK_NR = 524288L; // 0x80000L + field public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = 65536L; // 0x10000L + field public static final long NETWORK_TYPE_BITMASK_UMTS = 4L; // 0x4L + field public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L; // 0x0L field public static final int RADIO_POWER_OFF = 0; // 0x0 field public static final int RADIO_POWER_ON = 1; // 0x1 field public static final int RADIO_POWER_UNAVAILABLE = 2; // 0x2 diff --git a/api/test-current.txt b/api/test-current.txt index 34c8f6ef5ece..01127249c1fc 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -608,6 +608,7 @@ package android.net { } public class ConnectivityManager { + method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.os.Bundle); field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC"; field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT"; } diff --git a/cmds/bootanimation/bootanim.rc b/cmds/bootanimation/bootanim.rc index 1b3c32b20503..469c9646a4aa 100644 --- a/cmds/bootanimation/bootanim.rc +++ b/cmds/bootanimation/bootanim.rc @@ -2,7 +2,6 @@ service bootanim /system/bin/bootanimation class core animation user graphics group graphics audio - updatable disabled oneshot writepid /dev/stune/top-app/tasks diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 5fd148e5d5af..1d629da2f250 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -161,6 +161,8 @@ message Atom { BluetoothBondStateChanged bluetooth_bond_state_changed = 165; BluetoothClassicPairingEventReported bluetooth_classic_pairing_event_reported = 166; BluetoothSmpPairingEventReported bluetooth_smp_pairing_event_reported = 167; + ProcessStartTime process_start_time = 169; + BluetoothSocketConnectionStateChanged bluetooth_socket_connection_state_changed = 171; } // Pulled events will start at field 10000. @@ -1719,6 +1721,44 @@ message BluetoothSmpPairingEventReported { } /** + * Logs when a Bluetooth socket’s connection state changed + * + * Logged from: + * system/bt + */ +message BluetoothSocketConnectionStateChanged { + // An identifier that can be used to match events for this device. + // Currently, this is a salted hash of the MAC address of this Bluetooth device. + // Salt: Randomly generated 256 bit value + // Hash algorithm: HMAC-SHA256 + // Size: 32 byte + // Default: null or empty if this is a server listener socket + optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; + // Temporary port of this socket for the current connection or session only + // Default 0 when unknown or don't care + optional int32 port = 2; + // Socket type as mentioned in + // frameworks/base/core/java/android/bluetooth/BluetoothSocket.java + // Default: SOCKET_TYPE_UNKNOWN + optional android.bluetooth.SocketTypeEnum type = 3; + // Socket connection state + // Default: SOCKET_CONNECTION_STATE_UNKNOWN + optional android.bluetooth.SocketConnectionstateEnum state = 4; + // Number of bytes sent to remote device during this connection + optional int64 tx_bytes = 5; + // Number of bytes received from remote device during this connection + optional int64 rx_bytes = 6; + // Socket owner's UID + optional int32 uid = 7 [(is_uid) = true]; + // Server port of this socket, if any. When both |server_port| and |port| fields are populated, + // |port| must be spawned by |server_port| + // Default 0 when unknown or don't care + optional int32 server_port = 8; + // Whether this is a server listener socket + optional android.bluetooth.SocketRoleEnum is_server = 9; +} + +/** * Logs when something is plugged into or removed from the USB-C connector. * * Logged from: @@ -2929,3 +2969,59 @@ message SeOmapiReported { optional string package_name = 3; } + +/* +* Logs number of milliseconds it takes to start a process. +* The definition of app process start time is from the app launch time to +* the time that Zygote finished forking the app process and loaded the +* application package's java classes. + +* This metric is different from AppStartOccurred which is for foreground +* activity only. + +* ProcessStartTime can report all processes (both foreground and background) +* start time. +* +* Logged from: +* frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java +*/ +message ProcessStartTime { + // The uid of the ProcessRecord. + optional int32 uid = 1 [(is_uid) = true]; + + // The process pid. + optional int32 pid = 2; + + // The process name. + // Usually package name, "system" for system server. + // Provided by ActivityManagerService. + optional string process_name = 3; + + enum StartType { + UNKNOWN = 0; + WARM = 1; + HOT = 2; + COLD = 3; + } + + // The start type. + optional StartType type = 4; + + // The elapsed realtime at the start of the process. + optional int64 process_start_time_millis = 5; + + // Number of milliseconds it takes to reach bind application. + optional int32 bind_application_delay_millis = 6; + + // Number of milliseconds it takes to finish start of the process. + optional int32 process_start_delay_millis = 7; + + // hostingType field in ProcessRecord, the component type such as "activity", + // "service", "content provider", "broadcast" or other strings. + optional string hosting_type = 8; + + // hostingNameStr field in ProcessRecord. The component class name that runs + // in this process. + optional string hosting_name = 9; +} + diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt index 3d5b32a62ca5..236919866be3 100644 --- a/config/hiddenapi-greylist.txt +++ b/config/hiddenapi-greylist.txt @@ -2546,7 +2546,6 @@ Lcom/android/internal/telephony/cat/CatService;->isStkAppInstalled()Z Lcom/android/internal/telephony/cat/CatService;->mCmdIf:Lcom/android/internal/telephony/CommandsInterface; Lcom/android/internal/telephony/cat/CatService;->mContext:Landroid/content/Context; Lcom/android/internal/telephony/cat/CatService;->mCurrntCmd:Lcom/android/internal/telephony/cat/CatCmdMessage; -Lcom/android/internal/telephony/cat/CatService;->mHandlerThread:Landroid/os/HandlerThread; Lcom/android/internal/telephony/cat/CatService;->mMenuCmd:Lcom/android/internal/telephony/cat/CatCmdMessage; Lcom/android/internal/telephony/cat/CatService;->mMsgDecoder:Lcom/android/internal/telephony/cat/RilMessageDecoder; Lcom/android/internal/telephony/cat/CatService;->mSlotId:I diff --git a/core/java/android/accessibilityservice/OWNERS b/core/java/android/accessibilityservice/OWNERS new file mode 100644 index 000000000000..265674a74b7e --- /dev/null +++ b/core/java/android/accessibilityservice/OWNERS @@ -0,0 +1,3 @@ +svetoslavganov@google.com +pweaver@google.com +rhedjao@google.com diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 0d3110cfded8..ac33c169288b 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -850,8 +850,9 @@ public final class LoadedApk { } } - // /vendor/lib, /odm/lib and /product/lib are added to the native lib search - // paths of the classloader. Note that this is done AFTER the classloader is + // /aepx/com.android.runtime/lib, /vendor/lib, /odm/lib and /product/lib + // are added to the native lib search paths of the classloader. + // Note that this is done AFTER the classloader is // created by ApplicationLoaders.getDefault().getClassLoader(...). The // reason is because if we have added the paths when creating the classloader // above, the paths are also added to the search path of the linker namespace @@ -868,8 +869,11 @@ public final class LoadedApk { // System.loadLibrary(). In order to prevent the problem, we explicitly // add the paths only to the classloader, and not to the native loader // (linker namespace). - List<String> extraLibPaths = new ArrayList<>(3); + List<String> extraLibPaths = new ArrayList<>(4); String abiSuffix = VMRuntime.getRuntime().is64Bit() ? "64" : ""; + if (!defaultSearchPaths.contains("/apex/com.android.runtime/lib")) { + extraLibPaths.add("/apex/com.android.runtime/lib" + abiSuffix); + } if (!defaultSearchPaths.contains("/vendor/lib")) { extraLibPaths.add("/vendor/lib" + abiSuffix); } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index a8fc9d242a1e..c42a2bce2c48 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -26,7 +26,6 @@ import android.app.job.JobScheduler; import android.app.slice.SliceManager; import android.app.timedetector.TimeDetector; import android.app.timezone.RulesManager; -import android.app.timezonedetector.TimeZoneDetector; import android.app.trust.TrustManager; import android.app.usage.IStorageStatsManager; import android.app.usage.IUsageStatsManager; @@ -1060,13 +1059,6 @@ final class SystemServiceRegistry { throws ServiceNotFoundException { return new TimeDetector(); }}); - registerService(Context.TIME_ZONE_DETECTOR_SERVICE, TimeZoneDetector.class, - new CachedServiceFetcher<TimeZoneDetector>() { - @Override - public TimeZoneDetector createService(ContextImpl ctx) - throws ServiceNotFoundException { - return new TimeZoneDetector(); - }}); registerService(Context.DYNAMIC_ANDROID_SERVICE, DynamicAndroidManager.class, new CachedServiceFetcher<DynamicAndroidManager>() { @Override diff --git a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl deleted file mode 100644 index ef2cbab137dc..000000000000 --- a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl +++ /dev/null @@ -1,34 +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.app.timezonedetector; - -/** - * System private API to comunicate with time zone detector service. - * - * <p>Used by parts of the Android system with signals associated with the device's time zone to - * provide information to the Time Zone Detector Service. - * - * <p>Use the {@link android.app.timezonedetector.TimeZoneDetector} class rather than going through - * this Binder interface directly. See {@link android.app.timezonedetector.TimeZoneDetectorService} - * for more complete documentation. - * - * - * {@hide} - */ -interface ITimeZoneDetectorService { - void stubbedCall(); -} diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java deleted file mode 100644 index be3c7649a486..000000000000 --- a/core/java/android/app/timezonedetector/TimeZoneDetector.java +++ /dev/null @@ -1,57 +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.app.timezonedetector; - -import android.annotation.SystemService; -import android.content.Context; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.ServiceManager.ServiceNotFoundException; -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 { - - private static final String TAG = "timezonedetector.TimeZoneDetector"; - private static final boolean DEBUG = false; - - private final ITimeZoneDetectorService mITimeZoneDetectorService; - - public TimeZoneDetector() throws ServiceNotFoundException { - mITimeZoneDetectorService = ITimeZoneDetectorService.Stub.asInterface( - ServiceManager.getServiceOrThrow(Context.TIME_ZONE_DETECTOR_SERVICE)); - - } - /** - * Does nothing. - * TODO: Remove this when the service implementation is built out. - */ - public void stubbedCall() { - if (DEBUG) { - Log.d(TAG, "stubbedCall called"); - } - try { - mITimeZoneDetectorService.stubbedCall(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } -} diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index ba6e7ae82276..493aac6e5c40 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3087,7 +3087,6 @@ public abstract class Context { CROSS_PROFILE_APPS_SERVICE, //@hide: SYSTEM_UPDATE_SERVICE, //@hide: TIME_DETECTOR_SERVICE, - //@hide: TIME_ZONE_DETECTOR_SERVICE, }) @Retention(RetentionPolicy.SOURCE) public @interface ServiceName {} @@ -4290,15 +4289,6 @@ public abstract class Context { /** * Use with {@link #getSystemService(String)} to retrieve an - * {@link android.app.timezonedetector.ITimeZoneDetectorService}. - * @hide - * - * @see #getSystemService(String) - */ - public static final String TIME_ZONE_DETECTOR_SERVICE = "time_zone_detector"; - - /** - * Use with {@link #getSystemService(String)} to retrieve an * {@link android.telephony.ims.RcsManager}. * @hide */ diff --git a/core/java/android/content/DynamicAndroidClient.java b/core/java/android/content/DynamicAndroidClient.java new file mode 100644 index 000000000000..571cba429ea9 --- /dev/null +++ b/core/java/android/content/DynamicAndroidClient.java @@ -0,0 +1,370 @@ +/* + * 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; + +import android.annotation.CallbackExecutor; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Slog; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.ref.WeakReference; +import java.util.concurrent.Executor; + +/** + * This class contains methods and constants used to start DynamicAndroid + * installation, and a listener for progress update. + * @hide + */ +@SystemApi +public class DynamicAndroidClient { + /** @hide */ + @IntDef(prefix = { "STATUS_" }, value = { + STATUS_UNKNOWN, + STATUS_NOT_STARTED, + STATUS_IN_PROGRESS, + STATUS_READY, + STATUS_IN_USE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface InstallationStatus {} + + /** @hide */ + @IntDef(prefix = { "CAUSE_" }, value = { + CAUSE_NOT_SPECIFIED, + CAUSE_INSTALL_COMPLETED, + CAUSE_INSTALL_CANCELLED, + CAUSE_ERROR_IO, + CAUSE_ERROR_INVALID_URL, + CAUSE_ERROR_IPC, + CAUSE_ERROR_EXCEPTION, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface StatusChangedCause {} + + private static final String TAG = "DynAndroidClient"; + + private static final long DEFAULT_USERDATA_SIZE = (10L << 30); + + + /** Listener for installation status update. */ + public interface OnStatusChangedListener { + /** + * This callback is called when installation status is changed, and when the + * client is {@link #bind} to DynamicAndroid installation service. + * + * @param status status code, also defined in {@code DynamicAndroidClient}. + * @param cause cause code, also defined in {@code DynamicAndroidClient}. + * @param progress number of bytes installed. + */ + void onStatusChanged(@InstallationStatus int status, @StatusChangedCause int cause, + long progress); + } + + /* + * Status codes + */ + /** We are bound to installation service, but failed to get its status */ + public static final int STATUS_UNKNOWN = 0; + + /** Installation is not started yet. */ + public static final int STATUS_NOT_STARTED = 1; + + /** Installation is in progress. */ + public static final int STATUS_IN_PROGRESS = 2; + + /** Installation is finished but the user has not launched it. */ + public static final int STATUS_READY = 3; + + /** Device is running in Dynamic Android. */ + public static final int STATUS_IN_USE = 4; + + /* + * Causes + */ + /** Cause is not specified. This means the status is not changed. */ + public static final int CAUSE_NOT_SPECIFIED = 0; + + /** Status changed because installation is completed. */ + public static final int CAUSE_INSTALL_COMPLETED = 1; + + /** Status changed because installation is cancelled. */ + public static final int CAUSE_INSTALL_CANCELLED = 2; + + /** Installation failed due to IOException. */ + public static final int CAUSE_ERROR_IO = 3; + + /** Installation failed because the image URL source is not supported. */ + public static final int CAUSE_ERROR_INVALID_URL = 4; + + /** Installation failed due to IPC error. */ + public static final int CAUSE_ERROR_IPC = 5; + + /** Installation failed due to unhandled exception. */ + public static final int CAUSE_ERROR_EXCEPTION = 6; + + /* + * IPC Messages + */ + /** + * Message to register listener. + * @hide + */ + public static final int MSG_REGISTER_LISTENER = 1; + + /** + * Message to unregister listener. + * @hide + */ + public static final int MSG_UNREGISTER_LISTENER = 2; + + /** + * Message for status update. + * @hide + */ + public static final int MSG_POST_STATUS = 3; + + /* + * Messages keys + */ + /** + * Message key, for progress update. + * @hide + */ + public static final String KEY_INSTALLED_SIZE = "KEY_INSTALLED_SIZE"; + + /* + * Intent Actions + */ + /** + * Intent action: start installation. + * @hide + */ + public static final String ACTION_START_INSTALL = + "android.content.action.START_INSTALL"; + + /** + * Intent action: notify user if we are currently running in Dynamic Android. + * @hide + */ + public static final String ACTION_NOTIFY_IF_IN_USE = + "android.content.action.NOTIFY_IF_IN_USE"; + + /* + * Intent Keys + */ + /** + * Intent key: URL to system image. + * @hide + */ + public static final String KEY_SYSTEM_URL = "KEY_SYSTEM_URL"; + + /** + * Intent key: Size of system image, in bytes. + * @hide + */ + public static final String KEY_SYSTEM_SIZE = "KEY_SYSTEM_SIZE"; + + /** + * Intent key: Number of bytes to reserve for userdata. + * @hide + */ + public static final String KEY_USERDATA_SIZE = "KEY_USERDATA_SIZE"; + + + private static class IncomingHandler extends Handler { + private final WeakReference<DynamicAndroidClient> mWeakClient; + + IncomingHandler(DynamicAndroidClient service) { + super(Looper.getMainLooper()); + mWeakClient = new WeakReference<>(service); + } + + @Override + public void handleMessage(Message msg) { + DynamicAndroidClient service = mWeakClient.get(); + + if (service != null) { + service.handleMessage(msg); + } + } + } + + private class DynAndroidServiceConnection implements ServiceConnection { + public void onServiceConnected(ComponentName className, IBinder service) { + Slog.v(TAG, "DynAndroidService connected"); + + mService = new Messenger(service); + + try { + Message msg = Message.obtain(null, MSG_REGISTER_LISTENER); + msg.replyTo = mMessenger; + + mService.send(msg); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to get status from installation service"); + mExecutor.execute(() -> { + mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0); + }); + } + } + + public void onServiceDisconnected(ComponentName className) { + Slog.v(TAG, "DynAndroidService disconnected"); + mService = null; + } + } + + private final Context mContext; + private final DynAndroidServiceConnection mConnection; + private final Messenger mMessenger; + + private boolean mBound; + private Executor mExecutor; + private OnStatusChangedListener mListener; + private Messenger mService; + + /** + * @hide + */ + @SystemApi + public DynamicAndroidClient(@NonNull Context context) { + mContext = context; + mConnection = new DynAndroidServiceConnection(); + mMessenger = new Messenger(new IncomingHandler(this)); + } + + /** + * This method register a listener for status change. The listener is called using + * the executor. + */ + public void setOnStatusChangedListener( + @NonNull OnStatusChangedListener listener, + @NonNull @CallbackExecutor Executor executor) { + mListener = listener; + mExecutor = executor; + } + + /** + * This method register a listener for status change. The listener is called in main + * thread. + */ + public void setOnStatusChangedListener( + @NonNull OnStatusChangedListener listener) { + mListener = listener; + mExecutor = null; + } + + /** + * Bind to DynamicAndroidInstallationService. + */ + public void bind() { + Intent intent = new Intent(); + intent.setClassName("com.android.dynandroid", + "com.android.dynandroid.DynamicAndroidInstallationService"); + + mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); + + mBound = true; + } + + /** + * Unbind from DynamicAndroidInstallationService. + */ + public void unbind() { + if (!mBound) { + return; + } + + if (mService != null) { + try { + Message msg = Message.obtain(null, MSG_UNREGISTER_LISTENER); + msg.replyTo = mMessenger; + mService.send(msg); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to unregister from installation service"); + } + } + + // Detach our existing connection. + mContext.unbindService(mConnection); + + mBound = false; + } + + /** + * Start installing DynamicAndroid from URL with default userdata size. + * + * @param systemUrl A network URL or a file URL to system image. + * @param systemSize size of system image. + */ + public void start(String systemUrl, long systemSize) { + start(systemUrl, systemSize, DEFAULT_USERDATA_SIZE); + } + + /** + * Start installing DynamicAndroid from URL. + * + * @param systemUrl A network URL or a file URL to system image. + * @param systemSize size of system image. + * @param userdataSize bytes reserved for userdata. + */ + public void start(String systemUrl, long systemSize, long userdataSize) { + Intent intent = new Intent(); + + intent.setClassName("com.android.dynandroid", + "com.android.dynandroid.VerificationActivity"); + + intent.setAction(ACTION_START_INSTALL); + + intent.putExtra(KEY_SYSTEM_URL, systemUrl); + intent.putExtra(KEY_SYSTEM_SIZE, systemSize); + intent.putExtra(KEY_USERDATA_SIZE, userdataSize); + + mContext.startActivity(intent); + } + + private void handleMessage(Message msg) { + switch (msg.what) { + case MSG_POST_STATUS: + int status = msg.arg1; + int cause = msg.arg2; + // obj is non-null + long progress = ((Bundle) msg.obj).getLong(KEY_INSTALLED_SIZE); + + if (mExecutor != null) { + mExecutor.execute(() -> { + mListener.onStatusChanged(status, cause, progress); + }); + } else { + mListener.onStatusChanged(status, cause, progress); + } + break; + default: + // do nothing + + } + } +} diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 2ea23ec6b5c8..68ac46c874ff 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -68,6 +68,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.Socket; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -177,10 +178,10 @@ public class ConnectivityManager { * The lookup key for a {@link NetworkInfo} object. Retrieve with * {@link android.content.Intent#getParcelableExtra(String)}. * - * @deprecated Since {@link NetworkInfo} can vary based on UID, applications - * should always obtain network information through - * {@link #getActiveNetworkInfo()}. - * @see #EXTRA_NETWORK_TYPE + * @deprecated The {@link NetworkInfo} object is deprecated, as many of its properties + * can't accurately represent modern network characteristics. + * Please obtain information about networks from the {@link NetworkCapabilities} + * or {@link LinkProperties} objects instead. */ @Deprecated public static final String EXTRA_NETWORK_INFO = "networkInfo"; @@ -189,7 +190,11 @@ public class ConnectivityManager { * Network type which triggered a {@link #CONNECTIVITY_ACTION} broadcast. * * @see android.content.Intent#getIntExtra(String, int) + * @deprecated The network type is not rich enough to represent the characteristics + * of modern networks. Please use {@link NetworkCapabilities} instead, + * in particular the transports. */ + @Deprecated public static final String EXTRA_NETWORK_TYPE = "networkType"; /** @@ -1243,9 +1248,13 @@ public class ConnectivityManager { * is no current default network. * * {@hide} + * @deprecated please use {@link #getLinkProperties(Network)} on the return + * value of {@link #getActiveNetwork()} instead. In particular, + * this method will return non-null LinkProperties even if the + * app is blocked by policy from using this network. */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 109783091) public LinkProperties getActiveLinkProperties() { try { return mService.getActiveLinkProperties(); @@ -1881,7 +1890,8 @@ public class ConnectivityManager { * @param callback A {@link SocketKeepalive.Callback}. Used for notifications about keepalive * changes. Must be extended by applications that use this API. * - * @return A {@link SocketKeepalive} object, which can be used to control this keepalive object. + * @return A {@link SocketKeepalive} object that can be used to control the keepalive on the + * given socket. **/ public SocketKeepalive createSocketKeepalive(@NonNull Network network, @NonNull UdpEncapsulationSocket socket, @@ -1910,6 +1920,8 @@ public class ConnectivityManager { * @param callback A {@link SocketKeepalive.Callback}. Used for notifications about keepalive * changes. Must be extended by applications that use this API. * + * @return A {@link SocketKeepalive} object that can be used to control the keepalive on the + * given socket. * @hide */ @SystemApi @@ -1925,6 +1937,34 @@ public class ConnectivityManager { } /** + * Request that keepalives be started on a TCP socket. + * The socket must be established. + * + * @param network The {@link Network} the socket is on. + * @param socket The socket that needs to be kept alive. + * @param executor The executor on which callback will be invoked. This implementation assumes + * the provided {@link Executor} runs the callbacks in sequence with no + * concurrency. Failing this, no guarantee of correctness can be made. It is + * the responsibility of the caller to ensure the executor provides this + * guarantee. A simple way of creating such an executor is with the standard + * tool {@code Executors.newSingleThreadExecutor}. + * @param callback A {@link SocketKeepalive.Callback}. Used for notifications about keepalive + * changes. Must be extended by applications that use this API. + * + * @return A {@link SocketKeepalive} object that can be used to control the keepalive on the + * given socket. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) + public SocketKeepalive createSocketKeepalive(@NonNull Network network, + @NonNull Socket socket, + @NonNull Executor executor, + @NonNull Callback callback) { + return new TcpSocketKeepalive(mService, network, socket, executor, callback); + } + + /** * Ensure that a network route exists to deliver traffic to the specified * host via the specified network interface. An attempt to add a route that * already exists is ignored, but treated as successful. @@ -3867,6 +3907,25 @@ public class ConnectivityManager { } /** + * Requests that the system open the captive portal app with the specified extras. + * + * <p>This endpoint is exclusively for use by the NetworkStack and is protected by the + * corresponding permission. + * @param appExtras Extras to include in the app start intent. + * @hide + */ + @SystemApi + @TestApi + @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) + public void startCaptivePortalApp(Bundle appExtras) { + try { + mService.startCaptivePortalAppInternal(appExtras); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Determine whether the device is configured to avoid bad wifi. * @hide */ diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 78fafebc4f37..92a5839ac2af 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -27,6 +27,7 @@ import android.net.NetworkQuotaInfo; import android.net.NetworkRequest; import android.net.NetworkState; import android.net.ProxyInfo; +import android.os.Bundle; import android.os.IBinder; import android.os.Messenger; import android.os.ParcelFileDescriptor; @@ -167,6 +168,7 @@ interface IConnectivityManager void setAcceptUnvalidated(in Network network, boolean accept, boolean always); void setAvoidUnvalidated(in Network network); void startCaptivePortalApp(in Network network); + void startCaptivePortalAppInternal(in Bundle appExtras); boolean getAvoidBadWifi(); int getMultipathPreference(in Network Network); @@ -188,6 +190,9 @@ interface IConnectivityManager int intervalSeconds, in Messenger messenger, in IBinder binder, String srcAddr, String dstAddr); + void startTcpKeepalive(in Network network, in FileDescriptor fd, int intervalSeconds, + in Messenger messenger, in IBinder binder); + void stopKeepalive(in Network network, int slot); String getCaptivePortalServerUrl(); diff --git a/core/java/android/net/INetworkMonitorCallbacks.aidl b/core/java/android/net/INetworkMonitorCallbacks.aidl index a8682f9ddd3b..514658549651 100644 --- a/core/java/android/net/INetworkMonitorCallbacks.aidl +++ b/core/java/android/net/INetworkMonitorCallbacks.aidl @@ -24,7 +24,7 @@ oneway interface INetworkMonitorCallbacks { void onNetworkMonitorCreated(in INetworkMonitor networkMonitor); void notifyNetworkTested(int testResult, @nullable String redirectUrl); void notifyPrivateDnsConfigResolved(in PrivateDnsConfigParcel config); - void showProvisioningNotification(String action); + void showProvisioningNotification(String action, String packageName); void hideProvisioningNotification(); void logCaptivePortalLoginEvent(int eventId, String packageName); }
\ No newline at end of file diff --git a/core/java/android/net/KeepalivePacketData.java b/core/java/android/net/KeepalivePacketData.java index 16555d8bd488..18726f748d30 100644 --- a/core/java/android/net/KeepalivePacketData.java +++ b/core/java/android/net/KeepalivePacketData.java @@ -96,7 +96,7 @@ public class KeepalivePacketData implements Parcelable { out.writeByteArray(mPacket); } - private KeepalivePacketData(Parcel in) { + protected KeepalivePacketData(Parcel in) { srcAddress = NetworkUtils.numericToInetAddress(in.readString()); dstAddress = NetworkUtils.numericToInetAddress(in.readString()); srcPort = in.readInt(); diff --git a/core/java/android/net/NattKeepalivePacketData.java b/core/java/android/net/NattKeepalivePacketData.java index dd2b108cdbfd..bdb246f516a2 100644 --- a/core/java/android/net/NattKeepalivePacketData.java +++ b/core/java/android/net/NattKeepalivePacketData.java @@ -16,6 +16,9 @@ package android.net; +import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS; +import static android.net.SocketKeepalive.ERROR_INVALID_PORT; + import android.net.SocketKeepalive.InvalidPacketException; import android.net.util.IpUtils; import android.system.OsConstants; @@ -44,11 +47,11 @@ public final class NattKeepalivePacketData extends KeepalivePacketData { throws InvalidPacketException { if (!(srcAddress instanceof Inet4Address) || !(dstAddress instanceof Inet4Address)) { - throw new InvalidPacketException(SocketKeepalive.ERROR_INVALID_IP_ADDRESS); + throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS); } if (dstPort != NattSocketKeepalive.NATT_PORT) { - throw new InvalidPacketException(SocketKeepalive.ERROR_INVALID_PORT); + throw new InvalidPacketException(ERROR_INVALID_PORT); } int length = IPV4_HEADER_LENGTH + UDP_HEADER_LENGTH + 1; diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java index dfb6d6fd6428..7bef69012bf7 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -177,6 +177,26 @@ public abstract class NetworkAgent extends Handler { */ public static final int EVENT_SOCKET_KEEPALIVE = BASE + 13; + // TODO: move the above 2 constants down so they are in order once merge conflicts are resolved + /** + * Sent by the KeepaliveTracker to NetworkAgent to add a packet filter. + * + * For TCP keepalive offloads, keepalive packets are sent by the firmware. However, because the + * remote site will send ACK packets in response to the keepalive packets, the firmware also + * needs to be configured to properly filter the ACKs to prevent the system from waking up. + * This does not happen with UDP, so this message is TCP-specific. + * arg1 = slot number of the keepalive to filter for. + * obj = the keepalive packet to send repeatedly. + */ + public static final int CMD_ADD_KEEPALIVE_PACKET_FILTER = BASE + 16; + + /** + * Sent by the KeepaliveTracker to NetworkAgent to remove a packet filter. See + * {@link #CMD_ADD_KEEPALIVE_PACKET_FILTER}. + * arg1 = slot number of the keepalive packet filter to remove. + */ + public static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER = BASE + 17; + /** * Sent by ConnectivityService to inform this network transport of signal strength thresholds * that when crossed should trigger a system wakeup and a NetworkCapabilities update. @@ -312,6 +332,14 @@ public abstract class NetworkAgent extends Handler { preventAutomaticReconnect(); break; } + case CMD_ADD_KEEPALIVE_PACKET_FILTER: { + addKeepalivePacketFilter(msg); + break; + } + case CMD_REMOVE_KEEPALIVE_PACKET_FILTER: { + removeKeepalivePacketFilter(msg); + break; + } } } @@ -461,6 +489,24 @@ public abstract class NetworkAgent extends Handler { } /** + * Called by ConnectivityService to add specific packet filter to network hardware to block + * ACKs matching the sent keepalive packets. Implementations that support this feature must + * override this method. + */ + protected void addKeepalivePacketFilter(Message msg) { + onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED); + } + + /** + * Called by ConnectivityService to remove a packet filter installed with + * {@link #addKeepalivePacketFilter(Message)}. Implementations that support this feature + * must override this method. + */ + protected void removeKeepalivePacketFilter(Message msg) { + onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED); + } + + /** * Called by ConnectivityService to inform this network transport of signal strength thresholds * that when crossed should trigger a system wakeup and a NetworkCapabilities update. */ diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java index b6cd6359384a..ca49438390e9 100644 --- a/core/java/android/net/NetworkStack.java +++ b/core/java/android/net/NetworkStack.java @@ -15,6 +15,7 @@ */ package android.net; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; @@ -27,6 +28,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.PackageManager; import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.IDhcpServerCallbacks; import android.net.ip.IIpClientCallbacks; @@ -63,9 +65,6 @@ public class NetworkStack { public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK"; - /** @hide */ - public static final String NETWORKSTACK_PACKAGE_NAME = "com.android.mainline.networkstack"; - private static final int NETWORKSTACK_TIMEOUT_MS = 10_000; @NonNull @@ -204,7 +203,33 @@ public class NetworkStack { final ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0); intent.setComponent(comp); - if (comp == null || !context.bindServiceAsUser(intent, new NetworkStackConnection(), + if (comp == null) { + Slog.wtf(TAG, "Could not resolve the network stack with " + intent); + // TODO: crash/reboot system server ? + return; + } + + final PackageManager pm = context.getPackageManager(); + int uid = -1; + try { + uid = pm.getPackageUid(comp.getPackageName(), UserHandle.USER_SYSTEM); + } catch (PackageManager.NameNotFoundException e) { + Slog.wtf("Network stack package not found", e); + // Fall through + } + + if (uid != Process.NETWORK_STACK_UID) { + throw new SecurityException("Invalid network stack UID: " + uid); + } + + final int hasPermission = + pm.checkPermission(PERMISSION_MAINLINE_NETWORK_STACK, comp.getPackageName()); + if (hasPermission != PERMISSION_GRANTED) { + throw new SecurityException( + "Network stack does not have permission " + PERMISSION_MAINLINE_NETWORK_STACK); + } + + if (!context.bindServiceAsUser(intent, new NetworkStackConnection(), Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) { Slog.wtf(TAG, "Could not bind to network stack in-process, or in app with " + intent); diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java index 07668a9e3a82..0ae29b125149 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/core/java/android/net/NetworkUtils.java @@ -71,6 +71,18 @@ public class NetworkUtils { throws SocketException; /** + * Attaches a socket filter that drops all of incoming packets. + * @param fd the socket's {@link FileDescriptor}. + */ + public static native void attachDropAllBPFFilter(FileDescriptor fd) throws SocketException; + + /** + * Detaches a socket filter. + * @param fd the socket's {@link FileDescriptor}. + */ + public static native void detachBPFFilter(FileDescriptor fd) throws SocketException; + + /** * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements. * @param fd the socket's {@link FileDescriptor}. * @param ifIndex the interface index. @@ -170,6 +182,16 @@ public class NetworkUtils { private static native void addArpEntry(byte[] ethAddr, byte[] netAddr, String ifname, FileDescriptor fd) throws IOException; + + /** + * Get the tcp repair window associated with the {@code fd}. + * + * @param fd the tcp socket's {@link FileDescriptor}. + * @return a {@link TcpRepairWindow} object indicates tcp window size. + */ + public static native TcpRepairWindow getTcpRepairWindow(FileDescriptor fd) + throws ErrnoException; + /** * @see Inet4AddressUtils#intToInet4AddressHTL(int) * @deprecated Use either {@link Inet4AddressUtils#intToInet4AddressHTH(int)} diff --git a/core/java/android/net/SocketKeepalive.java b/core/java/android/net/SocketKeepalive.java index a47c11af40dc..07728beb9c64 100644 --- a/core/java/android/net/SocketKeepalive.java +++ b/core/java/android/net/SocketKeepalive.java @@ -19,6 +19,7 @@ package android.net; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; @@ -110,15 +111,43 @@ public abstract class SocketKeepalive implements AutoCloseable { public static final int MAX_INTERVAL_SEC = 3600; /** - * This packet is invalid. - * See the error code for details. + * An exception that embarks an error code. * @hide */ - public static class InvalidPacketException extends Exception { + public static class ErrorCodeException extends Exception { public final int error; - public InvalidPacketException(int error) { + public ErrorCodeException(final int error, final Throwable e) { + super(e); this.error = error; } + public ErrorCodeException(final int error) { + this.error = error; + } + } + + /** + * This socket is invalid. + * See the error code for details, and the optional cause. + * @hide + */ + public static class InvalidSocketException extends ErrorCodeException { + public InvalidSocketException(final int error, final Throwable e) { + super(error, e); + } + public InvalidSocketException(final int error) { + super(error); + } + } + + /** + * This packet is invalid. + * See the error code for details. + * @hide + */ + public static class InvalidPacketException extends ErrorCodeException { + public InvalidPacketException(final int error) { + super(error); + } } @NonNull final IConnectivityManager mService; @@ -127,7 +156,7 @@ public abstract class SocketKeepalive implements AutoCloseable { @NonNull private final SocketKeepalive.Callback mCallback; @NonNull private final Looper mLooper; @NonNull final Messenger mMessenger; - @NonNull Integer mSlot; + @Nullable Integer mSlot; SocketKeepalive(@NonNull IConnectivityManager service, @NonNull Network network, @NonNull Executor executor, @NonNull Callback callback) { diff --git a/core/java/android/net/TcpKeepalivePacketData.java b/core/java/android/net/TcpKeepalivePacketData.java new file mode 100644 index 000000000000..f07dfb64cd01 --- /dev/null +++ b/core/java/android/net/TcpKeepalivePacketData.java @@ -0,0 +1,234 @@ +/* + * 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.net; + +import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.SocketKeepalive.InvalidPacketException; +import android.net.util.IpUtils; +import android.os.Parcel; +import android.os.Parcelable; +import android.system.OsConstants; + +import java.net.Inet4Address; +import java.net.InetAddress; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Objects; + +/** + * Represents the actual tcp keep alive packets which will be used for hardware offload. + * @hide + */ +public class TcpKeepalivePacketData extends KeepalivePacketData implements Parcelable { + private static final String TAG = "TcpKeepalivePacketData"; + + /** TCP sequence number. */ + public final int tcpSeq; + + /** TCP ACK number. */ + public final int tcpAck; + + /** TCP RCV window. */ + public final int tcpWnd; + + /** TCP RCV window scale. */ + public final int tcpWndScale; + + private static final int IPV4_HEADER_LENGTH = 20; + private static final int IPV6_HEADER_LENGTH = 40; + private static final int TCP_HEADER_LENGTH = 20; + + // This should only be constructed via static factory methods, such as + // tcpKeepalivePacket. + private TcpKeepalivePacketData(TcpSocketInfo tcpDetails, byte[] data) + throws InvalidPacketException { + super(tcpDetails.srcAddress, tcpDetails.srcPort, tcpDetails.dstAddress, + tcpDetails.dstPort, data); + tcpSeq = tcpDetails.seq; + tcpAck = tcpDetails.ack; + // In the packet, the window is shifted right by the window scale. + tcpWnd = tcpDetails.rcvWnd; + tcpWndScale = tcpDetails.rcvWndScale; + } + + /** + * Factory method to create tcp keepalive packet structure. + */ + public static TcpKeepalivePacketData tcpKeepalivePacket( + TcpSocketInfo tcpDetails) throws InvalidPacketException { + final byte[] packet; + if ((tcpDetails.srcAddress instanceof Inet4Address) + && (tcpDetails.dstAddress instanceof Inet4Address)) { + packet = buildV4Packet(tcpDetails); + } else { + // TODO: support ipv6 + throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS); + } + + return new TcpKeepalivePacketData(tcpDetails, packet); + } + + /** + * Build ipv4 tcp keepalive packet, not including the link-layer header. + */ + // TODO : if this code is ever moved to the network stack, factorize constants with the ones + // over there. + private static byte[] buildV4Packet(TcpSocketInfo tcpDetails) { + final int length = IPV4_HEADER_LENGTH + TCP_HEADER_LENGTH; + ByteBuffer buf = ByteBuffer.allocate(length); + buf.order(ByteOrder.BIG_ENDIAN); + // IP version and TOS. TODO : fetch this from getsockopt(SOL_IP, IP_TOS) + buf.putShort((short) 0x4500); + buf.putShort((short) length); + buf.putInt(0x4000); // ID, flags=DF, offset + // TODO : fetch TTL from getsockopt(SOL_IP, IP_TTL) + buf.put((byte) 64); + buf.put((byte) OsConstants.IPPROTO_TCP); + final int ipChecksumOffset = buf.position(); + buf.putShort((short) 0); // IP checksum + buf.put(tcpDetails.srcAddress.getAddress()); + buf.put(tcpDetails.dstAddress.getAddress()); + buf.putShort((short) tcpDetails.srcPort); + buf.putShort((short) tcpDetails.dstPort); + buf.putInt(tcpDetails.seq); // Sequence Number + buf.putInt(tcpDetails.ack); // ACK + buf.putShort((short) 0x5010); // TCP length=5, flags=ACK + buf.putShort((short) (tcpDetails.rcvWnd >> tcpDetails.rcvWndScale)); // Window size + final int tcpChecksumOffset = buf.position(); + buf.putShort((short) 0); // TCP checksum + // URG is not set therefore the urgent pointer is not included + buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0)); + buf.putShort(tcpChecksumOffset, IpUtils.tcpChecksum( + buf, 0, IPV4_HEADER_LENGTH, TCP_HEADER_LENGTH)); + + return buf.array(); + } + + // TODO: add buildV6Packet. + + /** Represents tcp/ip information. */ + // TODO: Replace TcpSocketInfo with TcpKeepalivePacketDataParcelable. + public static class TcpSocketInfo { + public final InetAddress srcAddress; + public final InetAddress dstAddress; + public final int srcPort; + public final int dstPort; + public final int seq; + public final int ack; + public final int rcvWnd; + public final int rcvWndScale; + + public TcpSocketInfo(InetAddress sAddr, int sPort, InetAddress dAddr, + int dPort, int writeSeq, int readSeq, int rWnd, int rWndScale) { + srcAddress = sAddr; + dstAddress = dAddr; + srcPort = sPort; + dstPort = dPort; + seq = writeSeq; + ack = readSeq; + rcvWnd = rWnd; + rcvWndScale = rWndScale; + } + } + + @Override + public boolean equals(@Nullable final Object o) { + if (!(o instanceof TcpKeepalivePacketData)) return false; + final TcpKeepalivePacketData other = (TcpKeepalivePacketData) o; + return this.srcAddress.equals(other.srcAddress) + && this.dstAddress.equals(other.dstAddress) + && this.srcPort == other.srcPort + && this.dstPort == other.dstPort + && this.tcpAck == other.tcpAck + && this.tcpSeq == other.tcpSeq + && this.tcpWnd == other.tcpWnd + && this.tcpWndScale == other.tcpWndScale; + } + + @Override + public int hashCode() { + return Objects.hash(srcAddress, dstAddress, srcPort, dstPort, tcpAck, tcpSeq, tcpWnd, + tcpWndScale); + } + + /* Parcelable Implementation. */ + /* Note that this object implements parcelable (and needs to keep doing this as it inherits + * from a class that does), but should usually be parceled as a stable parcelable using + * the toStableParcelable() and fromStableParcelable() methods. + */ + public int describeContents() { + return 0; + } + + /** Write to parcel. */ + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + out.writeInt(tcpSeq); + out.writeInt(tcpAck); + out.writeInt(tcpWnd); + out.writeInt(tcpWndScale); + } + + private TcpKeepalivePacketData(Parcel in) { + super(in); + tcpSeq = in.readInt(); + tcpAck = in.readInt(); + tcpWnd = in.readInt(); + tcpWndScale = in.readInt(); + } + + /** Parcelable Creator. */ + public static final Parcelable.Creator<TcpKeepalivePacketData> CREATOR = + new Parcelable.Creator<TcpKeepalivePacketData>() { + public TcpKeepalivePacketData createFromParcel(Parcel in) { + return new TcpKeepalivePacketData(in); + } + + public TcpKeepalivePacketData[] newArray(int size) { + return new TcpKeepalivePacketData[size]; + } + }; + + /** + * Convert this TcpKeepalivePacketData to a TcpKeepalivePacketDataParcelable. + */ + @NonNull + public TcpKeepalivePacketDataParcelable toStableParcelable() { + final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable(); + parcel.srcAddress = srcAddress.getAddress(); + parcel.srcPort = srcPort; + parcel.dstAddress = dstAddress.getAddress(); + parcel.dstPort = dstPort; + parcel.seq = tcpSeq; + parcel.ack = tcpAck; + return parcel; + } + + @Override + public String toString() { + return "saddr: " + srcAddress + + " daddr: " + dstAddress + + " sport: " + srcPort + + " dport: " + dstPort + + " seq: " + tcpSeq + + " ack: " + tcpAck + + " wnd: " + tcpWnd + + " wndScale: " + tcpWndScale; + } +} diff --git a/telephony/java/android/telephony/ims/RcsPart.java b/core/java/android/net/TcpKeepalivePacketDataParcelable.aidl index da501738a0bf..7329c63b09be 100644 --- a/telephony/java/android/telephony/ims/RcsPart.java +++ b/core/java/android/net/TcpKeepalivePacketDataParcelable.aidl @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.telephony.ims; -import android.os.Parcelable; +package android.net; -/** - * A part of a composite {@link RcsMessage}. - * @hide - TODO(sahinc) make this public - */ -public abstract class RcsPart implements Parcelable { +parcelable TcpKeepalivePacketDataParcelable { + byte[] srcAddress; + int srcPort; + byte[] dstAddress; + int dstPort; + int seq; + int ack; } diff --git a/core/java/android/net/TcpRepairWindow.java b/core/java/android/net/TcpRepairWindow.java new file mode 100644 index 000000000000..86034f0a76ed --- /dev/null +++ b/core/java/android/net/TcpRepairWindow.java @@ -0,0 +1,45 @@ +/* + * 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.net; + +/** + * Corresponds to C's {@code struct tcp_repair_window} from + * include/uapi/linux/tcp.h + * + * @hide + */ +public final class TcpRepairWindow { + public final int sndWl1; + public final int sndWnd; + public final int maxWindow; + public final int rcvWnd; + public final int rcvWup; + public final int rcvWndScale; + + /** + * Constructs an instance with the given field values. + */ + public TcpRepairWindow(final int sndWl1, final int sndWnd, final int maxWindow, + final int rcvWnd, final int rcvWup, final int rcvWndScale) { + this.sndWl1 = sndWl1; + this.sndWnd = sndWnd; + this.maxWindow = maxWindow; + this.rcvWnd = rcvWnd; + this.rcvWup = rcvWup; + this.rcvWndScale = rcvWndScale; + } +} diff --git a/core/java/android/net/TcpSocketKeepalive.java b/core/java/android/net/TcpSocketKeepalive.java new file mode 100644 index 000000000000..8f6ee7bf2950 --- /dev/null +++ b/core/java/android/net/TcpSocketKeepalive.java @@ -0,0 +1,78 @@ +/* + * 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.net; + +import android.annotation.NonNull; +import android.os.Binder; +import android.os.RemoteException; +import android.util.Log; + +import java.io.FileDescriptor; +import java.net.Socket; +import java.util.concurrent.Executor; + +/** @hide */ +final class TcpSocketKeepalive extends SocketKeepalive { + + private final Socket mSocket; + + TcpSocketKeepalive(@NonNull IConnectivityManager service, + @NonNull Network network, + @NonNull Socket socket, + @NonNull Executor executor, + @NonNull Callback callback) { + super(service, network, executor, callback); + mSocket = socket; + } + + /** + * Starts keepalives. {@code mSocket} must be a connected TCP socket. + * + * - The application must not write to or read from the socket after calling this method, until + * onDataReceived, onStopped, or onError are called. If it does, the keepalive will fail + * with {@link #ERROR_SOCKET_NOT_IDLE}, or {@code #ERROR_INVALID_SOCKET} if the socket + * experienced an error (as in poll(2) returned POLLERR); if this happens, the data received + * from the socket may be invalid, and the socket can't be recovered. + * - If the socket has data in the send or receive buffer, then this call will fail with + * {@link #ERROR_SOCKET_NOT_IDLE} and can be retried after the data has been processed. + * An app could ensure this by using an application-layer protocol where it can receive + * acknowledgement that it will go into keepalive mode. It could then go into keepalive + * mode after having read the acknowledgement, draining the socket. + */ + @Override + void startImpl(int intervalSec) { + try { + final FileDescriptor fd = mSocket.getFileDescriptor$(); + mService.startTcpKeepalive(mNetwork, fd, intervalSec, mMessenger, new Binder()); + } catch (RemoteException e) { + Log.e(TAG, "Error starting packet keepalive: ", e); + stopLooper(); + } + } + + @Override + void stopImpl() { + try { + if (mSlot != null) { + mService.stopKeepalive(mNetwork, mSlot); + } + } catch (RemoteException e) { + Log.e(TAG, "Error stopping packet keepalive: ", e); + stopLooper(); + } + } +} diff --git a/core/java/android/net/ip/IIpClient.aidl b/core/java/android/net/ip/IIpClient.aidl index 7769ec2b65ac..a4a80e1efe6f 100644 --- a/core/java/android/net/ip/IIpClient.aidl +++ b/core/java/android/net/ip/IIpClient.aidl @@ -17,6 +17,7 @@ package android.net.ip; import android.net.ProxyInfoParcelable; import android.net.ProvisioningConfigurationParcelable; +import android.net.TcpKeepalivePacketDataParcelable; /** @hide */ oneway interface IIpClient { @@ -29,4 +30,6 @@ oneway interface IIpClient { void setTcpBufferSizes(in String tcpBufferSizes); void setHttpProxy(in ProxyInfoParcelable proxyInfo); void setMulticastFilter(boolean enabled); + void addKeepalivePacketFilter(int slot, in TcpKeepalivePacketDataParcelable pkt); + void removeKeepalivePacketFilter(int slot); } diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java index 1f3369376b10..a851e04e78ec 100644 --- a/core/java/android/os/AsyncTask.java +++ b/core/java/android/os/AsyncTask.java @@ -21,13 +21,14 @@ import android.annotation.Nullable; import android.annotation.WorkerThread; import java.util.ArrayDeque; -import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.FutureTask; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -190,13 +191,19 @@ import java.util.concurrent.atomic.AtomicInteger; public abstract class AsyncTask<Params, Progress, Result> { private static final String LOG_TAG = "AsyncTask"; - private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); - // We want at least 2 threads and at most 4 threads in the core pool, - // preferring to have 1 less than the CPU count to avoid saturating - // the CPU with background work - private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)); - private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; - private static final int KEEP_ALIVE_SECONDS = 30; + // We keep only a single pool thread around all the time. + // We let the pool grow to a fairly large number of threads if necessary, + // but let them time out quickly. In the unlikely case that we run out of threads, + // we fall back to a simple unbounded-queue executor. + // This combination ensures that: + // 1. We normally keep few threads (1) around. + // 2. We queue only after launching a significantly larger, but still bounded, set of threads. + // 3. We keep the total number of threads bounded, but still allow an unbounded set + // of tasks to be queued. + private static final int CORE_POOL_SIZE = 1; + private static final int MAXIMUM_POOL_SIZE = 20; + private static final int BACKUP_POOL_SIZE = 5; + private static final int KEEP_ALIVE_SECONDS = 3; private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); @@ -206,8 +213,29 @@ public abstract class AsyncTask<Params, Progress, Result> { } }; - private static final BlockingQueue<Runnable> sPoolWorkQueue = - new LinkedBlockingQueue<Runnable>(128); + // Used only for rejected executions. + // Initialization protected by sRunOnSerialPolicy lock. + private static ThreadPoolExecutor sBackupExecutor; + private static LinkedBlockingQueue<Runnable> sBackupExecutorQueue; + + private static final RejectedExecutionHandler sRunOnSerialPolicy = + new RejectedExecutionHandler() { + public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { + android.util.Log.w(LOG_TAG, "Exceeded ThreadPoolExecutor pool size"); + // As a last ditch fallback, run it on an executor with an unbounded queue. + // Create this executor lazily, hopefully almost never. + synchronized (this) { + if (sBackupExecutor == null) { + sBackupExecutorQueue = new LinkedBlockingQueue<Runnable>(); + sBackupExecutor = new ThreadPoolExecutor( + BACKUP_POOL_SIZE, BACKUP_POOL_SIZE, KEEP_ALIVE_SECONDS, + TimeUnit.SECONDS, sBackupExecutorQueue, sThreadFactory); + sBackupExecutor.allowCoreThreadTimeOut(true); + } + } + sBackupExecutor.execute(r); + } + }; /** * An {@link Executor} that can be used to execute tasks in parallel. @@ -217,8 +245,8 @@ public abstract class AsyncTask<Params, Progress, Result> { static { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, - sPoolWorkQueue, sThreadFactory); - threadPoolExecutor.allowCoreThreadTimeOut(true); + new SynchronousQueue<Runnable>(), sThreadFactory); + threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy); THREAD_POOL_EXECUTOR = threadPoolExecutor; } diff --git a/core/java/android/os/ParcelFileDescriptor.aidl b/core/java/android/os/ParcelFileDescriptor.aidl deleted file mode 100644 index c07b98055d5a..000000000000 --- a/core/java/android/os/ParcelFileDescriptor.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* //device/java/android/android/os/ParcelFileDescriptor.aidl -** -** Copyright 2007, 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; - -parcelable ParcelFileDescriptor cpp_header "binder/ParcelFileDescriptor.h"; diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index 8492b0cf58e2..3ee54ceebaa9 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -32,6 +32,7 @@ import android.content.pm.PackageManager; import android.provider.Settings; import android.telephony.euicc.EuiccManager; import android.text.TextUtils; +import android.text.format.DateFormat; import android.util.Log; import android.view.Display; import android.view.WindowManager; @@ -762,7 +763,8 @@ public class RecoverySystem { String reasonArg = null; if (!TextUtils.isEmpty(reason)) { - reasonArg = "--reason=" + sanitizeArg(reason); + String timeStamp = DateFormat.format("yyyy-MM-ddTHH:mm:ssZ", System.currentTimeMillis()).toString(); + reasonArg = "--reason=" + sanitizeArg(reason + "," + timeStamp); } final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ; diff --git a/core/java/android/os/connectivity/CellularBatteryStats.java b/core/java/android/os/connectivity/CellularBatteryStats.java index 2593c85cff12..c99ecb32d418 100644 --- a/core/java/android/os/connectivity/CellularBatteryStats.java +++ b/core/java/android/os/connectivity/CellularBatteryStats.java @@ -44,6 +44,7 @@ public final class CellularBatteryStats implements Parcelable { private long[] mTimeInRatMs; private long[] mTimeInRxSignalStrengthLevelMs; private long[] mTxTimeMs; + private long mMonitoredRailChargeConsumedMaMs; public static final Parcelable.Creator<CellularBatteryStats> CREATOR = new Parcelable.Creator<CellularBatteryStats>() { @@ -74,6 +75,7 @@ public final class CellularBatteryStats implements Parcelable { out.writeLongArray(mTimeInRatMs); out.writeLongArray(mTimeInRxSignalStrengthLevelMs); out.writeLongArray(mTxTimeMs); + out.writeLong(mMonitoredRailChargeConsumedMaMs); } public void readFromParcel(Parcel in) { @@ -90,6 +92,7 @@ public final class CellularBatteryStats implements Parcelable { in.readLongArray(mTimeInRatMs); in.readLongArray(mTimeInRxSignalStrengthLevelMs); in.readLongArray(mTxTimeMs); + mMonitoredRailChargeConsumedMaMs = in.readLong(); } public long getLoggingDurationMs() { @@ -144,6 +147,10 @@ public final class CellularBatteryStats implements Parcelable { return mTxTimeMs; } + public long getMonitoredRailChargeConsumedMaMs() { + return mMonitoredRailChargeConsumedMaMs; + } + public void setLoggingDurationMs(long t) { mLoggingDurationMs = t; return; @@ -211,6 +218,11 @@ public final class CellularBatteryStats implements Parcelable { return; } + public void setMonitoredRailChargeConsumedMaMs(long monitoredRailEnergyConsumedMaMs) { + mMonitoredRailChargeConsumedMaMs = monitoredRailEnergyConsumedMaMs; + return; + } + public int describeContents() { return 0; } @@ -237,6 +249,7 @@ public final class CellularBatteryStats implements Parcelable { Arrays.fill(mTimeInRxSignalStrengthLevelMs, 0); mTxTimeMs = new long[ModemActivityInfo.TX_POWER_LEVELS]; Arrays.fill(mTxTimeMs, 0); + mMonitoredRailChargeConsumedMaMs = 0; return; } }
\ No newline at end of file diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index de54a8aa5548..f63c0adbdf4b 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -772,19 +772,6 @@ public class CallLog { * @param callBlockReason The reason why the call is blocked. * @param callScreeningAppName The call screening application name which block the call. * @param callScreeningComponentName The call screening component name which block the call. - * @param callIdPackageName The package name of the - * {@link android.telecom.CallScreeningService} which provided - * {@link CallIdentification}. - * @param callIdAppName The app name of the {@link android.telecom.CallScreeningService} - * which provided {@link CallIdentification}. - * @param callIdName The caller name provided by the - * {@link android.telecom.CallScreeningService}. - * @param callIdDescription The caller description provided by the - * {@link android.telecom.CallScreeningService}. - * @param callIdDetails The caller details provided by the - * {@link android.telecom.CallScreeningService}. - * @param callIdCallType The caller type provided by the - * {@link android.telecom.CallScreeningService}. * * @result The URI of the call log entry belonging to the user that made or received this * call. This could be of the shadow provider. Do not return it to non-system apps, @@ -803,37 +790,10 @@ public class CallLog { number, userToBeInsertedTo, addForAllUsers)); } final ContentResolver resolver = context.getContentResolver(); - int numberPresentation = PRESENTATION_ALLOWED; - TelecomManager tm = null; - try { - tm = TelecomManager.from(context); - } catch (UnsupportedOperationException e) {} - - String accountAddress = null; - if (tm != null && accountHandle != null) { - PhoneAccount account = tm.getPhoneAccount(accountHandle); - if (account != null) { - Uri address = account.getSubscriptionAddress(); - if (address != null) { - accountAddress = address.getSchemeSpecificPart(); - } - } - } + String accountAddress = getLogAccountAddress(context, accountHandle); - // Remap network specified number presentation types - // PhoneConstants.PRESENTATION_xxx to calllog number presentation types - // Calls.PRESENTATION_xxx, in order to insulate the persistent calllog - // from any future radio changes. - // If the number field is empty set the presentation type to Unknown. - if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) { - numberPresentation = PRESENTATION_RESTRICTED; - } else if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) { - numberPresentation = PRESENTATION_PAYPHONE; - } else if (TextUtils.isEmpty(number) - || presentation == PhoneConstants.PRESENTATION_UNKNOWN) { - numberPresentation = PRESENTATION_UNKNOWN; - } + int numberPresentation = getLogNumberPresentation(number, presentation); if (numberPresentation != PRESENTATION_ALLOWED) { number = ""; if (ci != null) { @@ -1138,8 +1098,7 @@ public class CallLog { if (TextUtils.isEmpty(countryIso)) { return; } - final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, - getCurrentCountryIso(context)); + final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso); if (TextUtils.isEmpty(normalizedNumber)) { return; } @@ -1148,6 +1107,54 @@ public class CallLog { resolver.update(Data.CONTENT_URI, values, Data._ID + "=?", new String[] {dataId}); } + /** + * Remap network specified number presentation types + * PhoneConstants.PRESENTATION_xxx to calllog number presentation types + * Calls.PRESENTATION_xxx, in order to insulate the persistent calllog + * from any future radio changes. + * If the number field is empty set the presentation type to Unknown. + */ + private static int getLogNumberPresentation(String number, int presentation) { + if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) { + return presentation; + } + + if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) { + return presentation; + } + + if (TextUtils.isEmpty(number) + || presentation == PhoneConstants.PRESENTATION_UNKNOWN) { + return PRESENTATION_UNKNOWN; + } + + return PRESENTATION_ALLOWED; + } + + private static String getLogAccountAddress(Context context, + PhoneAccountHandle accountHandle) { + TelecomManager tm = null; + try { + tm = TelecomManager.from(context); + } catch (UnsupportedOperationException e) { + if (VERBOSE_LOG) { + Log.v(LOG_TAG, "No TelecomManager found to get account address."); + } + } + + String accountAddress = null; + if (tm != null && accountHandle != null) { + PhoneAccount account = tm.getPhoneAccount(accountHandle); + if (account != null) { + Uri address = account.getSubscriptionAddress(); + if (address != null) { + accountAddress = address.getSchemeSpecificPart(); + } + } + } + return accountAddress; + } + private static String getCurrentCountryIso(Context context) { String countryIso = null; final CountryDetector detector = (CountryDetector) context.getSystemService( diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index e904b0713e24..abd7c89a0301 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -12862,6 +12862,19 @@ public final class Settings { "user_preferred_sub2","user_preferred_sub3"}; /** + * Which subscription is enabled for a physical slot. + * @hide + */ + public static final String ENABLED_SUBSCRIPTION_FOR_SLOT = "enabled_subscription_for_slot"; + + /** + * Whether corresponding logical modem is enabled for a physical slot. + * The value 1 - enable, 0 - disable + * @hide + */ + public static final String MODEM_STACK_ENABLED_FOR_SLOT = "modem_stack_enabled_for_slot"; + + /** * Whether to enable new contacts aggregator or not. * The value 1 - enable, 0 - disable * @hide diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index fda0a3ee4147..15c14e7bd1ef 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -30,8 +30,7 @@ import android.icu.text.Edits; import android.icu.util.ULocale; import android.os.Parcel; import android.os.Parcelable; -import android.os.SystemProperties; -import android.provider.Settings; +import android.sysprop.DisplayProperties; import android.text.style.AbsoluteSizeSpan; import android.text.style.AccessibilityClickableSpan; import android.text.style.AccessibilityURLSpan; @@ -2001,7 +2000,7 @@ public class TextUtils { return ((locale != null && !locale.equals(Locale.ROOT) && ULocale.forLocale(locale).isRightToLeft()) // If forcing into RTL layout mode, return RTL as default - || SystemProperties.getBoolean(Settings.Global.DEVELOPMENT_FORCE_RTL, false)) + || DisplayProperties.debug_force_rtl().orElse(false)) ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR; } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index f228773aebf2..efff6d617376 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -74,8 +74,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.os.SystemClock; -import android.os.SystemProperties; import android.os.Trace; +import android.sysprop.DisplayProperties; import android.text.InputType; import android.text.TextUtils; import android.util.AttributeSet; @@ -790,14 +790,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, protected static final String VIEW_LOG_TAG = "View"; /** - * When set to true, apps will draw debugging information about their layouts. - * - * @hide - */ - @UnsupportedAppUsage - public static final String DEBUG_LAYOUT_PROPERTY = "debug.layout"; - - /** * When set to true, this view will save its attribute data. * * @hide @@ -26855,7 +26847,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Show where the margins, bounds and layout bounds are for each view. */ - boolean mDebugLayout = SystemProperties.getBoolean(DEBUG_LAYOUT_PROPERTY, false); + boolean mDebugLayout = DisplayProperties.debug_layout().orElse(false); /** * Point used to compute visible regions. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index fd11ef13f9ac..798401d87b5a 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -70,6 +70,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; +import android.sysprop.DisplayProperties; import android.util.AndroidRuntimeException; import android.util.DisplayMetrics; import android.util.Log; @@ -6829,7 +6830,7 @@ public final class ViewRootImpl implements ViewParent, } // Layout debugging - boolean layout = SystemProperties.getBoolean(View.DEBUG_LAYOUT_PROPERTY, false); + boolean layout = DisplayProperties.debug_layout().orElse(false); if (layout != mAttachInfo.mDebugLayout) { mAttachInfo.mDebugLayout = layout; if (!mHandler.hasMessages(MSG_INVALIDATE_WORLD)) { diff --git a/core/java/android/view/accessibility/OWNERS b/core/java/android/view/accessibility/OWNERS new file mode 100644 index 000000000000..265674a74b7e --- /dev/null +++ b/core/java/android/view/accessibility/OWNERS @@ -0,0 +1,3 @@ +svetoslavganov@google.com +pweaver@google.com +rhedjao@google.com diff --git a/core/java/android/webkit/OWNERS b/core/java/android/webkit/OWNERS index 00e540a46ab2..b33da57c42e3 100644 --- a/core/java/android/webkit/OWNERS +++ b/core/java/android/webkit/OWNERS @@ -1,3 +1,4 @@ changwan@google.com +ntfschr@google.com tobiasjs@google.com torne@google.com diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 1048cb4e6e3a..56eb128558e8 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -93,6 +93,11 @@ public final class Zygote { */ public static final int PROFILE_SYSTEM_SERVER = 1 << 14; + /* + * Enable using the ART app image startup cache + */ + public static final int USE_APP_IMAGE_STARTUP_CACHE = 1 << 16; + /** No external storage should be mounted. */ public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE; /** Default external storage should be mounted. */ diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index 2bb075989f35..d067ae792e2d 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -315,9 +315,14 @@ class ZygoteConnection { } } - private class HiddenApiUsageLogger implements VMRuntime.HiddenApiUsageLogger { + private static class HiddenApiUsageLogger implements VMRuntime.HiddenApiUsageLogger { private final MetricsLogger mMetricsLogger = new MetricsLogger(); + private static HiddenApiUsageLogger sInstance = new HiddenApiUsageLogger(); + + public static HiddenApiUsageLogger getInstance() { + return HiddenApiUsageLogger.sInstance; + } public void hiddenApiUsed(String packageName, String signature, int accessMethod, boolean accessDenied) { @@ -351,7 +356,7 @@ class ZygoteConnection { private void handleHiddenApiAccessLogSampleRate(int samplingRate) { try { ZygoteInit.setHiddenApiAccessLogSampleRate(samplingRate); - ZygoteInit.setHiddenApiUsageLogger(new HiddenApiUsageLogger()); + ZygoteInit.setHiddenApiUsageLogger(HiddenApiUsageLogger.getInstance()); mSocketOutStream.writeInt(0); } catch (IOException ioe) { throw new IllegalStateException("Error writing to command socket", ioe); diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 9f23797d6ccc..e132abd7e4cb 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -41,6 +41,7 @@ import android.system.OsConstants; import android.system.StructCapUserData; import android.system.StructCapUserHeader; import android.text.Hyphenator; +import android.text.TextUtils; import android.util.EventLog; import android.util.Log; import android.util.Slog; @@ -84,6 +85,8 @@ public class ZygoteInit { private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload"; private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0"; + private static final String PROPERTY_USE_APP_IMAGE_STARTUP_CACHE = + "persist.device_config.runtime_native.use_app_image_startup_cache"; private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020; private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030; @@ -705,6 +708,13 @@ public class ZygoteInit { parsedArgs.mRuntimeFlags |= Zygote.PROFILE_SYSTEM_SERVER; } + String use_app_image_cache = SystemProperties.get( + PROPERTY_USE_APP_IMAGE_STARTUP_CACHE, ""); + // Property defaults to true currently. + if (!TextUtils.isEmpty(use_app_image_cache) && !use_app_image_cache.equals("false")) { + parsedArgs.mRuntimeFlags |= Zygote.USE_APP_IMAGE_STARTUP_CACHE; + } + /* Request to fork the system server process */ pid = Zygote.forkSystemServer( parsedArgs.mUid, parsedArgs.mGid, diff --git a/core/jni/Android.bp b/core/jni/Android.bp index c81a77d4c16e..08a5789743cc 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -278,8 +278,8 @@ cc_library_shared { "libsoundtrigger", "libminikin", "libprocessgroup", - "libnativebridge", - "libnativeloader", + "libnativebridge_lazy", + "libnativeloader_lazy", "libmemunreachable", "libhidlbase", "libhidltransport", diff --git a/core/jni/OWNERS b/core/jni/OWNERS index 86342c47c455..774c2242e144 100644 --- a/core/jni/OWNERS +++ b/core/jni/OWNERS @@ -3,7 +3,7 @@ per-file *Camera*,*camera* = cychen@google.com, epeev@google.com, etalvala@googl per-file *Camera*,*camera* = shuzhenwang@google.com, yinchiayeh@google.com, zhijunhe@google.com # Connectivity -per-file android_net_* = ek@google.com, lorenzo@google.com, satk@google.com +per-file android_net_* = codewiz@google.com, jchalard@google.com, lorenzo@google.com, reminv@google.com, satk@google.com # Zygote per-file com_android_inernal_os_Zygote.*,fd_utils.* = chriswailes@google.com, ngeoffray@google.com, sehr@google.com, narayan@google.com, maco@google.com diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp index 7eddcfe425d3..cfb2dd199f39 100644 --- a/core/jni/android_net_NetUtils.cpp +++ b/core/jni/android_net_NetUtils.cpp @@ -29,6 +29,7 @@ #include <net/if.h> #include <linux/filter.h> #include <linux/if_arp.h> +#include <linux/tcp.h> #include <netinet/ether.h> #include <netinet/icmp6.h> #include <netinet/ip.h> @@ -226,6 +227,34 @@ static void android_net_utils_attachControlPacketFilter( } } +static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd) +{ + struct sock_filter filter_code[] = { + // Reject all. + BPF_STMT(BPF_RET | BPF_K, 0) + }; + struct sock_fprog filter = { + sizeof(filter_code) / sizeof(filter_code[0]), + filter_code, + }; + + int fd = jniGetFDFromFileDescriptor(env, javaFd); + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) { + jniThrowExceptionFmt(env, "java/net/SocketException", + "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); + } +} + +static void android_net_utils_detachBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd) +{ + int dummy = 0; + int fd = jniGetFDFromFileDescriptor(env, javaFd); + if (setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, &dummy, sizeof(dummy)) != 0) { + jniThrowExceptionFmt(env, "java/net/SocketException", + "setsockopt(SO_DETACH_FILTER): %s", strerror(errno)); + } + +} static void android_net_utils_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd, jint ifIndex) { @@ -458,6 +487,41 @@ static jbyteArray android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, return answer; } +static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, jobject javaFd) { + if (javaFd == NULL) { + jniThrowNullPointerException(env, NULL); + return NULL; + } + + int fd = jniGetFDFromFileDescriptor(env, javaFd); + struct tcp_repair_window trw = {}; + socklen_t size = sizeof(trw); + + // Obtain the parameters of the TCP repair window. + int rc = getsockopt(fd, IPPROTO_TCP, TCP_REPAIR_WINDOW, &trw, &size); + if (rc == -1) { + throwErrnoException(env, "getsockopt : TCP_REPAIR_WINDOW", errno); + return NULL; + } + + struct tcp_info tcpinfo = {}; + socklen_t tcpinfo_size = sizeof(tcp_info); + + // Obtain the window scale from the tcp info structure. This contains a scale factor that + // should be applied to the window size. + rc = getsockopt(fd, IPPROTO_TCP, TCP_INFO, &tcpinfo, &tcpinfo_size); + if (rc == -1) { + throwErrnoException(env, "getsockopt : TCP_INFO", errno); + return NULL; + } + + jclass class_TcpRepairWindow = env->FindClass("android/net/TcpRepairWindow"); + jmethodID ctor = env->GetMethodID(class_TcpRepairWindow, "<init>", "(IIIIII)V"); + + return env->NewObject(class_TcpRepairWindow, ctor, trw.snd_wl1, trw.snd_wnd, trw.max_window, + trw.rcv_wnd, trw.rcv_wup, tcpinfo.tcpi_rcv_wscale); +} + // ---------------------------------------------------------------------------- /* @@ -475,6 +539,9 @@ static const JNINativeMethod gNetworkUtilMethods[] = { { "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter }, { "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachRaFilter }, { "attachControlPacketFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachControlPacketFilter }, + { "attachDropAllBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDropAllBPFFilter }, + { "detachBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_detachBPFFilter }, + { "getTcpRepairWindow", "(Ljava/io/FileDescriptor;)Landroid/net/TcpRepairWindow;", (void*) android_net_utils_getTcpRepairWindow }, { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_setupRaSocket }, { "resNetworkSend", "(I[BII)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkSend }, { "resNetworkQuery", "(ILjava/lang/String;III)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkQuery }, diff --git a/core/proto/android/bluetooth/enums.proto b/core/proto/android/bluetooth/enums.proto index a88a06cf091c..b4f3d1ea5ae4 100644 --- a/core/proto/android/bluetooth/enums.proto +++ b/core/proto/android/bluetooth/enums.proto @@ -110,3 +110,31 @@ enum UnbondReasonEnum { UNBOND_REASON_REMOTE_AUTH_CANCELED = 8; UNBOND_REASON_REMOVED = 9; } + +enum SocketTypeEnum { + SOCKET_TYPE_UNKNOWN = 0; + SOCKET_TYPE_RFCOMM = 1; + SOCKET_TYPE_SCO = 2; + SOCKET_TYPE_L2CAP_BREDR = 3; + SOCKET_TYPE_L2CAP_LE = 4; +} + +enum SocketConnectionstateEnum { + SOCKET_CONNECTION_STATE_UNKNOWN = 0; + // Socket acts as a server waiting for connection + SOCKET_CONNECTION_STATE_LISTENING = 1; + // Socket acts as a client trying to connect + SOCKET_CONNECTION_STATE_CONNECTING = 2; + // Socket is connected + SOCKET_CONNECTION_STATE_CONNECTED = 3; + // Socket tries to disconnect from remote + SOCKET_CONNECTION_STATE_DISCONNECTING = 4; + // This socket is closed + SOCKET_CONNECTION_STATE_DISCONNECTED = 5; +} + +enum SocketRoleEnum { + SOCKET_ROLE_UNKNOWN = 0; + SOCKET_ROLE_LISTEN = 1; + SOCKET_ROLE_CONNECTION = 2; +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 9a7e8dd0b7cb..7590675a9f9d 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -481,6 +481,7 @@ <protected-broadcast android:name="android.security.action.TRUST_STORE_CHANGED" /> <protected-broadcast android:name="android.security.action.KEYCHAIN_CHANGED" /> <protected-broadcast android:name="android.security.action.KEY_ACCESS_CHANGED" /> + <protected-broadcast android:name="android.telecom.action.NUISANCE_CALL_STATUS_CHANGED" /> <protected-broadcast android:name="android.telecom.action.PHONE_ACCOUNT_REGISTERED" /> <protected-broadcast android:name="android.telecom.action.PHONE_ACCOUNT_UNREGISTERED" /> <protected-broadcast android:name="android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION" /> diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 212c7235f27b..94ba172fc676 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -505,7 +505,9 @@ public class SettingsBackupTest { Settings.Global.OVERRIDE_SETTINGS_PROVIDER_RESTORE_ANY_VERSION, Settings.Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED, Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS, - Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS); + Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS, + Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT, + Settings.Global.MODEM_STACK_ENABLED_FOR_SLOT); private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS = newHashSet( Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 9a148e4e7b79..4b247c6a8c4b 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -158,6 +158,7 @@ applications that come with the platform <permission name="android.permission.WRITE_APN_SETTINGS"/> <permission name="android.permission.WRITE_SECURE_SETTINGS"/> <permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/> + <permission name="android.permission.READ_PRECISE_PHONE_STATE"/> <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/> <permission name="com.android.voicemail.permission.WRITE_VOICEMAIL"/> </privapp-permissions> @@ -201,21 +202,14 @@ applications that come with the platform <privapp-permissions package="com.android.mainline.networkstack"> <permission name="android.permission.ACCESS_NETWORK_CONDITIONS"/> - <permission name="android.permission.CHANGE_BACKGROUND_DATA_SETTING"/> <permission name="android.permission.CONNECTIVITY_INTERNAL"/> <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/> <permission name="android.permission.CONTROL_VPN"/> + <permission name="android.permission.INTERACT_ACROSS_USERS"/> <permission name="android.permission.LOCAL_MAC_ADDRESS"/> - <permission name="android.permission.MANAGE_IPSEC_TUNNELS"/> - <permission name="android.permission.MANAGE_NETWORK_POLICY"/> <permission name="android.permission.MANAGE_SUBSCRIPTION_PLANS"/> <permission name="android.permission.MANAGE_USB"/> - <permission name="android.permission.NETWORK_BYPASS_PRIVATE_DNS"/> - <permission name="android.permission.NETWORK_SETTINGS"/> - <permission name="android.permission.NETWORK_STACK" /> - <permission name="android.permission.NET_TUNNELING"/> <permission name="android.permission.PACKET_KEEPALIVE_OFFLOAD"/> - <permission name="android.permission.PEERS_MAC_ADDRESS"/> <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/> <permission name="android.permission.READ_PRECISE_PHONE_STATE"/> <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> @@ -253,6 +247,7 @@ applications that come with the platform <permission name="android.permission.CHANGE_LOWPAN_STATE"/> <permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/> <permission name="android.permission.CLEAR_APP_CACHE"/> + <permission name="android.permission.ACCESS_INSTANT_APPS" /> <permission name="android.permission.CONNECTIVITY_INTERNAL"/> <permission name="android.permission.DELETE_CACHE_FILES"/> <permission name="android.permission.DELETE_PACKAGES"/> diff --git a/libs/hwui/FrameMetricsObserver.h b/libs/hwui/FrameMetricsObserver.h index ba72e937095f..0b9ae5cb4b7b 100644 --- a/libs/hwui/FrameMetricsObserver.h +++ b/libs/hwui/FrameMetricsObserver.h @@ -23,7 +23,7 @@ namespace uirenderer { class FrameMetricsObserver : public VirtualLightRefBase { public: - virtual void notify(const int64_t* buffer); + virtual void notify(const int64_t* buffer) = 0; }; }; // namespace uirenderer diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml index ac55bfa1aed7..e4d35915c77c 100644 --- a/packages/NetworkStack/AndroidManifest.xml +++ b/packages/NetworkStack/AndroidManifest.xml @@ -27,8 +27,8 @@ <uses-permission android:name="android.permission.NETWORK_SETTINGS" /> <!-- Signature permission defined in NetworkStackStub --> <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK" /> - <!-- Launch captive portal app as specific user --> - <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> + <!-- Send latency broadcast as current user --> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> <uses-permission android:name="android.permission.NETWORK_STACK" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <application diff --git a/packages/NetworkStack/OWNERS b/packages/NetworkStack/OWNERS new file mode 100644 index 000000000000..a395465e5f21 --- /dev/null +++ b/packages/NetworkStack/OWNERS @@ -0,0 +1,5 @@ +codewiz@google.com +jchalard@google.com +lorenzo@google.com +reminv@google.com +satk@google.com diff --git a/packages/NetworkStack/src/android/net/apf/ApfFilter.java b/packages/NetworkStack/src/android/net/apf/ApfFilter.java index 4fa7d6462092..923f162c92a6 100644 --- a/packages/NetworkStack/src/android/net/apf/ApfFilter.java +++ b/packages/NetworkStack/src/android/net/apf/ApfFilter.java @@ -23,6 +23,7 @@ import static android.system.OsConstants.ETH_P_ARP; import static android.system.OsConstants.ETH_P_IP; import static android.system.OsConstants.ETH_P_IPV6; import static android.system.OsConstants.IPPROTO_ICMPV6; +import static android.system.OsConstants.IPPROTO_TCP; import static android.system.OsConstants.IPPROTO_UDP; import static android.system.OsConstants.SOCK_RAW; @@ -38,6 +39,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.net.LinkAddress; import android.net.LinkProperties; +import android.net.TcpKeepalivePacketDataParcelable; import android.net.apf.ApfGenerator.IllegalInstructionException; import android.net.apf.ApfGenerator.Register; import android.net.ip.IpClient.IpClientCallbacksWrapper; @@ -55,6 +57,7 @@ import android.system.Os; import android.text.format.DateUtils; import android.util.Log; import android.util.Pair; +import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -149,7 +152,9 @@ public class ApfFilter { DROPPED_IPV6_NON_ICMP_MULTICAST, DROPPED_802_3_FRAME, DROPPED_ETHERTYPE_BLACKLISTED, - DROPPED_ARP_REPLY_SPA_NO_HOST; + DROPPED_ARP_REPLY_SPA_NO_HOST, + DROPPED_IPV4_KEEPALIVE_ACK, + DROPPED_IPV6_KEEPALIVE_ACK; // Returns the negative byte offset from the end of the APF data segment for // a given counter. @@ -285,6 +290,7 @@ public class ApfFilter { private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16; private static final int IPV4_ANY_HOST_ADDRESS = 0; private static final int IPV4_BROADCAST_ADDRESS = -1; // 255.255.255.255 + private static final int IPV4_HEADER_LEN = 20; // Without options // Traffic class and Flow label are not byte aligned. Luckily we // don't care about either value so we'll consider bytes 1-3 of the @@ -305,6 +311,8 @@ public class ApfFilter { private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 2; private static final int UDP_HEADER_LEN = 8; + private static final int TCP_HEADER_SIZE_OFFSET = 12; + private static final int DHCP_CLIENT_PORT = 68; // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 28; @@ -788,7 +796,7 @@ public class ApfFilter { boolean isExpired() { // TODO: We may want to handle 0 lifetime RAs differently, if they are common. We'll - // have to calculte the filter lifetime specially as a fraction of 0 is still 0. + // have to calculate the filter lifetime specially as a fraction of 0 is still 0. return currentLifetime() <= 0; } @@ -847,11 +855,147 @@ public class ApfFilter { } } + // A class to hold keepalive ack information. + private abstract static class TcpKeepaliveAck { + // Note that the offset starts from IP header. + // These must be added ether header length when generating program. + static final int IP_HEADER_OFFSET = 0; + + protected static class TcpKeepaliveAckData { + public final byte[] srcAddress; + public final int srcPort; + public final byte[] dstAddress; + public final int dstPort; + public final int seq; + public final int ack; + // Create the characteristics of the ack packet from the sent keepalive packet. + TcpKeepaliveAckData(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) { + srcAddress = sentKeepalivePacket.dstAddress; + srcPort = sentKeepalivePacket.dstPort; + dstAddress = sentKeepalivePacket.srcAddress; + dstPort = sentKeepalivePacket.srcPort; + seq = sentKeepalivePacket.ack; + ack = sentKeepalivePacket.seq + 1; + } + } + + protected final TcpKeepaliveAckData mPacket; + protected final byte[] mSrcDstAddr; + + TcpKeepaliveAck(final TcpKeepaliveAckData packet, final byte[] srcDstAddr) { + mPacket = packet; + mSrcDstAddr = srcDstAddr; + } + + static byte[] concatArrays(final byte[]... arr) { + int size = 0; + for (byte[] a : arr) { + size += a.length; + } + final byte[] result = new byte[size]; + int offset = 0; + for (byte[] a : arr) { + System.arraycopy(a, 0, result, offset, a.length); + offset += a.length; + } + return result; + } + + public String toString() { + return String.format("%s(%d) -> %s(%d), seq=%d, ack=%d", + mPacket.srcAddress, + mPacket.srcPort, + mPacket.dstAddress, + mPacket.dstPort, + mPacket.seq, + mPacket.ack); + } + + // Append a filter for this keepalive ack to {@code gen}. + // Jump to drop if it matches the keepalive ack. + // Jump to the next filter if packet doesn't match the keepalive ack. + abstract void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException; + } + + private class TcpKeepaliveAckV4 extends TcpKeepaliveAck { + private static final int IPV4_SRC_ADDR_OFFSET = IP_HEADER_OFFSET + 12; + private static final int IPV4_TCP_SRC_PORT_OFFSET = 0; + private static final int IPV4_TCP_DST_PORT_OFFSET = 2; + private static final int IPV4_TCP_SEQ_OFFSET = 4; + private static final int IPV4_TCP_ACK_OFFSET = 8; + + TcpKeepaliveAckV4(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) { + this(new TcpKeepaliveAckData(sentKeepalivePacket)); + } + TcpKeepaliveAckV4(final TcpKeepaliveAckData packet) { + super(packet, concatArrays(packet.srcAddress, packet.dstAddress) /* srcDstAddr */); + } + + @Override + void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException { + final String nextFilterLabel = "keepalive_ack" + getUniqueNumberLocked(); + gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET); + gen.addJumpIfR0NotEquals(IPPROTO_TCP, nextFilterLabel); + gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET); + gen.addJumpIfBytesNotEqual(Register.R0, mSrcDstAddr, nextFilterLabel); + + // Pass the packet if it's not zero-sized : + // Load the IP header size into R1 + gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); + // Load the TCP header size into R0 (it's indexed by R1) + gen.addLoad8Indexed(Register.R0, ETH_HEADER_LEN + TCP_HEADER_SIZE_OFFSET); + // Size offset is in the top nibble, but it must be multiplied by 4, and the two + // top bits of the low nibble are guaranteed to be zeroes. Right-shift R0 by 2. + gen.addRightShift(2); + // R0 += R1 -> R0 contains TCP + IP headers lenght + gen.addAddR1(); + // Add the Ethernet header length to R0. + gen.addLoadImmediate(Register.R1, ETH_HEADER_LEN); + gen.addAddR1(); + // Compare total length of headers to the size of the packet. + gen.addLoadFromMemory(Register.R1, gen.PACKET_SIZE_MEMORY_SLOT); + gen.addNeg(Register.R0); + gen.addAddR1(); + gen.addJumpIfR0NotEquals(0, nextFilterLabel); + + // Add IPv4 header length + gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); + gen.addLoad16Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_SRC_PORT_OFFSET); + gen.addJumpIfR0NotEquals(mPacket.srcPort, nextFilterLabel); + gen.addLoad16Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_DST_PORT_OFFSET); + gen.addJumpIfR0NotEquals(mPacket.dstPort, nextFilterLabel); + gen.addLoad32Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_SEQ_OFFSET); + gen.addJumpIfR0NotEquals(mPacket.seq, nextFilterLabel); + gen.addLoad32Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_ACK_OFFSET); + gen.addJumpIfR0NotEquals(mPacket.ack, nextFilterLabel); + + maybeSetupCounter(gen, Counter.DROPPED_IPV4_KEEPALIVE_ACK); + gen.addJump(mCountAndDropLabel); + gen.defineLabel(nextFilterLabel); + } + } + + private class TcpKeepaliveAckV6 extends TcpKeepaliveAck { + TcpKeepaliveAckV6(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) { + this(new TcpKeepaliveAckData(sentKeepalivePacket)); + } + TcpKeepaliveAckV6(final TcpKeepaliveAckData packet) { + super(packet, concatArrays(packet.srcAddress, packet.dstAddress) /* srcDstAddr */); + } + + @Override + void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException { + throw new UnsupportedOperationException("IPv6 Keepalive is not supported yet"); + } + } + // Maximum number of RAs to filter for. private static final int MAX_RAS = 10; @GuardedBy("this") - private ArrayList<Ra> mRas = new ArrayList<Ra>(); + private ArrayList<Ra> mRas = new ArrayList<>(); + @GuardedBy("this") + private SparseArray<TcpKeepaliveAck> mKeepaliveAcks = new SparseArray<>(); // There is always some marginal benefit to updating the installed APF program when an RA is // seen because we can extend the program's lifetime slightly, but there is some cost to @@ -980,6 +1124,8 @@ public class ApfFilter { // drop // if it's IPv4 broadcast: // drop + // if keepalive ack + // drop // pass if (mMulticastFilter) { @@ -1023,6 +1169,9 @@ public class ApfFilter { gen.addJumpIfR0Equals(broadcastAddr, mCountAndDropLabel); } + // If any keepalive filters, + generateKeepaliveFilter(gen); + // If L2 broadcast packet, drop. // TODO: can we invert this condition to fall through to the common pass case below? maybeSetupCounter(gen, Counter.PASSED_IPV4_UNICAST); @@ -1030,6 +1179,8 @@ public class ApfFilter { gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel); maybeSetupCounter(gen, Counter.DROPPED_IPV4_L2_BROADCAST); gen.addJump(mCountAndDropLabel); + } else { + generateKeepaliveFilter(gen); } // Otherwise, pass @@ -1037,6 +1188,13 @@ public class ApfFilter { gen.addJump(mCountAndPassLabel); } + private void generateKeepaliveFilter(ApfGenerator gen) throws IllegalInstructionException { + // Drop IPv4 Keepalive acks + for (int i = 0; i < mKeepaliveAcks.size(); ++i) { + final TcpKeepaliveAck ack = mKeepaliveAcks.valueAt(i); + if (ack instanceof TcpKeepaliveAckV4) ack.generateFilterLocked(gen); + } + } /** * Generate filter code to process IPv6 packets. Execution of this code ends in either the @@ -1057,6 +1215,8 @@ public class ApfFilter { // drop // if it's ICMPv6 NA to ff02::1: // drop + // if keepalive ack + // drop gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET); @@ -1112,6 +1272,12 @@ public class ApfFilter { maybeSetupCounter(gen, Counter.DROPPED_IPV6_MULTICAST_NA); gen.addJump(mCountAndDropLabel); gen.defineLabel(skipUnsolicitedMulticastNALabel); + + // Drop IPv6 Keepalive acks + for (int i = 0; i < mKeepaliveAcks.size(); ++i) { + final TcpKeepaliveAck ack = mKeepaliveAcks.valueAt(i); + if (ack instanceof TcpKeepaliveAckV6) ack.generateFilterLocked(gen); + } } /** @@ -1489,6 +1655,36 @@ public class ApfFilter { installNewProgramLocked(); } + /** + * Add keepalive ack packet filter. + * This will add a filter to drop acks to the keepalive packet passed as an argument. + * + * @param slot The index used to access the filter. + * @param sentKeepalivePacket The attributes of the sent keepalive packet. + */ + public synchronized void addKeepalivePacketFilter(final int slot, + final TcpKeepalivePacketDataParcelable sentKeepalivePacket) { + log("Adding keepalive ack(" + slot + ")"); + if (null != mKeepaliveAcks.get(slot)) { + throw new IllegalArgumentException("Keepalive slot " + slot + " is occupied"); + } + final int ipVersion = sentKeepalivePacket.srcAddress.length == 4 ? 4 : 6; + mKeepaliveAcks.put(slot, (ipVersion == 4) + ? new TcpKeepaliveAckV4(sentKeepalivePacket) + : new TcpKeepaliveAckV6(sentKeepalivePacket)); + installNewProgramLocked(); + } + + /** + * Remove keepalive packet filter. + * + * @param slot The index used to access the filter. + */ + public synchronized void removeKeepalivePacketFilter(int slot) { + mKeepaliveAcks.remove(slot); + installNewProgramLocked(); + } + static public long counterValue(byte[] data, Counter counter) throws ArrayIndexOutOfBoundsException { // Follow the same wrap-around addressing scheme of the interpreter. @@ -1541,6 +1737,17 @@ public class ApfFilter { } pw.decreaseIndent(); + pw.println("Keepalive filter:"); + pw.increaseIndent(); + for (int i = 0; i < mKeepaliveAcks.size(); ++i) { + final TcpKeepaliveAck keepaliveAck = mKeepaliveAcks.valueAt(i); + pw.print("Slot "); + pw.print(mKeepaliveAcks.keyAt(i)); + pw.print(" : "); + pw.println(keepaliveAck); + } + pw.decreaseIndent(); + if (DBG) { pw.println("Last program:"); pw.increaseIndent(); diff --git a/packages/NetworkStack/src/android/net/apf/ApfGenerator.java b/packages/NetworkStack/src/android/net/apf/ApfGenerator.java index 87a1b5ea8b4d..809327a0b79d 100644 --- a/packages/NetworkStack/src/android/net/apf/ApfGenerator.java +++ b/packages/NetworkStack/src/android/net/apf/ApfGenerator.java @@ -476,7 +476,7 @@ public class ApfGenerator { /** * Add an instruction to the end of the program to load 16-bits from the packet into - * {@code register}. The offset of the loaded 16-bits from the begining of the packet is + * {@code register}. The offset of the loaded 16-bits from the beginning of the packet is * the sum of {@code offset} and the value in register R1. */ public ApfGenerator addLoad16Indexed(Register register, int offset) { @@ -488,7 +488,7 @@ public class ApfGenerator { /** * Add an instruction to the end of the program to load 32-bits from the packet into - * {@code register}. The offset of the loaded 32-bits from the begining of the packet is + * {@code register}. The offset of the loaded 32-bits from the beginning of the packet is * the sum of {@code offset} and the value in register R1. */ public ApfGenerator addLoad32Indexed(Register register, int offset) { diff --git a/packages/NetworkStack/src/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java index 12fe8c507db4..9e5991298834 100644 --- a/packages/NetworkStack/src/android/net/ip/IpClient.java +++ b/packages/NetworkStack/src/android/net/ip/IpClient.java @@ -23,6 +23,7 @@ import static android.net.shared.LinkPropertiesParcelableUtil.toStableParcelable import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission; +import android.annotation.NonNull; import android.content.Context; import android.net.ConnectivityManager; import android.net.DhcpResults; @@ -34,6 +35,7 @@ import android.net.ProvisioningConfigurationParcelable; import android.net.ProxyInfo; import android.net.ProxyInfoParcelable; import android.net.RouteInfo; +import android.net.TcpKeepalivePacketDataParcelable; import android.net.apf.ApfCapabilities; import android.net.apf.ApfFilter; import android.net.dhcp.DhcpClient; @@ -292,6 +294,8 @@ public class IpClient extends StateMachine { private static final int EVENT_PROVISIONING_TIMEOUT = 10; private static final int EVENT_DHCPACTION_TIMEOUT = 11; private static final int EVENT_READ_PACKET_FILTER_COMPLETE = 12; + private static final int CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF = 13; + private static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF = 14; // Internal commands to use instead of trying to call transitionTo() inside // a given State's enter() method. Calling transitionTo() from enter/exit @@ -522,6 +526,16 @@ public class IpClient extends StateMachine { checkNetworkStackCallingPermission(); IpClient.this.setMulticastFilter(enabled); } + @Override + public void addKeepalivePacketFilter(int slot, TcpKeepalivePacketDataParcelable pkt) { + checkNetworkStackCallingPermission(); + IpClient.this.addKeepalivePacketFilter(slot, pkt); + } + @Override + public void removeKeepalivePacketFilter(int slot) { + checkNetworkStackCallingPermission(); + IpClient.this.removeKeepalivePacketFilter(slot); + } } public String getInterfaceName() { @@ -644,6 +658,22 @@ public class IpClient extends StateMachine { } /** + * Called by WifiStateMachine to add keepalive packet filter before setting up + * keepalive offload. + */ + public void addKeepalivePacketFilter(int slot, @NonNull TcpKeepalivePacketDataParcelable pkt) { + sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, 0 /* Unused */, pkt); + } + + /** + * Called by WifiStateMachine to remove keepalive packet filter after stopping keepalive + * offload. + */ + public void removeKeepalivePacketFilter(int slot) { + sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF, slot, 0 /* Unused */); + } + + /** * Dump logs of this IpClient. */ public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { @@ -1512,6 +1542,23 @@ public class IpClient extends StateMachine { break; } + case CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF: { + final int slot = msg.arg1; + if (mApfFilter != null) { + mApfFilter.addKeepalivePacketFilter(slot, + (TcpKeepalivePacketDataParcelable) msg.obj); + } + break; + } + + case CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF: { + final int slot = msg.arg1; + if (mApfFilter != null) { + mApfFilter.removeKeepalivePacketFilter(slot); + } + break; + } + case EVENT_DHCPACTION_TIMEOUT: stopDhcpAction(); break; diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java index dbffa6d6bcf2..2e72d8296a37 100644 --- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java @@ -61,6 +61,7 @@ import android.net.util.SharedLog; import android.net.util.Stopwatch; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; +import android.os.Bundle; import android.os.Message; import android.os.RemoteException; import android.os.SystemClock; @@ -499,7 +500,7 @@ public class NetworkMonitor extends StateMachine { private void showProvisioningNotification(String action) { try { - mCallback.showProvisioningNotification(action); + mCallback.showProvisioningNotification(action, mContext.getPackageName()); } catch (RemoteException e) { Log.e(TAG, "Error showing provisioning notification", e); } @@ -674,11 +675,11 @@ public class NetworkMonitor extends StateMachine { public boolean processMessage(Message message) { switch (message.what) { case CMD_LAUNCH_CAPTIVE_PORTAL_APP: - final Intent intent = new Intent( - ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN); + final Bundle appExtras = new Bundle(); // OneAddressPerFamilyNetwork is not parcelable across processes. - intent.putExtra(ConnectivityManager.EXTRA_NETWORK, new Network(mNetwork)); - intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL, + appExtras.putParcelable( + ConnectivityManager.EXTRA_NETWORK, new Network(mNetwork)); + appExtras.putParcelable(ConnectivityManager.EXTRA_CAPTIVE_PORTAL, new CaptivePortal(new ICaptivePortal.Stub() { @Override public void appResponse(int response) { @@ -700,16 +701,14 @@ public class NetworkMonitor extends StateMachine { } })); final CaptivePortalProbeResult probeRes = mLastPortalProbeResult; - intent.putExtra(EXTRA_CAPTIVE_PORTAL_URL, probeRes.detectUrl); + appExtras.putString(EXTRA_CAPTIVE_PORTAL_URL, probeRes.detectUrl); if (probeRes.probeSpec != null) { final String encodedSpec = probeRes.probeSpec.getEncodedSpec(); - intent.putExtra(EXTRA_CAPTIVE_PORTAL_PROBE_SPEC, encodedSpec); + appExtras.putString(EXTRA_CAPTIVE_PORTAL_PROBE_SPEC, encodedSpec); } - intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT, + appExtras.putString(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT, mCaptivePortalUserAgent); - intent.setFlags( - Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK); - mContext.startActivityAsUser(intent, UserHandle.CURRENT); + mCm.startCaptivePortalApp(appExtras); return HANDLED; default: return NOT_HANDLED; diff --git a/packages/NetworkStack/tests/Android.bp b/packages/NetworkStack/tests/Android.bp index 45fa2dc2f383..4a09b3e205a6 100644 --- a/packages/NetworkStack/tests/Android.bp +++ b/packages/NetworkStack/tests/Android.bp @@ -49,6 +49,7 @@ android_test { "libhidlbase", "libhidltransport", "libhwbinder", + "libjsoncpp", "liblog", "liblzma", "libnativehelper", diff --git a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java index f76e41217c2a..a4a100000d12 100644 --- a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java +++ b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java @@ -39,13 +39,14 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.net.LinkAddress; import android.net.LinkProperties; +import android.net.SocketKeepalive; +import android.net.TcpKeepalivePacketData; +import android.net.TcpKeepalivePacketData.TcpSocketInfo; import android.net.apf.ApfFilter.ApfConfiguration; import android.net.apf.ApfGenerator.IllegalInstructionException; import android.net.apf.ApfGenerator.Register; import android.net.ip.IIpClientCallbacks; -import android.net.ip.IpClient; import android.net.ip.IpClient.IpClientCallbacksWrapper; -import android.net.ip.IpClientCallbacks; import android.net.metrics.IpConnectivityLog; import android.net.metrics.RaEvent; import android.net.util.InterfaceParams; @@ -1003,15 +1004,31 @@ public class ApfTest { private static final byte[] ETH_BROADCAST_MAC_ADDRESS = {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }; + private static final int IPV4_HEADER_LEN = 20; private static final int IPV4_VERSION_IHL_OFFSET = ETH_HEADER_LEN + 0; + private static final int IPV4_TOTAL_LENGTH_OFFSET = ETH_HEADER_LEN + 2; private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9; + private static final int IPV4_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 12; private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16; + private static final int IPV4_TCP_HEADER_LEN = 20; + private static final int IPV4_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN; + private static final int IPV4_TCP_SRC_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 0; + private static final int IPV4_TCP_DEST_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 2; + private static final int IPV4_TCP_SEQ_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 4; + private static final int IPV4_TCP_ACK_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 8; + private static final int IPV4_TCP_HEADER_LENGTH_OFFSET = IPV4_TCP_HEADER_OFFSET + 12; private static final byte[] IPV4_BROADCAST_ADDRESS = {(byte) 255, (byte) 255, (byte) 255, (byte) 255}; private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6; private static final int IPV6_HEADER_LEN = 40; + private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8; private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24; + private static final int IPV6_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN; + private static final int IPV6_TCP_SRC_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 0; + private static final int IPV6_TCP_DEST_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 2; + private static final int IPV6_TCP_SEQ_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 4; + private static final int IPV6_TCP_ACK_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 8; // The IPv6 all nodes address ff02::1 private static final byte[] IPV6_ALL_NODES_ADDRESS = { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; @@ -1491,6 +1508,200 @@ public class ApfTest { return packet.array(); } + private static final byte[] IPV4_KEEPALIVE_SRC_ADDR = {10, 0, 0, 5}; + private static final byte[] IPV4_KEEPALIVE_DST_ADDR = {10, 0, 0, 6}; + private static final byte[] IPV4_ANOTHER_ADDR = {10, 0 , 0, 7}; + private static final byte[] IPV6_KEEPALIVE_SRC_ADDR = + {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf1}; + private static final byte[] IPV6_KEEPALIVE_DST_ADDR = + {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf2}; + private static final byte[] IPV6_ANOTHER_ADDR = + {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf5}; + + @Test + public void testApfFilterKeepaliveAck() throws Exception { + final MockIpClientCallback cb = new MockIpClientCallback(); + final ApfConfiguration config = getDefaultConfig(); + config.multicastFilter = DROP_MULTICAST; + config.ieee802_3Filter = DROP_802_3_FRAMES; + final TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog); + byte[] program; + final int srcPort = 12345; + final int dstPort = 54321; + final int seqNum = 2123456789; + final int ackNum = 1234567890; + final int anotherSrcPort = 23456; + final int anotherDstPort = 65432; + final int anotherSeqNum = 2123456780; + final int anotherAckNum = 1123456789; + final int slot1 = 1; + final int slot2 = 2; + final int window = 14480; + final int windowScale = 4; + + // src: 10.0.0.5, port: 12345 + // dst: 10.0.0.6, port: 54321 + InetAddress srcAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_SRC_ADDR); + InetAddress dstAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_DST_ADDR); + + final TcpSocketInfo v4Tsi = new TcpSocketInfo( + srcAddr, srcPort, dstAddr, dstPort, seqNum, ackNum, window, windowScale); + final TcpKeepalivePacketData ipv4TcpKeepalivePacket = + TcpKeepalivePacketData.tcpKeepalivePacket(v4Tsi); + + apfFilter.addKeepalivePacketFilter(slot1, ipv4TcpKeepalivePacket.toStableParcelable()); + program = cb.getApfProgram(); + + // Verify IPv4 keepalive ack packet is dropped + // src: 10.0.0.6, port: 54321 + // dst: 10.0.0.5, port: 12345 + assertDrop(program, + ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */)); + // Verify IPv4 non-keepalive ack packet from the same source address is passed + assertPass(program, + ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, ackNum + 100, seqNum, 0 /* dataLength */)); + assertPass(program, + ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, ackNum, seqNum + 1, 10 /* dataLength */)); + // Verify IPv4 packet from another address is passed + assertPass(program, + ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort, + anotherDstPort, anotherSeqNum, anotherAckNum)); + + // Remove IPv4 keepalive filter + apfFilter.removeKeepalivePacketFilter(slot1); + + try { + // src: 2404:0:0:0:0:0:faf1, port: 12345 + // dst: 2404:0:0:0:0:0:faf2, port: 54321 + srcAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_SRC_ADDR); + dstAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_DST_ADDR); + final TcpSocketInfo v6Tsi = new TcpSocketInfo( + srcAddr, srcPort, dstAddr, dstPort, seqNum, ackNum, window, windowScale); + final TcpKeepalivePacketData ipv6TcpKeepalivePacket = + TcpKeepalivePacketData.tcpKeepalivePacket(v6Tsi); + apfFilter.addKeepalivePacketFilter(slot1, ipv6TcpKeepalivePacket.toStableParcelable()); + program = cb.getApfProgram(); + + // Verify IPv6 keepalive ack packet is dropped + // src: 2404:0:0:0:0:0:faf2, port: 54321 + // dst: 2404:0:0:0:0:0:faf1, port: 12345 + assertDrop(program, + ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, ackNum, seqNum + 1)); + // Verify IPv6 non-keepalive ack packet from the same source address is passed + assertPass(program, + ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, ackNum + 100, seqNum)); + // Verify IPv6 packet from another address is passed + assertPass(program, + ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, anotherSrcPort, + anotherDstPort, anotherSeqNum, anotherAckNum)); + + // Remove IPv6 keepalive filter + apfFilter.removeKeepalivePacketFilter(slot1); + + // Verify multiple filters + apfFilter.addKeepalivePacketFilter(slot1, ipv4TcpKeepalivePacket.toStableParcelable()); + apfFilter.addKeepalivePacketFilter(slot2, ipv6TcpKeepalivePacket.toStableParcelable()); + program = cb.getApfProgram(); + + // Verify IPv4 keepalive ack packet is dropped + // src: 10.0.0.6, port: 54321 + // dst: 10.0.0.5, port: 12345 + assertDrop(program, + ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, ackNum, seqNum + 1)); + // Verify IPv4 non-keepalive ack packet from the same source address is passed + assertPass(program, + ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, ackNum + 100, seqNum)); + // Verify IPv4 packet from another address is passed + assertPass(program, + ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort, + anotherDstPort, anotherSeqNum, anotherAckNum)); + + // Verify IPv6 keepalive ack packet is dropped + // src: 2404:0:0:0:0:0:faf2, port: 54321 + // dst: 2404:0:0:0:0:0:faf1, port: 12345 + assertDrop(program, + ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, ackNum, seqNum + 1)); + // Verify IPv6 non-keepalive ack packet from the same source address is passed + assertPass(program, + ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, ackNum + 100, seqNum)); + // Verify IPv6 packet from another address is passed + assertPass(program, + ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, anotherSrcPort, + anotherDstPort, anotherSeqNum, anotherAckNum)); + + // Remove keepalive filters + apfFilter.removeKeepalivePacketFilter(slot1); + apfFilter.removeKeepalivePacketFilter(slot2); + } catch (SocketKeepalive.InvalidPacketException e) { + // TODO: support V6 packets + } + + program = cb.getApfProgram(); + + // Verify IPv4, IPv6 packets are passed + assertPass(program, + ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, ackNum, seqNum + 1)); + assertPass(program, + ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, ackNum, seqNum + 1)); + assertPass(program, + ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, srcPort, + dstPort, anotherSeqNum, anotherAckNum)); + assertPass(program, + ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, srcPort, + dstPort, anotherSeqNum, anotherAckNum)); + + apfFilter.shutdown(); + } + + private static byte[] ipv4Packet(byte[] sip, byte[] tip, int sport, + int dport, int seq, int ack) { + ByteBuffer packet = ByteBuffer.wrap(new byte[100]); + packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IP); + packet.put(IPV4_VERSION_IHL_OFFSET, (byte) 0x45); + put(packet, IPV4_SRC_ADDR_OFFSET, sip); + put(packet, IPV4_DEST_ADDR_OFFSET, tip); + packet.putShort(IPV4_TCP_SRC_PORT_OFFSET, (short) sport); + packet.putShort(IPV4_TCP_DEST_PORT_OFFSET, (short) dport); + packet.putInt(IPV4_TCP_SEQ_NUM_OFFSET, seq); + packet.putInt(IPV4_TCP_ACK_NUM_OFFSET, ack); + return packet.array(); + } + + private static byte[] ipv4Packet(byte[] sip, byte[] tip, int sport, + int dport, int seq, int ack, int dataLength) { + final int totalLength = dataLength + IPV4_HEADER_LEN + IPV4_TCP_HEADER_LEN; + + ByteBuffer packet = ByteBuffer.wrap(ipv4Packet(sip, tip, sport, dport, seq, ack)); + packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength); + // TCP header length 5, reserved 3 bits, NS=0 + packet.put(IPV4_TCP_HEADER_LENGTH_OFFSET, (byte) 0x50); + return packet.array(); + } + + private static byte[] ipv6Packet(byte[] sip, byte[] tip, int sport, + int dport, int seq, int ack) { + ByteBuffer packet = ByteBuffer.wrap(new byte[100]); + packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IPV6); + put(packet, IPV6_SRC_ADDR_OFFSET, sip); + put(packet, IPV6_DEST_ADDR_OFFSET, tip); + packet.putShort(IPV6_TCP_SRC_PORT_OFFSET, (short) sport); + packet.putShort(IPV6_TCP_DEST_PORT_OFFSET, (short) dport); + packet.putInt(IPV6_TCP_SEQ_NUM_OFFSET, seq); + packet.putInt(IPV6_TCP_ACK_NUM_OFFSET, ack); + return packet.array(); + } + // Verify that the last program pushed to the IpClient.Callback properly filters the // given packet for the given lifetime. private void verifyRaLifetime(byte[] program, ByteBuffer packet, int lifetime) { diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java index d11bb64213c3..b98b0f798fe3 100644 --- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java +++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java @@ -482,7 +482,7 @@ public class NetworkMonitorTest { nm.notifyNetworkConnected(); verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .showProvisioningNotification(any()); + .showProvisioningNotification(any(), any()); // Check that startCaptivePortalApp sends the expected intent. nm.launchCaptivePortalApp(); diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index 4791517d9273..0cae894e89a6 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -22,4 +22,20 @@ android_library { } +// NOTE: Keep this module in sync with ./common.mk +java_defaults { + name: "SettingsLibDefaults", + static_libs: [ + "androidx.annotation_annotation", + "androidx.lifecycle_lifecycle-common", + "androidx.legacy_legacy-support-v4", + "androidx.lifecycle_lifecycle-runtime", + "androidx.recyclerview_recyclerview", + "androidx.preference_preference", + "androidx.appcompat_appcompat", + "androidx.legacy_legacy-preference-v14", + "SettingsLib", + ], +} + // For the test package. diff --git a/packages/SettingsLib/common.mk b/packages/SettingsLib/common.mk index 834b83b49ada..8c309ff97370 100644 --- a/packages/SettingsLib/common.mk +++ b/packages/SettingsLib/common.mk @@ -12,6 +12,11 @@ # # include frameworks/base/packages/SettingsLib/common.mk # +# During the conversion to Soong bluprint files, the equivalent +# functionality is provided by adding +# defaults: ["SettingsLibDefaults"], +# to the corresponding module. +# NOTE: keep this file and ./Android.bp in sync. LOCAL_STATIC_JAVA_LIBRARIES += \ androidx.annotation_annotation \ diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 9f3401ea7463..6cd2f45213e3 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -16,6 +16,7 @@ package com.android.settingslib.bluetooth; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHearingAid; @@ -28,7 +29,7 @@ import android.os.ParcelUuid; import android.os.SystemClock; import android.text.TextUtils; import android.util.Log; -import android.bluetooth.BluetoothAdapter; + import androidx.annotation.VisibleForTesting; import com.android.settingslib.R; @@ -205,7 +206,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> // This is to ensure all the profiles are disconnected as some CK/Hs do not // disconnect PBAP connection when HF connection is brought down PbapServerProfile PbapProfile = mProfileManager.getPbapProfile(); - if (PbapProfile.getConnectionStatus(mDevice) == BluetoothProfile.STATE_CONNECTED) + if (PbapProfile != null && isConnectedProfile(PbapProfile)) { PbapProfile.disconnect(mDevice); } diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 9306219415cd..b277666411f9 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -78,6 +78,7 @@ <uses-permission android:name="android.permission.MOVE_PACKAGE" /> <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" /> <uses-permission android:name="android.permission.CLEAR_APP_CACHE" /> + <uses-permission android:name="android.permission.ACCESS_INSTANT_APPS" /> <uses-permission android:name="android.permission.DELETE_CACHE_FILES" /> <uses-permission android:name="android.permission.DELETE_PACKAGES" /> <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" /> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index be46d2c7e4ae..be7403a1ab87 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -99,7 +99,6 @@ public class CarStatusBar extends StatusBar implements Log.d(TAG, "Connecting to HVAC service"); Dependency.get(HvacController.class).connectToCarService(); } - mCarFacetButtonController = Dependency.get(CarFacetButtonController.class); mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned(); if (!mDeviceIsProvisioned) { @@ -117,7 +116,7 @@ public class CarStatusBar extends StatusBar implements /** * Remove all content from navbars and rebuild them. Used to allow for different nav bars - * before and after the device is provisioned + * before and after the device is provisioned. Also for change of density and font size. */ private void restartNavBars() { mCarFacetButtonController.removeAll(); @@ -216,6 +215,7 @@ public class CarStatusBar extends StatusBar implements protected void makeStatusBarView() { super.makeStatusBarView(); + mCarFacetButtonController = Dependency.get(CarFacetButtonController.class); mNotificationPanelBackground = getDefaultWallpaper(); mScrimController.setScrimBehindDrawable(mNotificationPanelBackground); @@ -513,6 +513,7 @@ public class CarStatusBar extends StatusBar implements @Override public void onDensityOrFontScaleChanged() { super.onDensityOrFontScaleChanged(); + restartNavBars(); // Need to update the background on density changed in case the change was due to night // mode. mNotificationPanelBackground = getDefaultWallpaper(); diff --git a/sax/tests/saxtests/Android.bp b/sax/tests/saxtests/Android.bp new file mode 100644 index 000000000000..5889f769a645 --- /dev/null +++ b/sax/tests/saxtests/Android.bp @@ -0,0 +1,11 @@ +android_test { + name: "FrameworksSaxTests", + // Include all test java files. + srcs: ["src/**/*.java"], + libs: [ + "android.test.runner", + "android.test.base", + ], + static_libs: ["junit"], + platform_apis: true, +} diff --git a/sax/tests/saxtests/Android.mk b/sax/tests/saxtests/Android.mk deleted file mode 100644 index c4517a9a954a..000000000000 --- a/sax/tests/saxtests/Android.mk +++ /dev/null @@ -1,16 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -# We only want this apk build for tests. -LOCAL_MODULE_TAGS := tests - -# Include all test java files. -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base -LOCAL_STATIC_JAVA_LIBRARIES := junit -LOCAL_PACKAGE_NAME := FrameworksSaxTests -LOCAL_PRIVATE_PLATFORM_APIS := true - -include $(BUILD_PACKAGE) - diff --git a/services/accessibility/OWNERS b/services/accessibility/OWNERS new file mode 100644 index 000000000000..265674a74b7e --- /dev/null +++ b/services/accessibility/OWNERS @@ -0,0 +1,3 @@ +svetoslavganov@google.com +pweaver@google.com +rhedjao@google.com diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index b98d7a194dfe..af2bbfbb1dc3 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -38,7 +38,6 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkPolicyManager.RULE_NONE; import static android.net.NetworkPolicyManager.uidRulesToString; -import static android.net.NetworkStack.NETWORKSTACK_PACKAGE_NAME; import static android.net.shared.NetworkMonitorUtils.isValidationRequired; import static android.net.shared.NetworkParcelableUtil.toStableParcelable; import static android.os.Process.INVALID_UID; @@ -1832,14 +1831,20 @@ public class ConnectivityService extends IConnectivityManager.Stub "ConnectivityService"); } - private void enforceAnyPermissionOf(String... permissions) { + private boolean checkAnyPermissionOf(String... permissions) { for (String permission : permissions) { if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) { - return; + return true; } } - throw new SecurityException( - "Requires one of the following permissions: " + String.join(", ", permissions) + "."); + return false; + } + + private void enforceAnyPermissionOf(String... permissions) { + if (!checkAnyPermissionOf(permissions)) { + throw new SecurityException("Requires one of the following permissions: " + + String.join(", ", permissions) + "."); + } } private void enforceInternetPermission() { @@ -1859,19 +1864,22 @@ public class ConnectivityService extends IConnectivityManager.Stub } private void enforceSettingsPermission() { - mContext.enforceCallingOrSelfPermission( + enforceAnyPermissionOf( android.Manifest.permission.NETWORK_SETTINGS, - "ConnectivityService"); + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); } private boolean checkSettingsPermission() { - return PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission( - android.Manifest.permission.NETWORK_SETTINGS); + return checkAnyPermissionOf( + android.Manifest.permission.NETWORK_SETTINGS, + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); } private boolean checkSettingsPermission(int pid, int uid) { return PERMISSION_GRANTED == mContext.checkPermission( - android.Manifest.permission.NETWORK_SETTINGS, pid, uid); + android.Manifest.permission.NETWORK_SETTINGS, pid, uid) + || PERMISSION_GRANTED == mContext.checkPermission( + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, pid, uid); } private void enforceTetherAccessPermission() { @@ -1881,9 +1889,9 @@ public class ConnectivityService extends IConnectivityManager.Stub } private void enforceConnectivityInternalPermission() { - mContext.enforceCallingOrSelfPermission( + enforceAnyPermissionOf( android.Manifest.permission.CONNECTIVITY_INTERNAL, - "ConnectivityService"); + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); } private void enforceControlAlwaysOnVpnPermission() { @@ -1894,20 +1902,16 @@ public class ConnectivityService extends IConnectivityManager.Stub private void enforceNetworkStackSettingsOrSetup() { enforceAnyPermissionOf( - android.Manifest.permission.NETWORK_SETTINGS, - android.Manifest.permission.NETWORK_SETUP_WIZARD, - android.Manifest.permission.NETWORK_STACK); - } - - private void enforceNetworkStackPermission() { - mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, - "ConnectivityService"); + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); } private boolean checkNetworkStackPermission() { - return PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission( - android.Manifest.permission.NETWORK_STACK); + return checkAnyPermissionOf( + android.Manifest.permission.NETWORK_STACK, + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); } private void enforceConnectivityRestrictedNetworksPermission() { @@ -2661,9 +2665,9 @@ public class ConnectivityService extends IConnectivityManager.Stub } @Override - public void showProvisioningNotification(String action) { + public void showProvisioningNotification(String action, String packageName) { final Intent intent = new Intent(action); - intent.setPackage(NETWORKSTACK_PACKAGE_NAME); + intent.setPackage(packageName); final PendingIntent pendingIntent; // Only the system server can register notifications with package "android" @@ -3230,6 +3234,25 @@ public class ConnectivityService extends IConnectivityManager.Stub }); } + /** + * NetworkStack endpoint to start the captive portal app. The NetworkStack needs to use this + * endpoint as it does not have INTERACT_ACROSS_USERS_FULL itself. + * @param appExtras Bundle to use as intent extras for the captive portal application. + * Must be treated as opaque to avoid preventing the captive portal app to + * update its arguments. + */ + @Override + public void startCaptivePortalAppInternal(Bundle appExtras) { + mContext.checkCallingOrSelfPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); + + final Intent appIntent = new Intent(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN); + appIntent.putExtras(appExtras); + appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK); + + Binder.withCleanCallingIdentity(() -> + mContext.startActivityAsUser(appIntent, UserHandle.CURRENT)); + } + public boolean avoidBadWifi() { return mMultinetworkPolicyTracker.getAvoidBadWifi(); } @@ -6329,6 +6352,14 @@ public class ConnectivityService extends IConnectivityManager.Stub } @Override + public void startTcpKeepalive(Network network, FileDescriptor fd, int intervalSeconds, + Messenger messenger, IBinder binder) { + enforceKeepalivePermission(); + mKeepaliveTracker.startTcpKeepalive( + getNetworkAgentInfoForNetwork(network), fd, intervalSeconds, messenger, binder); + } + + @Override public void stopKeepalive(Network network, int slot) { mHandler.sendMessage(mHandler.obtainMessage( NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE, slot, SocketKeepalive.SUCCESS, network)); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 8e80c744b75b..06d1ca632363 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -365,6 +365,7 @@ import android.provider.Downloads; import android.provider.Settings; import android.service.voice.IVoiceInteractionSession; import android.service.voice.VoiceInteractionManagerInternal; +import android.sysprop.DisplayProperties; import android.sysprop.VoldProperties; import android.telecom.TelecomManager; import android.text.TextUtils; @@ -7435,6 +7436,7 @@ public class ActivityManagerService extends IActivityManager.Stub // next app record if we are emulating process with anonymous threads. ProcessRecord app; long startTime = SystemClock.uptimeMillis(); + long bindApplicationTimeMillis; if (pid != MY_PID && pid >= 0) { synchronized (mPidsSelfLocked) { app = mPidsSelfLocked.get(pid); @@ -7665,6 +7667,7 @@ public class ActivityManagerService extends IActivityManager.Stub } checkTime(startTime, "attachApplicationLocked: immediately before bindApplication"); + bindApplicationTimeMillis = SystemClock.elapsedRealtime(); mStackSupervisor.getActivityMetricsLogger().notifyBindApplication(app); if (app.isolatedEntryPoint != null) { // This is an isolated process which should just call an entry point instead of @@ -7783,6 +7786,18 @@ public class ActivityManagerService extends IActivityManager.Stub checkTime(startTime, "attachApplicationLocked: after updateOomAdjLocked"); } + StatsLog.write( + StatsLog.PROCESS_START_TIME, + app.info.uid, + app.pid, + app.info.packageName, + StatsLog.PROCESS_START_TIME__TYPE__COLD, + app.startTime, + (int) (bindApplicationTimeMillis - app.startTime), + (int) (SystemClock.elapsedRealtime() - app.startTime), + app.hostingType, + (app.hostingNameStr != null ? app.hostingNameStr : "")); + return true; } @@ -14937,8 +14952,8 @@ public class ActivityManagerService extends IActivityManager.Stub mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK_ONLY); mHiddenApiBlacklist.registerObserver(); - // Transfer any global setting for forcing RTL layout, into a System Property - SystemProperties.set(DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0"); + // Transfer any global setting for forcing RTL layout, into a Display Property + DisplayProperties.debug_force_rtl(forceRtl); final Configuration configuration = new Configuration(); Settings.System.getConfiguration(resolver, configuration); diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java index 07e28f9fb543..6cff57d4bbb1 100644 --- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java +++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java @@ -17,6 +17,8 @@ package com.android.server.connectivity; import static android.net.NattSocketKeepalive.NATT_PORT; +import static android.net.NetworkAgent.CMD_ADD_KEEPALIVE_PACKET_FILTER; +import static android.net.NetworkAgent.CMD_REMOVE_KEEPALIVE_PACKET_FILTER; import static android.net.NetworkAgent.CMD_START_SOCKET_KEEPALIVE; import static android.net.NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE; import static android.net.NetworkAgent.EVENT_SOCKET_KEEPALIVE; @@ -37,6 +39,9 @@ import android.net.NattKeepalivePacketData; import android.net.NetworkAgent; import android.net.NetworkUtils; import android.net.SocketKeepalive.InvalidPacketException; +import android.net.SocketKeepalive.InvalidSocketException; +import android.net.TcpKeepalivePacketData; +import android.net.TcpKeepalivePacketData.TcpSocketInfo; import android.net.util.IpUtils; import android.os.Binder; import android.os.Handler; @@ -65,7 +70,7 @@ import java.util.HashMap; * * Provides methods to stop and start keepalive requests, and keeps track of keepalives across all * networks. This class is tightly coupled to ConnectivityService. It is not thread-safe and its - * methods must be called only from the ConnectivityService handler thread. + * handle* methods must be called only from the ConnectivityService handler thread. */ public class KeepaliveTracker { @@ -78,9 +83,12 @@ public class KeepaliveTracker { private final HashMap <NetworkAgentInfo, HashMap<Integer, KeepaliveInfo>> mKeepalives = new HashMap<> (); private final Handler mConnectivityServiceHandler; + @NonNull + private final TcpKeepaliveController mTcpController; public KeepaliveTracker(Handler handler) { mConnectivityServiceHandler = handler; + mTcpController = new TcpKeepaliveController(handler); } /** @@ -96,20 +104,33 @@ public class KeepaliveTracker { private final int mUid; private final int mPid; private final NetworkAgentInfo mNai; + private final int mType; + private final FileDescriptor mFd; - /** Keepalive slot. A small integer that identifies this keepalive among the ones handled - * by this network. */ + public static final int TYPE_NATT = 1; + public static final int TYPE_TCP = 2; + + // Keepalive slot. A small integer that identifies this keepalive among the ones handled + // by this network. private int mSlot = NO_KEEPALIVE; // Packet data. private final KeepalivePacketData mPacket; private final int mInterval; - // Whether the keepalive is started or not. - public boolean isStarted; - - public KeepaliveInfo(Messenger messenger, IBinder binder, NetworkAgentInfo nai, - KeepalivePacketData packet, int interval) { + // Whether the keepalive is started or not. The initial state is NOT_STARTED. + private static final int NOT_STARTED = 1; + private static final int STARTING = 2; + private static final int STARTED = 3; + private int mStartedState = NOT_STARTED; + + KeepaliveInfo(@NonNull Messenger messenger, + @NonNull IBinder binder, + @NonNull NetworkAgentInfo nai, + @NonNull KeepalivePacketData packet, + int interval, + int type, + @NonNull FileDescriptor fd) { mMessenger = messenger; mBinder = binder; mPid = Binder.getCallingPid(); @@ -118,6 +139,8 @@ public class KeepaliveTracker { mNai = nai; mPacket = packet; mInterval = interval; + mType = type; + mFd = fd; try { mBinder.linkToDeath(this, 0); @@ -130,32 +153,40 @@ public class KeepaliveTracker { return mNai; } + private String startedStateString(final int state) { + switch (state) { + case NOT_STARTED : return "NOT_STARTED"; + case STARTING : return "STARTING"; + case STARTED : return "STARTED"; + } + throw new IllegalArgumentException("Unknown state"); + } + public String toString() { - return new StringBuffer("KeepaliveInfo [") - .append(" network=").append(mNai.network) - .append(" isStarted=").append(isStarted) - .append(" ") - .append(IpUtils.addressAndPortToString(mPacket.srcAddress, mPacket.srcPort)) - .append("->") - .append(IpUtils.addressAndPortToString(mPacket.dstAddress, mPacket.dstPort)) - .append(" interval=" + mInterval) - .append(" packetData=" + HexDump.toHexString(mPacket.getPacket())) - .append(" uid=").append(mUid).append(" pid=").append(mPid) - .append(" ]") - .toString(); + return "KeepaliveInfo [" + + " network=" + mNai.network + + " startedState=" + startedStateString(mStartedState) + + " " + + IpUtils.addressAndPortToString(mPacket.srcAddress, mPacket.srcPort) + + "->" + + IpUtils.addressAndPortToString(mPacket.dstAddress, mPacket.dstPort) + + " interval=" + mInterval + + " uid=" + mUid + " pid=" + mPid + + " packetData=" + HexDump.toHexString(mPacket.getPacket()) + + " ]"; } /** Sends a message back to the application via its SocketKeepalive.Callback. */ void notifyMessenger(int slot, int err) { + if (DBG) { + Log.d(TAG, "notify keepalive " + mSlot + " on " + mNai.network + " for " + err); + } KeepaliveTracker.this.notifyMessenger(mMessenger, slot, err); } /** Called when the application process is killed. */ public void binderDied() { - // Not called from ConnectivityService handler thread, so send it a message. - mConnectivityServiceHandler.obtainMessage( - NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE, - mSlot, BINDER_DIED, mNai.network).sendToTarget(); + stop(BINDER_DIED); } void unlinkDeathRecipient() { @@ -202,7 +233,26 @@ public class KeepaliveTracker { int error = isValid(); if (error == SUCCESS) { Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.name()); - mNai.asyncChannel.sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket); + switch (mType) { + case TYPE_NATT: + mNai.asyncChannel + .sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket); + break; + case TYPE_TCP: + mTcpController.startSocketMonitor(mFd, this, mSlot); + mNai.asyncChannel + .sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0 /* Unused */, + mPacket); + // TODO: check result from apf and notify of failure as needed. + mNai.asyncChannel + .sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket); + break; + default: + Log.wtf(TAG, "Starting keepalive with unknown type: " + mType); + handleStopKeepalive(mNai, mSlot, error); + return; + } + mStartedState = STARTING; } else { handleStopKeepalive(mNai, mSlot, error); return; @@ -216,15 +266,27 @@ public class KeepaliveTracker { Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network); } } - if (isStarted) { + if (NOT_STARTED != mStartedState) { Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.name()); - mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot); + if (mType == TYPE_NATT) { + mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot); + } else if (mType == TYPE_TCP) { + mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot); + mNai.asyncChannel.sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER, mSlot); + mTcpController.stopSocketMonitor(mSlot); + } else { + Log.wtf(TAG, "Stopping keepalive with unknown type: " + mType); + } } // TODO: at the moment we unconditionally return failure here. In cases where the // NetworkAgent is alive, should we ask it to reply, so it can return failure? notifyMessenger(mSlot, reason); unlinkDeathRecipient(); } + + void onFileDescriptorInitiatedStop(final int socketKeepaliveReason) { + handleStopKeepalive(mNai, mSlot, socketKeepaliveReason); + } } void notifyMessenger(Messenger messenger, int slot, int err) { @@ -328,20 +390,38 @@ public class KeepaliveTracker { return; } - if (reason == SUCCESS && !ki.isStarted) { + // This can be called in a number of situations : + // - startedState is STARTING. + // - reason is SUCCESS => go to STARTED. + // - reason isn't SUCCESS => it's an error starting. Go to NOT_STARTED and stop keepalive. + // - startedState is STARTED. + // - reason is SUCCESS => it's a success stopping. Go to NOT_STARTED and stop keepalive. + // - reason isn't SUCCESS => it's an error in exec. Go to NOT_STARTED and stop keepalive. + // The control is not supposed to ever come here if the state is NOT_STARTED. This is + // because in NOT_STARTED state, the code will switch to STARTING before sending messages + // to start, and the only way to NOT_STARTED is this function, through the edges outlined + // above : in all cases, keepalive gets stopped and can't restart without going into + // STARTING as messages are ordered. This also depends on the hardware processing the + // messages in order. + // TODO : clarify this code and get rid of mStartedState. Using a StateMachine is an + // option. + if (reason == SUCCESS && KeepaliveInfo.STARTING == ki.mStartedState) { // Keepalive successfully started. if (DBG) Log.d(TAG, "Started keepalive " + slot + " on " + nai.name()); - ki.isStarted = true; + ki.mStartedState = KeepaliveInfo.STARTED; ki.notifyMessenger(slot, reason); } else { // Keepalive successfully stopped, or error. - ki.isStarted = false; + ki.mStartedState = KeepaliveInfo.NOT_STARTED; if (reason == SUCCESS) { + // The message indicated success stopping : don't call handleStopKeepalive. if (DBG) Log.d(TAG, "Successfully stopped keepalive " + slot + " on " + nai.name()); } else { + // The message indicated some error trying to start or during the course of + // keepalive : do call handleStopKeepalive. + handleStopKeepalive(nai, slot, reason); if (DBG) Log.d(TAG, "Keepalive " + slot + " on " + nai.name() + " error " + reason); } - handleStopKeepalive(nai, slot, reason); } } @@ -379,10 +459,49 @@ public class KeepaliveTracker { notifyMessenger(messenger, NO_KEEPALIVE, e.error); return; } - KeepaliveInfo ki = new KeepaliveInfo(messenger, binder, nai, packet, intervalSeconds); - Log.d(TAG, "Created keepalive: " + ki.toString()); + KeepaliveInfo ki = new KeepaliveInfo(messenger, binder, nai, packet, intervalSeconds, + KeepaliveInfo.TYPE_NATT, null); mConnectivityServiceHandler.obtainMessage( - CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget(); + NetworkAgent.CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget(); + } + + /** + * Called by ConnectivityService to start TCP keepalive on a file descriptor. + * + * In order to offload keepalive for application correctly, sequence number, ack number and + * other fields are needed to form the keepalive packet. Thus, this function synchronously + * puts the socket into repair mode to get the necessary information. After the socket has been + * put into repair mode, the application cannot access the socket until reverted to normal. + * + * See {@link android.net.SocketKeepalive}. + **/ + public void startTcpKeepalive(@Nullable NetworkAgentInfo nai, + @NonNull FileDescriptor fd, + int intervalSeconds, + @NonNull Messenger messenger, + @NonNull IBinder binder) { + if (nai == null) { + notifyMessenger(messenger, NO_KEEPALIVE, ERROR_INVALID_NETWORK); + return; + } + + TcpKeepalivePacketData packet = null; + try { + TcpSocketInfo tsi = TcpKeepaliveController.switchToRepairMode(fd); + packet = TcpKeepalivePacketData.tcpKeepalivePacket(tsi); + } catch (InvalidPacketException | InvalidSocketException e) { + try { + TcpKeepaliveController.switchOutOfRepairMode(fd); + } catch (ErrnoException e1) { + Log.e(TAG, "Couldn't move fd out of repair mode after failure to start keepalive"); + } + notifyMessenger(messenger, NO_KEEPALIVE, e.error); + return; + } + KeepaliveInfo ki = new KeepaliveInfo(messenger, binder, nai, packet, intervalSeconds, + KeepaliveInfo.TYPE_TCP, fd); + Log.d(TAG, "Created keepalive: " + ki.toString()); + mConnectivityServiceHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget(); } /** diff --git a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java new file mode 100644 index 000000000000..8a9ac23cf06a --- /dev/null +++ b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java @@ -0,0 +1,316 @@ +/* + * 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.connectivity; + +import static android.net.SocketKeepalive.DATA_RECEIVED; +import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET; +import static android.net.SocketKeepalive.ERROR_SOCKET_NOT_IDLE; +import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR; +import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT; +import static android.system.OsConstants.FIONREAD; +import static android.system.OsConstants.IPPROTO_TCP; +import static android.system.OsConstants.TIOCOUTQ; + +import android.annotation.NonNull; +import android.net.NetworkUtils; +import android.net.SocketKeepalive.InvalidSocketException; +import android.net.TcpKeepalivePacketData.TcpSocketInfo; +import android.net.TcpRepairWindow; +import android.os.Handler; +import android.os.MessageQueue; +import android.os.Messenger; +import android.system.ErrnoException; +import android.system.Int32Ref; +import android.system.Os; +import android.util.Log; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.connectivity.KeepaliveTracker.KeepaliveInfo; + +import java.io.FileDescriptor; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.SocketException; + +/** + * Manage tcp socket which offloads tcp keepalive. + * + * The input socket will be changed to repair mode and the application + * will not have permission to read/write data. If the application wants + * to write data, it must stop tcp keepalive offload to leave repair mode + * first. If a remote packet arrives, repair mode will be turned off and + * offload will be stopped. The application will receive a callback to know + * it can start reading data. + * + * {start,stop}SocketMonitor are thread-safe, but care must be taken in the + * order in which they are called. Please note that while calling + * {@link #startSocketMonitor(FileDescriptor, Messenger, int)} multiple times + * with either the same slot or the same FileDescriptor without stopping it in + * between will result in an exception, calling {@link #stopSocketMonitor(int)} + * multiple times with the same int is explicitly a no-op. + * Please also note that switching the socket to repair mode is not synchronized + * with either of these operations and has to be done in an orderly fashion + * with stopSocketMonitor. Take care in calling these in the right order. + * @hide + */ +public class TcpKeepaliveController { + private static final String TAG = "TcpKeepaliveController"; + private static final boolean DBG = false; + + private final MessageQueue mFdHandlerQueue; + + private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR; + + // Reference include/uapi/linux/tcp.h + private static final int TCP_REPAIR = 19; + private static final int TCP_REPAIR_QUEUE = 20; + private static final int TCP_QUEUE_SEQ = 21; + private static final int TCP_NO_QUEUE = 0; + private static final int TCP_RECV_QUEUE = 1; + private static final int TCP_SEND_QUEUE = 2; + private static final int TCP_REPAIR_OFF = 0; + private static final int TCP_REPAIR_ON = 1; + // Reference include/uapi/linux/sockios.h + private static final int SIOCINQ = FIONREAD; + private static final int SIOCOUTQ = TIOCOUTQ; + + /** + * Keeps track of packet listeners. + * Key: slot number of keepalive offload. + * Value: {@link FileDescriptor} being listened to. + */ + @GuardedBy("mListeners") + private final SparseArray<FileDescriptor> mListeners = new SparseArray<>(); + + public TcpKeepaliveController(final Handler connectivityServiceHandler) { + mFdHandlerQueue = connectivityServiceHandler.getLooper().getQueue(); + } + + /** + * Switch the tcp socket to repair mode and query tcp socket information. + * + * @param fd the fd of socket on which to use keepalive offload + * @return a {@link TcpKeepalivePacketData#TcpSocketInfo} object for current + * tcp/ip information. + */ + // TODO : make this private. It's far too confusing that this gets called from outside + // at a time that nobody can understand. + public static TcpSocketInfo switchToRepairMode(FileDescriptor fd) + throws InvalidSocketException { + if (DBG) Log.i(TAG, "switchToRepairMode to start tcp keepalive : " + fd); + final SocketAddress srcSockAddr; + final SocketAddress dstSockAddr; + final InetAddress srcAddress; + final InetAddress dstAddress; + final int srcPort; + final int dstPort; + int seq; + final int ack; + final TcpRepairWindow trw; + + // Query source address and port. + try { + srcSockAddr = Os.getsockname(fd); + } catch (ErrnoException e) { + Log.e(TAG, "Get sockname fail: ", e); + throw new InvalidSocketException(ERROR_INVALID_SOCKET, e); + } + if (srcSockAddr instanceof InetSocketAddress) { + srcAddress = getAddress((InetSocketAddress) srcSockAddr); + srcPort = getPort((InetSocketAddress) srcSockAddr); + } else { + Log.e(TAG, "Invalid or mismatched SocketAddress"); + throw new InvalidSocketException(ERROR_INVALID_SOCKET); + } + // Query destination address and port. + try { + dstSockAddr = Os.getpeername(fd); + } catch (ErrnoException e) { + Log.e(TAG, "Get peername fail: ", e); + throw new InvalidSocketException(ERROR_INVALID_SOCKET, e); + } + if (dstSockAddr instanceof InetSocketAddress) { + dstAddress = getAddress((InetSocketAddress) dstSockAddr); + dstPort = getPort((InetSocketAddress) dstSockAddr); + } else { + Log.e(TAG, "Invalid or mismatched peer SocketAddress"); + throw new InvalidSocketException(ERROR_INVALID_SOCKET); + } + + // Query sequence and ack number + dropAllIncomingPackets(fd, true); + try { + // Enter tcp repair mode. + Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR, TCP_REPAIR_ON); + // Check if socket is idle. + if (!isSocketIdle(fd)) { + throw new InvalidSocketException(ERROR_SOCKET_NOT_IDLE); + } + // Query write sequence number from SEND_QUEUE. + Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR_QUEUE, TCP_SEND_QUEUE); + seq = Os.getsockoptInt(fd, IPPROTO_TCP, TCP_QUEUE_SEQ); + // Query read sequence number from RECV_QUEUE. + Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR_QUEUE, TCP_RECV_QUEUE); + ack = Os.getsockoptInt(fd, IPPROTO_TCP, TCP_QUEUE_SEQ); + // Switch to NO_QUEUE to prevent illegal socket read/write in repair mode. + Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR_QUEUE, TCP_NO_QUEUE); + // Finally, check if socket is still idle. TODO : this check needs to move to + // after starting polling to prevent a race. + if (!isSocketIdle(fd)) { + throw new InvalidSocketException(ERROR_INVALID_SOCKET); + } + + // Query tcp window size. + trw = NetworkUtils.getTcpRepairWindow(fd); + } catch (ErrnoException e) { + Log.e(TAG, "Exception reading TCP state from socket", e); + try { + Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR, TCP_REPAIR_OFF); + } catch (ErrnoException ex) { + Log.e(TAG, "Exception while turning off repair mode due to exception", ex); + } + throw new InvalidSocketException(ERROR_INVALID_SOCKET, e); + } finally { + dropAllIncomingPackets(fd, false); + } + + // Keepalive sequence number is last sequence number - 1. If it couldn't be retrieved, + // then it must be set to -1, so decrement in all cases. + seq = seq - 1; + + return new TcpSocketInfo(srcAddress, srcPort, dstAddress, dstPort, seq, ack, trw.rcvWnd, + trw.rcvWndScale); + } + + /** + * Switch the tcp socket out of repair mode. + * + * @param fd the fd of socket to switch back to normal. + */ + // TODO : make this private. + public static void switchOutOfRepairMode(@NonNull final FileDescriptor fd) + throws ErrnoException { + Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR, TCP_REPAIR_OFF); + } + + /** + * Start monitoring incoming packets. + * + * @param fd socket fd to monitor. + * @param messenger a callback to notify socket status. + * @param slot keepalive slot. + */ + public void startSocketMonitor(@NonNull final FileDescriptor fd, + @NonNull final KeepaliveInfo ki, final int slot) { + synchronized (mListeners) { + if (null != mListeners.get(slot)) { + throw new IllegalArgumentException("This slot is already taken"); + } + for (int i = 0; i < mListeners.size(); ++i) { + if (fd.equals(mListeners.valueAt(i))) { + throw new IllegalArgumentException("This fd is already registered"); + } + } + mFdHandlerQueue.addOnFileDescriptorEventListener(fd, FD_EVENTS, (readyFd, events) -> { + // This can't be called twice because the queue guarantees that once the listener + // is unregistered it can't be called again, even for a message that arrived + // before it was unregistered. + final int reason; + if (0 != (events & EVENT_ERROR)) { + reason = ERROR_INVALID_SOCKET; + } else { + reason = DATA_RECEIVED; + } + ki.onFileDescriptorInitiatedStop(reason); + // The listener returns the new set of events to listen to. Because 0 means no + // event, the listener gets unregistered. + return 0; + }); + mListeners.put(slot, fd); + } + } + + /** Stop socket monitor */ + // This slot may have been stopped automatically already because the socket received data, + // was closed on the other end or otherwise suffered some error. In this case, this function + // is a no-op. + public void stopSocketMonitor(final int slot) { + final FileDescriptor fd; + synchronized (mListeners) { + fd = mListeners.get(slot); + if (null == fd) return; + mListeners.remove(slot); + } + mFdHandlerQueue.removeOnFileDescriptorEventListener(fd); + try { + if (DBG) Log.d(TAG, "Moving socket out of repair mode for stop : " + fd); + switchOutOfRepairMode(fd); + } catch (ErrnoException e) { + Log.e(TAG, "Cannot switch socket out of repair mode", e); + // Well, there is not much to do here to recover + } + } + + private static InetAddress getAddress(InetSocketAddress inetAddr) { + return inetAddr.getAddress(); + } + + private static int getPort(InetSocketAddress inetAddr) { + return inetAddr.getPort(); + } + + private static boolean isSocketIdle(FileDescriptor fd) throws ErrnoException { + return isReceiveQueueEmpty(fd) && isSendQueueEmpty(fd); + } + + private static boolean isReceiveQueueEmpty(FileDescriptor fd) + throws ErrnoException { + Int32Ref result = new Int32Ref(-1); + Os.ioctlInt(fd, SIOCINQ, result); + if (result.value != 0) { + Log.e(TAG, "Read queue has data"); + return false; + } + return true; + } + + private static boolean isSendQueueEmpty(FileDescriptor fd) + throws ErrnoException { + Int32Ref result = new Int32Ref(-1); + Os.ioctlInt(fd, SIOCOUTQ, result); + if (result.value != 0) { + Log.e(TAG, "Write queue has data"); + return false; + } + return true; + } + + private static void dropAllIncomingPackets(FileDescriptor fd, boolean enable) + throws InvalidSocketException { + try { + if (enable) { + NetworkUtils.attachDropAllBPFFilter(fd); + } else { + NetworkUtils.detachBPFFilter(fd); + } + } catch (SocketException e) { + Log.e(TAG, "Socket Exception: ", e); + throw new InvalidSocketException(ERROR_INVALID_SOCKET, e); + } + } +} diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index a14fd17209e8..19bdc0969e6d 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -96,6 +96,7 @@ import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; +import com.android.internal.telephony.TelephonyIntents; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.MessageUtils; @@ -180,6 +181,7 @@ public class Tethering extends BaseNetworkObserver { // into a single coherent structure. private final HashSet<IpServer> mForwardedDownstreams; private final VersionedBroadcastListener mCarrierConfigChange; + private final VersionedBroadcastListener mDefaultSubscriptionChange; private final TetheringDependencies mDeps; private final EntitlementManager mEntitlementMgr; @@ -232,6 +234,15 @@ public class Tethering extends BaseNetworkObserver { mEntitlementMgr.reevaluateSimCardProvisioning(); }); + filter = new IntentFilter(); + filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED); + mDefaultSubscriptionChange = new VersionedBroadcastListener( + "DefaultSubscriptionChangeListener", mContext, smHandler, filter, + (Intent ignored) -> { + mLog.log("OBSERVED default data subscription change"); + updateConfiguration(); + mEntitlementMgr.reevaluateSimCardProvisioning(); + }); mStateReceiver = new StateReceiver(); // Load tethering configuration. @@ -242,6 +253,7 @@ public class Tethering extends BaseNetworkObserver { private void startStateMachineUpdaters() { mCarrierConfigChange.startListening(); + mDefaultSubscriptionChange.startListening(); final Handler handler = mTetherMasterSM.getHandler(); IntentFilter filter = new IntentFilter(); @@ -270,7 +282,8 @@ public class Tethering extends BaseNetworkObserver { // NOTE: This is always invoked on the mLooper thread. private void updateConfiguration() { - mConfig = new TetheringConfiguration(mContext, mLog); + final int subId = mDeps.getDefaultDataSubscriptionId(); + mConfig = new TetheringConfiguration(mContext, mLog, subId); mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired); mEntitlementMgr.updateConfiguration(mConfig); } diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java index 1e6bb04858a1..8a46ff18979f 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java @@ -26,8 +26,8 @@ import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER; import static com.android.internal.R.array.config_mobile_hotspot_provision_app; import static com.android.internal.R.array.config_tether_bluetooth_regexs; import static com.android.internal.R.array.config_tether_dhcp_range; -import static com.android.internal.R.array.config_tether_usb_regexs; import static com.android.internal.R.array.config_tether_upstream_types; +import static com.android.internal.R.array.config_tether_usb_regexs; import static com.android.internal.R.array.config_tether_wifi_regexs; import static com.android.internal.R.bool.config_tether_upstream_automatic; import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui; @@ -38,6 +38,7 @@ import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.util.SharedLog; import android.provider.Settings; +import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -100,29 +101,34 @@ public class TetheringConfiguration { public final String[] provisioningApp; public final String provisioningAppNoUi; - public TetheringConfiguration(Context ctx, SharedLog log) { + public final int subId; + + public TetheringConfiguration(Context ctx, SharedLog log, int id) { final SharedLog configLog = log.forSubComponent("config"); - tetherableUsbRegexs = getResourceStringArray(ctx, config_tether_usb_regexs); + subId = id; + Resources res = getResources(ctx, subId); + + tetherableUsbRegexs = getResourceStringArray(res, config_tether_usb_regexs); // TODO: Evaluate deleting this altogether now that Wi-Fi always passes // us an interface name. Careful consideration needs to be given to // implications for Settings and for provisioning checks. - tetherableWifiRegexs = getResourceStringArray(ctx, config_tether_wifi_regexs); - tetherableBluetoothRegexs = getResourceStringArray(ctx, config_tether_bluetooth_regexs); + tetherableWifiRegexs = getResourceStringArray(res, config_tether_wifi_regexs); + tetherableBluetoothRegexs = getResourceStringArray(res, config_tether_bluetooth_regexs); dunCheck = checkDunRequired(ctx); configLog.log("DUN check returned: " + dunCheckString(dunCheck)); - chooseUpstreamAutomatically = getResourceBoolean(ctx, config_tether_upstream_automatic); - preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(ctx, dunCheck); + chooseUpstreamAutomatically = getResourceBoolean(res, config_tether_upstream_automatic); + preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, dunCheck); isDunRequired = preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN); - legacyDhcpRanges = getLegacyDhcpRanges(ctx); + legacyDhcpRanges = getLegacyDhcpRanges(res); defaultIPv4DNS = copy(DEFAULT_IPV4_DNS); enableLegacyDhcpServer = getEnableLegacyDhcpServer(ctx); - provisioningApp = getResourceStringArray(ctx, config_mobile_hotspot_provision_app); - provisioningAppNoUi = getProvisioningAppNoUi(ctx); + provisioningApp = getResourceStringArray(res, config_mobile_hotspot_provision_app); + provisioningAppNoUi = getProvisioningAppNoUi(res); configLog.log(toString()); } @@ -144,6 +150,9 @@ public class TetheringConfiguration { } public void dump(PrintWriter pw) { + pw.print("subId: "); + pw.println(subId); + dumpStringArray(pw, "tetherableUsbRegexs", tetherableUsbRegexs); dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs); dumpStringArray(pw, "tetherableBluetoothRegexs", tetherableBluetoothRegexs); @@ -169,6 +178,7 @@ public class TetheringConfiguration { public String toString() { final StringJoiner sj = new StringJoiner(" "); + sj.add(String.format("subId:%d", subId)); sj.add(String.format("tetherableUsbRegexs:%s", makeString(tetherableUsbRegexs))); sj.add(String.format("tetherableWifiRegexs:%s", makeString(tetherableWifiRegexs))); sj.add(String.format("tetherableBluetoothRegexs:%s", @@ -235,8 +245,8 @@ public class TetheringConfiguration { } } - private static Collection<Integer> getUpstreamIfaceTypes(Context ctx, int dunCheck) { - final int ifaceTypes[] = ctx.getResources().getIntArray(config_tether_upstream_types); + private static Collection<Integer> getUpstreamIfaceTypes(Resources res, int dunCheck) { + final int[] ifaceTypes = res.getIntArray(config_tether_upstream_types); final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length); for (int i : ifaceTypes) { switch (i) { @@ -286,33 +296,33 @@ public class TetheringConfiguration { return false; } - private static String[] getLegacyDhcpRanges(Context ctx) { - final String[] fromResource = getResourceStringArray(ctx, config_tether_dhcp_range); + private static String[] getLegacyDhcpRanges(Resources res) { + final String[] fromResource = getResourceStringArray(res, config_tether_dhcp_range); if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) { return fromResource; } return copy(LEGACY_DHCP_DEFAULT_RANGE); } - private static String getProvisioningAppNoUi(Context ctx) { + private static String getProvisioningAppNoUi(Resources res) { try { - return ctx.getResources().getString(config_mobile_hotspot_provision_app_no_ui); + return res.getString(config_mobile_hotspot_provision_app_no_ui); } catch (Resources.NotFoundException e) { return ""; } } - private static boolean getResourceBoolean(Context ctx, int resId) { + private static boolean getResourceBoolean(Resources res, int resId) { try { - return ctx.getResources().getBoolean(resId); + return res.getBoolean(resId); } catch (Resources.NotFoundException e404) { return false; } } - private static String[] getResourceStringArray(Context ctx, int resId) { + private static String[] getResourceStringArray(Resources res, int resId) { try { - final String[] strArray = ctx.getResources().getStringArray(resId); + final String[] strArray = res.getStringArray(resId); return (strArray != null) ? strArray : EMPTY_STRING_ARRAY; } catch (Resources.NotFoundException e404) { return EMPTY_STRING_ARRAY; @@ -325,6 +335,19 @@ public class TetheringConfiguration { return intVal != 0; } + private Resources getResources(Context ctx, int subId) { + if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + return getResourcesForSubIdWrapper(ctx, subId); + } else { + return ctx.getResources(); + } + } + + @VisibleForTesting + protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) { + return SubscriptionManager.getResourcesForSubId(ctx, subId); + } + private static String[] copy(String[] strarray) { return Arrays.copyOf(strarray, strarray.length); } diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java index 6d6f81eb98e6..3fddac111ec5 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java @@ -21,6 +21,7 @@ import android.net.NetworkRequest; import android.net.ip.IpServer; import android.net.util.SharedLog; import android.os.Handler; +import android.telephony.SubscriptionManager; import com.android.internal.util.StateMachine; import com.android.server.connectivity.MockableSystemProperties; @@ -85,4 +86,11 @@ public class TetheringDependencies { SharedLog log, MockableSystemProperties systemProperties) { return new EntitlementManager(ctx, target, log, systemProperties); } + + /** + * Get default data subscription id to build TetheringConfiguration. + */ + public int getDefaultDataSubscriptionId() { + return SubscriptionManager.getDefaultDataSubscriptionId(); + } } diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java index a8be07d76b58..08dea7f577a1 100644 --- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java +++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java @@ -480,8 +480,10 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { final String apkPath = pkg.baseCodePath; final ApplicationInfo appInfo = pkg.applicationInfo; final String outDexFile = appInfo.dataDir + "/code_cache/compiled_view.dex"; - if (appInfo.isPrivilegedApp()) { + if (appInfo.isPrivilegedApp() || appInfo.isDefaultToDeviceProtectedStorage()) { // Privileged apps prefer to load trusted code so they don't use compiled views. + // Also disable the view compiler for protected storage apps since there are + // selinux permissions required for writing to user_de. return false; } Log.i("PackageManager", "Compiling layouts in " + packageName + " (" + apkPath + diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java deleted file mode 100644 index 5f71b0b3a59a..000000000000 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java +++ /dev/null @@ -1,67 +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 com.android.server.timezonedetector; - -import com.android.internal.util.DumpUtils; -import com.android.server.SystemService; -import android.app.timezonedetector.ITimeZoneDetectorService; -import android.content.Context; -import android.util.Slog; -import java.io.FileDescriptor; -import java.io.PrintWriter; - -public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub { - private static final String TAG = "timezonedetector.TimeZoneDetectorService"; - - public static class Lifecycle extends SystemService { - - public Lifecycle(Context context) { - super(context); - } - - @Override - public void onStart() { - TimeZoneDetectorService service = TimeZoneDetectorService.create(getContext()); - // Publish the binder service so it can be accessed from other (appropriately - // permissioned) processes. - publishBinderService(Context.TIME_ZONE_DETECTOR_SERVICE, service); - } - } - - private final Context mContext; - - private static TimeZoneDetectorService create(Context context) { - return new TimeZoneDetectorService(context); - } - - public TimeZoneDetectorService(Context context) { - mContext = context; - } - - @Override - public void stubbedCall() { - // Empty call for initial tests. - Slog.d(TAG, "stubbedCall() called"); - // TODO: Remove when there are real methods. - } - - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; - // TODO: Implement when there is state. - } -} diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index d5e59c8dfd6a..c30babd464f4 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -128,6 +128,8 @@ public final class TvInputManagerService extends SystemService { private final WatchLogHandler mWatchLogHandler; + private IBinder.DeathRecipient mDeathRecipient; + public TvInputManagerService(Context context) { super(context); @@ -672,6 +674,7 @@ public final class TvInputManagerService extends SystemService { if (sessionToken == userState.mainSessionToken) { setMainLocked(sessionToken, false, callingUid, userId); } + sessionState.session.asBinder().unlinkToDeath(sessionState, 0); sessionState.session.release(); } } catch (RemoteException | SessionNotFoundException e) { @@ -707,6 +710,7 @@ public final class TvInputManagerService extends SystemService { clientState.sessionTokens.remove(sessionToken); if (clientState.isEmpty()) { userState.clientStateMap.remove(sessionState.client.asBinder()); + sessionState.client.asBinder().unlinkToDeath(clientState, 0); } } @@ -1000,17 +1004,19 @@ public final class TvInputManagerService extends SystemService { synchronized (mLock) { final UserState userState = getOrCreateUserStateLocked(resolvedUserId); userState.callbackSet.add(callback); - try { - callback.asBinder().linkToDeath(new IBinder.DeathRecipient() { - @Override - public void binderDied() { - synchronized (mLock) { - if (userState.callbackSet != null) { - userState.callbackSet.remove(callback); - } + mDeathRecipient = new IBinder.DeathRecipient() { + @Override + public void binderDied() { + synchronized (mLock) { + if (userState.callbackSet != null) { + userState.callbackSet.remove(callback); } } - }, 0); + } + }; + + try { + callback.asBinder().linkToDeath(mDeathRecipient, 0); } catch (RemoteException e) { Slog.e(TAG, "client process has already died", e); } @@ -1029,6 +1035,7 @@ public final class TvInputManagerService extends SystemService { synchronized (mLock) { UserState userState = getOrCreateUserStateLocked(resolvedUserId); userState.callbackSet.remove(callback); + callback.asBinder().unlinkToDeath(mDeathRecipient, 0); } } finally { Binder.restoreCallingIdentity(identity); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 75cd82e4e012..2338fffbf26a 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -238,8 +238,6 @@ public final class SystemServer { "com.android.internal.car.CarServiceHelperService"; private static final String TIME_DETECTOR_SERVICE_CLASS = "com.android.server.timedetector.TimeDetectorService$Lifecycle"; - private static final String TIME_ZONE_DETECTOR_SERVICE_CLASS = - "com.android.server.timezonedetector.TimeZoneDetectorService$Lifecycle"; private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst"; @@ -1316,14 +1314,6 @@ public final class SystemServer { reportWtf("starting StartTimeDetectorService service", e); } traceEnd(); - - traceBeginAndSlog("StartTimeZoneDetectorService"); - try { - mSystemServiceManager.startService(TIME_ZONE_DETECTOR_SERVICE_CLASS); - } catch (Throwable e) { - reportWtf("starting StartTimeZoneDetectorService service", e); - } - traceEnd(); } if (!isWatch) { diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java deleted file mode 100644 index 19d31cfafc35..000000000000 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java +++ /dev/null @@ -1,45 +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 com.android.server.timezonedetector; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Unit tests for the {@link TimeZoneDetectorService}. - */ -@RunWith(AndroidJUnit4.class) -public class TimeZoneDetectorServiceTest { - - private TimeZoneDetectorService mTimeZoneDetectorService; - - @Before - public void setUp() { - final Context context = InstrumentationRegistry.getContext(); - mTimeZoneDetectorService = new TimeZoneDetectorService(context); - } - - @Test - public void testStubbedCall() { - mTimeZoneDetectorService.stubbedCall(); - } -} diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 7ea61e30dafe..da682c6df621 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -69,6 +69,7 @@ import android.os.storage.StorageVolume; import android.provider.Settings; import android.service.usb.UsbDeviceManagerProto; import android.service.usb.UsbHandlerProto; +import android.sysprop.AdbProperties; import android.sysprop.VoldProperties; import android.util.Pair; import android.util.Slog; @@ -285,7 +286,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver } mControlFds.put(UsbManager.FUNCTION_PTP, ptpFd); - boolean secureAdbEnabled = SystemProperties.getBoolean("ro.adb.secure", false); + boolean secureAdbEnabled = AdbProperties.secure().orElse(false); boolean dataEncrypted = "1".equals(VoldProperties.decrypt().orElse("")); if (secureAdbEnabled && !dataEncrypted) { mDebuggingManager = new UsbDebuggingManager(context); diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp index 37caeb2044ff..f5b4308a1b50 100644 --- a/startop/view_compiler/Android.bp +++ b/startop/view_compiler/Android.bp @@ -58,8 +58,6 @@ cc_library_static { "util.cc", "layout_validation.cc", ], - // b/123880763, clang-tidy analyzer has segmentation fault with dex_builder.cc - tidy_checks: ["-clang-analyzer-*"], host_supported: true, } diff --git a/startop/view_compiler/OWNERS b/startop/view_compiler/OWNERS new file mode 100644 index 000000000000..e5aead9ddac8 --- /dev/null +++ b/startop/view_compiler/OWNERS @@ -0,0 +1,2 @@ +eholk@google.com +mathieuc@google.com diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc index 4c1a0dc7f749..6047e8c74e38 100644 --- a/startop/view_compiler/dex_builder.cc +++ b/startop/view_compiler/dex_builder.cc @@ -426,7 +426,7 @@ void MethodBuilder::EncodeInvoke(const Instruction& instruction, ::art::Instruct // Some of the registers don't fit in the four bit short form of the invoke // instruction, so we need to do an invoke/range. To do this, we need to // first move all the arguments into contiguous temporary registers. - std::array<Value, kMaxArgs> scratch{GetScratchRegisters<kMaxArgs>()}; + std::array<Value, kMaxArgs> scratch = GetScratchRegisters<kMaxArgs>(); const auto& prototype = dex_->GetPrototypeByMethodId(instruction.method_id()); CHECK(prototype.has_value()); diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java index 826ad82dfbb2..818ebd998f50 100644 --- a/telecomm/java/android/telecom/CallScreeningService.java +++ b/telecomm/java/android/telecom/CallScreeningService.java @@ -16,6 +16,7 @@ package android.telecom; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SdkConstant; import android.app.Service; @@ -32,6 +33,9 @@ import com.android.internal.os.SomeArgs; import com.android.internal.telecom.ICallScreeningAdapter; import com.android.internal.telecom.ICallScreeningService; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * This service can be implemented by the default dialer (see * {@link TelecomManager#getDefaultDialerPackage()}) or a third party app to allow or disallow @@ -88,6 +92,128 @@ import com.android.internal.telecom.ICallScreeningService; * </pre> */ public abstract class CallScreeningService extends Service { + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef( + prefix = { "CALL_DURATION_" }, + value = {CALL_DURATION_VERY_SHORT, CALL_DURATION_SHORT, CALL_DURATION_MEDIUM, + CALL_DURATION_LONG}) + public @interface CallDuration {} + + /** + * Call duration reported with {@link #EXTRA_CALL_DURATION} to indicate to the + * {@link CallScreeningService} the duration of a call for which the user reported the nuisance + * status (see {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}). The + * {@link CallScreeningService} can use this as a signal for training nuisance detection + * algorithms. The call duration is reported in coarse grained buckets to minimize exposure of + * identifying call log information to the {@link CallScreeningService}. + * <p> + * Indicates the call was < 3 seconds in duration. + */ + public static final int CALL_DURATION_VERY_SHORT = 1; + + /** + * Call duration reported with {@link #EXTRA_CALL_DURATION} to indicate to the + * {@link CallScreeningService} the duration of a call for which the user reported the nuisance + * status (see {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}). The + * {@link CallScreeningService} can use this as a signal for training nuisance detection + * algorithms. The call duration is reported in coarse grained buckets to minimize exposure of + * identifying call log information to the {@link CallScreeningService}. + * <p> + * Indicates the call was greater than 3 seconds, but less than 60 seconds in duration. + */ + public static final int CALL_DURATION_SHORT = 2; + + /** + * Call duration reported with {@link #EXTRA_CALL_DURATION} to indicate to the + * {@link CallScreeningService} the duration of a call for which the user reported the nuisance + * status (see {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}). The + * {@link CallScreeningService} can use this as a signal for training nuisance detection + * algorithms. The call duration is reported in coarse grained buckets to minimize exposure of + * identifying call log information to the {@link CallScreeningService}. + * <p> + * Indicates the call was greater than 60 seconds, but less than 120 seconds in duration. + */ + public static final int CALL_DURATION_MEDIUM = 3; + + /** + * Call duration reported with {@link #EXTRA_CALL_DURATION} to indicate to the + * {@link CallScreeningService} the duration of a call for which the user reported the nuisance + * status (see {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}). The + * {@link CallScreeningService} can use this as a signal for training nuisance detection + * algorithms. The call duration is reported in coarse grained buckets to minimize exposure of + * identifying call log information to the {@link CallScreeningService}. + * <p> + * Indicates the call was greater than 120 seconds. + */ + public static final int CALL_DURATION_LONG = 4; + + /** + * Telecom sends this intent to the {@link CallScreeningService} which the user has chosen to + * fill the call screening role when the user indicates through the default dialer whether a + * call is a nuisance call or not (see + * {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}). + * <p> + * The following extra values are provided for the call: + * <ol> + * <li>{@link #EXTRA_CALL_HANDLE} - the handle of the call.</li> + * <li>{@link #EXTRA_IS_NUISANCE} - {@code true} if the user reported the call as a nuisance + * call, {@code false} otherwise.</li> + * <li>{@link #EXTRA_CALL_TYPE} - reports the type of call (incoming, rejected, missed, + * blocked).</li> + * <li>{@link #EXTRA_CALL_DURATION} - the duration of the call (see + * {@link #EXTRA_CALL_DURATION} for valid values).</li> + * </ol> + * <p> + * {@link CallScreeningService} implementations which want to track whether the user reports + * calls are nuisance calls should use declare a broadcast receiver in their manifest for this + * intent. + * <p> + * Note: Only {@link CallScreeningService} implementations which have provided + * {@link CallIdentification} information for calls at some point will receive this intent. + */ + public static final String ACTION_NUISANCE_CALL_STATUS_CHANGED = + "android.telecom.action.NUISANCE_CALL_STATUS_CHANGED"; + + /** + * Extra used to provide the handle of the call for + * {@link #ACTION_NUISANCE_CALL_STATUS_CHANGED}. The call handle is reported as a + * {@link Uri}. + */ + public static final String EXTRA_CALL_HANDLE = "android.telecom.extra.CALL_HANDLE"; + + /** + * Boolean extra used to indicate whether the user reported a call as a nuisance call. + * When {@code true}, the user reported that a call was a nuisance call, {@code false} + * otherwise. Sent with {@link #ACTION_NUISANCE_CALL_STATUS_CHANGED}. + */ + public static final String EXTRA_IS_NUISANCE = "android.telecom.extra.IS_NUISANCE"; + + /** + * Integer extra used with {@link #ACTION_NUISANCE_CALL_STATUS_CHANGED} to report the type of + * call. Valid values are: + * <UL> + * <li>{@link android.provider.CallLog.Calls#MISSED_TYPE}</li> + * <li>{@link android.provider.CallLog.Calls#INCOMING_TYPE}</li> + * <li>{@link android.provider.CallLog.Calls#BLOCKED_TYPE}</li> + * <li>{@link android.provider.CallLog.Calls#REJECTED_TYPE}</li> + * </UL> + */ + public static final String EXTRA_CALL_TYPE = "android.telecom.extra.CALL_TYPE"; + + /** + * Integer extra used to with {@link #ACTION_NUISANCE_CALL_STATUS_CHANGED} to report how long + * the call lasted. Valid values are: + * <UL> + * <LI>{@link #CALL_DURATION_VERY_SHORT}</LI> + * <LI>{@link #CALL_DURATION_SHORT}</LI> + * <LI>{@link #CALL_DURATION_MEDIUM}</LI> + * <LI>{@link #CALL_DURATION_LONG}</LI> + * </UL> + */ + public static final String EXTRA_CALL_DURATION = "android.telecom.extra.CALL_DURATION"; + /** * The {@link Intent} that must be declared as handled by the service. */ diff --git a/telecomm/java/android/telecom/ConferenceParticipant.java b/telecomm/java/android/telecom/ConferenceParticipant.java index 20b04ebed6a4..6317770676cd 100644 --- a/telecomm/java/android/telecom/ConferenceParticipant.java +++ b/telecomm/java/android/telecom/ConferenceParticipant.java @@ -19,6 +19,10 @@ package android.telecom; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; +import android.text.TextUtils; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.telephony.PhoneConstants; /** * Parcelable representation of a participant's state in a conference call. @@ -27,6 +31,11 @@ import android.os.Parcelable; public class ConferenceParticipant implements Parcelable { /** + * RFC5767 states that a SIP URI with an unknown number should use an address of + * {@code anonymous@anonymous.invalid}. E.g. the host name is anonymous.invalid. + */ + private static final String ANONYMOUS_INVALID_HOST = "anonymous.invalid"; + /** * The conference participant's handle (e.g., phone number). */ private final Uri mHandle; @@ -50,6 +59,16 @@ public class ConferenceParticipant implements Parcelable { private final int mState; /** + * The connect time of the participant. + */ + private long mConnectTime; + + /** + * The connect elapsed time of the participant. + */ + private long mConnectElapsedTime; + + /** * Creates an instance of {@code ConferenceParticipant}. * * @param handle The conference participant's handle (e.g., phone number). @@ -92,6 +111,54 @@ public class ConferenceParticipant implements Parcelable { } /** + * Determines the number presentation for a conference participant. Per RFC5767, if the host + * name contains {@code anonymous.invalid} we can assume that there is no valid caller ID + * information for the caller, otherwise we'll assume that the URI can be shown. + * + * @return The number presentation. + */ + @VisibleForTesting + public int getParticipantPresentation() { + Uri address = getHandle(); + if (address == null) { + return PhoneConstants.PRESENTATION_RESTRICTED; + } + + String number = address.getSchemeSpecificPart(); + // If no number, bail early and set restricted presentation. + if (TextUtils.isEmpty(number)) { + return PhoneConstants.PRESENTATION_RESTRICTED; + } + // Per RFC3261, the host name portion can also potentially include extra information: + // E.g. sip:anonymous1@anonymous.invalid;legid=1 + // In this case, hostName will be anonymous.invalid and there is an extra parameter for + // legid=1. + // Parameters are optional, and the address (e.g. test@test.com) will always be the first + // part, with any parameters coming afterwards. + String [] hostParts = number.split("[;]"); + String addressPart = hostParts[0]; + + // Get the number portion from the address part. + // This will typically be formatted similar to: 6505551212@test.com + String [] numberParts = addressPart.split("[@]"); + + // If we can't parse the host name out of the URI, then there is probably other data + // present, and is likely a valid SIP URI. + if (numberParts.length != 2) { + return PhoneConstants.PRESENTATION_ALLOWED; + } + String hostName = numberParts[1]; + + // If the hostname portion of the SIP URI is the invalid host string, presentation is + // restricted. + if (hostName.equals(ANONYMOUS_INVALID_HOST)) { + return PhoneConstants.PRESENTATION_RESTRICTED; + } + + return PhoneConstants.PRESENTATION_ALLOWED; + } + + /** * Writes the {@code ConferenceParticipant} to a parcel. * * @param dest The Parcel in which the object should be written. @@ -121,6 +188,10 @@ public class ConferenceParticipant implements Parcelable { sb.append(Log.pii(mEndpoint)); sb.append(" State: "); sb.append(Connection.stateToString(mState)); + sb.append(" ConnectTime: "); + sb.append(getConnectTime()); + sb.append(" ConnectElapsedTime: "); + sb.append(getConnectElapsedTime()); sb.append("]"); return sb.toString(); } @@ -155,4 +226,26 @@ public class ConferenceParticipant implements Parcelable { public int getState() { return mState; } + + /** + * The connect time of the participant to the conference. + */ + public long getConnectTime() { + return mConnectTime; + } + + public void setConnectTime(long connectTime) { + this.mConnectTime = connectTime; + } + + /** + * The connect elpased time of the participant to the conference. + */ + public long getConnectElapsedTime() { + return mConnectElapsedTime; + } + + public void setConnectElapsedTime(long connectElapsedTime) { + mConnectElapsedTime = connectElapsedTime; + } } diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 05d5a13092f1..bd0d4ae27800 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -16,10 +16,6 @@ package android.telecom; -import com.android.internal.os.SomeArgs; -import com.android.internal.telecom.IVideoCallback; -import com.android.internal.telecom.IVideoProvider; - import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -43,6 +39,10 @@ import android.telephony.TelephonyManager; import android.util.ArraySet; import android.view.Surface; +import com.android.internal.os.SomeArgs; +import com.android.internal.telecom.IVideoCallback; +import com.android.internal.telecom.IVideoProvider; + import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java index 1de67a5883e3..5a97c948f31f 100644 --- a/telecomm/java/android/telecom/DisconnectCause.java +++ b/telecomm/java/android/telecom/DisconnectCause.java @@ -16,9 +16,9 @@ package android.telecom; +import android.media.ToneGenerator; import android.os.Parcel; import android.os.Parcelable; -import android.media.ToneGenerator; import android.text.TextUtils; import java.util.Objects; @@ -91,6 +91,12 @@ public final class DisconnectCause implements Parcelable { */ public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED"; + /** + * Reason code, which indicates that the conference call is simulating single party conference. + * @hide + */ + public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL"; + private int mDisconnectCode; private CharSequence mDisconnectLabel; private CharSequence mDisconnectDescription; diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 94ed7c30867b..268e70fe5d12 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -1976,6 +1976,33 @@ public class TelecomManager { } /** + * Called by the default dialer to report to Telecom when the user has marked a previous + * incoming call as a nuisance call or not. + * <p> + * Where the user has chosen a {@link CallScreeningService} to fill the call screening role, + * Telecom will notify that {@link CallScreeningService} of the user's report. + * <p> + * Requires that the caller is the default dialer app. + * + * @param handle The phone number of an incoming call which the user is reporting as either a + * nuisance of non-nuisance call. + * @param isNuisanceCall {@code true} if the user is reporting the call as a nuisance call, + * {@code false} if the user is reporting the call as a non-nuisance call. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public void reportNuisanceCallStatus(@NonNull Uri handle, boolean isNuisanceCall) { + ITelecomService service = getTelecomService(); + if (service != null) { + try { + service.reportNuisanceCallStatus(handle, isNuisanceCall, + mContext.getOpPackageName()); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelecomService#showCallScreen", e); + } + } + } + + /** * Handles {@link Intent#ACTION_CALL} intents trampolined from UserCallActivity. * @param intent The {@link Intent#ACTION_CALL} intent to handle. * @hide diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index e1d5c17d5e3a..5030f90afd3e 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -285,6 +285,8 @@ interface ITelecomService { */ boolean isInEmergencyCall(); + oneway void reportNuisanceCallStatus(in Uri address, boolean isNuisance, String callingPackage); + /** * @see TelecomServiceImpl#handleCallIntent */ @@ -299,4 +301,5 @@ interface ITelecomService { void addOrRemoveTestCallCompanionApp(String packageName, boolean isAdded); void setTestAutoModeApp(String packageName); + } diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java index fb8f3e78ff84..51c8f65a6c9d 100644 --- a/telephony/java/android/provider/Telephony.java +++ b/telephony/java/android/provider/Telephony.java @@ -2054,6 +2054,643 @@ public final class Telephony { } /** + * Columns for the "rcs_*" tables used by {@link android.telephony.ims.RcsMessageStore} classes. + * + * @hide - not meant for public use + */ + public interface RcsColumns { + /** + * The authority for the content provider + */ + String AUTHORITY = "rcs"; + + /** + * The URI to start building upon to use {@link com.android.providers.telephony.RcsProvider} + */ + Uri CONTENT_AND_AUTHORITY = Uri.parse("content://" + AUTHORITY); + + /** + * The value to be used whenever a transaction that expects an integer to be returned + * failed. + */ + int TRANSACTION_FAILED = Integer.MIN_VALUE; + + /** + * The value that denotes a timestamp was not set before (e.g. a message that is not + * delivered yet will not have a DELIVERED_TIMESTAMP) + */ + long TIMESTAMP_NOT_SET = 0; + + /** + * The table that {@link android.telephony.ims.RcsThread} gets persisted to + */ + interface RcsThreadColumns { + /** + * The path that should be used for referring to + * {@link android.telephony.ims.RcsThread}s in + * {@link com.android.providers.telephony.RcsProvider} URIs. + */ + String RCS_THREAD_URI_PART = "thread"; + + /** + * The URI to query or modify {@link android.telephony.ims.RcsThread} via the content + * provider. + */ + Uri RCS_THREAD_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY, RCS_THREAD_URI_PART); + + /** + * The unique identifier of an {@link android.telephony.ims.RcsThread} + */ + String RCS_THREAD_ID_COLUMN = "rcs_thread_id"; + } + + /** + * The table that {@link android.telephony.ims.Rcs1To1Thread} gets persisted to + */ + interface Rcs1To1ThreadColumns extends RcsThreadColumns { + /** + * The path that should be used for referring to + * {@link android.telephony.ims.Rcs1To1Thread}s in + * {@link com.android.providers.telephony.RcsProvider} URIs. + */ + String RCS_1_TO_1_THREAD_URI_PART = "p2p_thread"; + + /** + * The URI to query or modify {@link android.telephony.ims.Rcs1To1Thread}s via the + * content provider + */ + Uri RCS_1_TO_1_THREAD_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY, + RCS_1_TO_1_THREAD_URI_PART); + + /** + * The SMS/MMS thread to fallback to in case of an RCS outage + */ + String FALLBACK_THREAD_ID_COLUMN = "rcs_fallback_thread_id"; + } + + /** + * The table that {@link android.telephony.ims.RcsGroupThread} gets persisted to + */ + interface RcsGroupThreadColumns extends RcsThreadColumns { + /** + * The path that should be used for referring to + * {@link android.telephony.ims.RcsGroupThread}s in + * {@link com.android.providers.telephony.RcsProvider} URIs. + */ + String RCS_GROUP_THREAD_URI_PART = "group_thread"; + + /** + * The URI to query or modify {@link android.telephony.ims.RcsGroupThread}s via the + * content provider + */ + Uri RCS_GROUP_THREAD_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY, + RCS_GROUP_THREAD_URI_PART); + + /** + * The owner/admin of the {@link android.telephony.ims.RcsGroupThread} + */ + String OWNER_PARTICIPANT_COLUMN = "owner_participant"; + + /** + * The user visible name of the group + */ + String GROUP_NAME_COLUMN = "group_name"; + + /** + * The user visible icon of the group + */ + String GROUP_ICON_COLUMN = "group_icon"; + + /** + * The RCS conference URI for this group + */ + String CONFERENCE_URI_COLUMN = "conference_uri"; + } + + /** + * The view that enables polling from all types of RCS threads at once + */ + interface RcsUnifiedThreadColumns extends RcsThreadColumns, Rcs1To1ThreadColumns, + RcsGroupThreadColumns { + /** + * The type of this {@link android.telephony.ims.RcsThread} + */ + String THREAD_TYPE_COLUMN = "thread_type"; + + /** + * Integer returned as a result from a database query that denotes the thread is 1 to 1 + */ + int THREAD_TYPE_1_TO_1 = 0; + + /** + * Integer returned as a result from a database query that denotes the thread is 1 to 1 + */ + int THREAD_TYPE_GROUP = 1; + } + + /** + * The table that {@link android.telephony.ims.RcsParticipant} gets persisted to + */ + interface RcsParticipantColumns { + /** + * The path that should be used for referring to + * {@link android.telephony.ims.RcsParticipant}s in + * {@link com.android.providers.telephony.RcsProvider} URIs. + */ + String RCS_PARTICIPANT_URI_PART = "participant"; + + /** + * The URI to query or modify {@link android.telephony.ims.RcsParticipant}s via the + * content provider + */ + Uri RCS_PARTICIPANT_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY, + RCS_PARTICIPANT_URI_PART); + + /** + * The unique identifier of the entry in the database + */ + String RCS_PARTICIPANT_ID_COLUMN = "rcs_participant_id"; + + /** + * A foreign key on canonical_address table, also used by SMS/MMS + */ + String CANONICAL_ADDRESS_ID_COLUMN = "canonical_address_id"; + + /** + * The user visible RCS alias for this participant. + */ + String RCS_ALIAS_COLUMN = "rcs_alias"; + } + + /** + * Additional constants to enable access to {@link android.telephony.ims.RcsParticipant} + * related data + */ + interface RcsParticipantHelpers extends RcsParticipantColumns { + /** + * The view that unifies "rcs_participant" and "canonical_addresses" tables for easy + * access to participant address. + */ + String RCS_PARTICIPANT_WITH_ADDRESS_VIEW = "rcs_participant_with_address_view"; + + /** + * The view that unifies "rcs_participant", "canonical_addresses" and + * "rcs_thread_participant" junction table to get full information on participants that + * contribute to threads. + */ + String RCS_PARTICIPANT_WITH_THREAD_VIEW = "rcs_participant_with_thread_view"; + } + + /** + * The table that {@link android.telephony.ims.RcsMessage} gets persisted to + */ + interface RcsMessageColumns { + /** + * Denotes the type of this message (i.e. + * {@link android.telephony.ims.RcsIncomingMessage} or + * {@link android.telephony.ims.RcsOutgoingMessage} + */ + String MESSAGE_TYPE_COLUMN = "rcs_message_type"; + + /** + * The unique identifier for the message in the database - i.e. the primary key. + */ + String MESSAGE_ID_COLUMN = "rcs_message_row_id"; + + /** + * The globally unique RCS identifier for the message. Please see 4.4.5.2 - GSMA + * RCC.53 (RCS Device API 1.6 Specification) + */ + String GLOBAL_ID_COLUMN = "rcs_message_global_id"; + + /** + * The subscription where this message was sent from/to. + */ + String SUB_ID_COLUMN = "sub_id"; + + /** + * The sending status of the message. + * @see android.telephony.ims.RcsMessage.RcsMessageStatus + */ + String STATUS_COLUMN = "status"; + + /** + * The creation timestamp of the message. + */ + String ORIGINATION_TIMESTAMP_COLUMN = "origination_timestamp"; + + /** + * The text content of the message. + */ + String MESSAGE_TEXT_COLUMN = "rcs_text"; + + /** + * The latitude content of the message, if it contains a location. + */ + String LATITUDE_COLUMN = "latitude"; + + /** + * The longitude content of the message, if it contains a location. + */ + String LONGITUDE_COLUMN = "longitude"; + } + + /** + * The table that additional information of {@link android.telephony.ims.RcsIncomingMessage} + * gets persisted to. + */ + interface RcsIncomingMessageColumns extends RcsMessageColumns { + /** + The path that should be used for referring to + * {@link android.telephony.ims.RcsIncomingMessage}s in + * {@link com.android.providers.telephony.RcsProvider} URIs. + */ + String INCOMING_MESSAGE_URI_PART = "incoming_message"; + + /** + * The URI to query incoming messages through + * {@link com.android.providers.telephony.RcsProvider} + */ + Uri INCOMING_MESSAGE_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY, + INCOMING_MESSAGE_URI_PART); + + /** + * The ID of the {@link android.telephony.ims.RcsParticipant} that sent this message + */ + String SENDER_PARTICIPANT_ID_COLUMN = "sender_participant"; + + /** + * The timestamp of arrival for this message. + */ + String ARRIVAL_TIMESTAMP_COLUMN = "arrival_timestamp"; + + /** + * The time when the recipient has read this message. + */ + String SEEN_TIMESTAMP_COLUMN = "seen_timestamp"; + } + + /** + * The table that additional information of {@link android.telephony.ims.RcsOutgoingMessage} + * gets persisted to. + */ + interface RcsOutgoingMessageColumns extends RcsMessageColumns { + /** + * The path that should be used for referring to + * {@link android.telephony.ims.RcsOutgoingMessage}s in + * {@link com.android.providers.telephony.RcsProvider} URIs. + */ + String OUTGOING_MESSAGE_URI_PART = "outgoing_message"; + + /** + * The URI to query or modify {@link android.telephony.ims.RcsOutgoingMessage}s via the + * content provider + */ + Uri OUTGOING_MESSAGE_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY, + OUTGOING_MESSAGE_URI_PART); + } + + /** + * The delivery information of an {@link android.telephony.ims.RcsOutgoingMessage} + */ + interface RcsMessageDeliveryColumns extends RcsOutgoingMessageColumns { + /** + * The path that should be used for referring to + * {@link android.telephony.ims.RcsOutgoingMessageDelivery}s in + * {@link com.android.providers.telephony.RcsProvider} URIs. + */ + String DELIVERY_URI_PART = "delivery"; + + /** + * The timestamp of delivery of this message. + */ + String DELIVERED_TIMESTAMP_COLUMN = "delivered_timestamp"; + + /** + * The time when the recipient has read this message. + */ + String SEEN_TIMESTAMP_COLUMN = "seen_timestamp"; + } + + /** + * The views that allow querying {@link android.telephony.ims.RcsIncomingMessage} and + * {@link android.telephony.ims.RcsOutgoingMessage} at the same time. + */ + interface RcsUnifiedMessageColumns extends RcsIncomingMessageColumns, + RcsOutgoingMessageColumns { + /** + * The path that is used to query all {@link android.telephony.ims.RcsMessage} in + * {@link com.android.providers.telephony.RcsProvider} URIs. + */ + String UNIFIED_MESSAGE_URI_PART = "message"; + + /** + * The URI to query all types of {@link android.telephony.ims.RcsMessage}s + */ + Uri UNIFIED_MESSAGE_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY, + UNIFIED_MESSAGE_URI_PART); + + /** + * The name of the view that unites rcs_message and rcs_incoming_message tables. + */ + String UNIFIED_INCOMING_MESSAGE_VIEW = "unified_incoming_message_view"; + + /** + * The name of the view that unites rcs_message and rcs_outgoing_message tables. + */ + String UNIFIED_OUTGOING_MESSAGE_VIEW = "unified_outgoing_message_view"; + + /** + * The column that shows from which table the message entry came from. + */ + String MESSAGE_TYPE_COLUMN = "message_type"; + + /** + * Integer returned as a result from a database query that denotes that the message is + * an incoming message + */ + int MESSAGE_TYPE_INCOMING = 1; + + /** + * Integer returned as a result from a database query that denotes that the message is + * an outgoing message + */ + int MESSAGE_TYPE_OUTGOING = 0; + } + + /** + * The table that {@link android.telephony.ims.RcsFileTransferPart} gets persisted to. + */ + interface RcsFileTransferColumns { + /** + * The path that should be used for referring to + * {@link android.telephony.ims.RcsFileTransferPart}s in + * {@link com.android.providers.telephony.RcsProvider} URIs. + */ + String FILE_TRANSFER_URI_PART = "file_transfer"; + + /** + * The URI to query or modify {@link android.telephony.ims.RcsFileTransferPart}s via the + * content provider + */ + Uri FILE_TRANSFER_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY, + FILE_TRANSFER_URI_PART); + + /** + * The globally unique file transfer ID for this RCS file transfer. + */ + String FILE_TRANSFER_ID_COLUMN = "rcs_file_transfer_id"; + + /** + * The RCS session ID for this file transfer. The ID is implementation dependent but + * should be unique. + */ + String SESSION_ID_COLUMN = "session_id"; + + /** + * The URI that points to the content of this file transfer + */ + String CONTENT_URI_COLUMN = "content_uri"; + + /** + * The file type of this file transfer in bytes. The validity of types is not enforced + * in {@link android.telephony.ims.RcsMessageStore} APIs. + */ + String CONTENT_TYPE_COLUMN = "content_type"; + + /** + * The size of the file transfer in bytes. + */ + String FILE_SIZE_COLUMN = "file_size"; + + /** + * Number of bytes that was successfully transmitted for this file transfer + */ + String SUCCESSFULLY_TRANSFERRED_BYTES = "transfer_offset"; + + /** + * The status of this file transfer + * @see android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus + */ + String TRANSFER_STATUS_COLUMN = "transfer_status"; + + /** + * The on-screen width of the file transfer, if it contains multi-media + */ + String WIDTH_COLUMN = "width"; + + /** + * The on-screen height of the file transfer, if it contains multi-media + */ + String HEIGHT_COLUMN = "height"; + + /** + * The duration of the content in milliseconds if this file transfer contains + * multi-media + */ + String DURATION_MILLIS_COLUMN = "duration"; + + /** + * The URI to the preview of the content of this file transfer + */ + String PREVIEW_URI_COLUMN = "preview_uri"; + + /** + * The type of the preview of the content of this file transfer. The validity of types + * is not enforced in {@link android.telephony.ims.RcsMessageStore} APIs. + */ + String PREVIEW_TYPE_COLUMN = "preview_type"; + } + + /** + * The table that holds the information for + * {@link android.telephony.ims.RcsGroupThreadEvent} and its subclasses. + */ + interface RcsThreadEventColumns { + /** + * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to + * refer to participant joined events (example URI: + * {@code content://rcs/group_thread/3/participant_joined_event}) + */ + String PARTICIPANT_JOINED_URI_PART = "participant_joined_event"; + + /** + * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to + * refer to participant left events. (example URI: + * {@code content://rcs/group_thread/3/participant_left_event/4}) + */ + String PARTICIPANT_LEFT_URI_PART = "participant_left_event"; + + /** + * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to + * refer to name changed events. (example URI: + * {@code content://rcs/group_thread/3/name_changed_event}) + */ + String NAME_CHANGED_URI_PART = "name_changed_event"; + + /** + * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to + * refer to icon changed events. (example URI: + * {@code content://rcs/group_thread/3/icon_changed_event}) + */ + String ICON_CHANGED_URI_PART = "icon_changed_event"; + + /** + * The unique ID of this event in the database, i.e. the primary key + */ + String EVENT_ID_COLUMN = "event_id"; + + /** + * The type of this event + * + * @see RcsEventTypes + */ + String EVENT_TYPE_COLUMN = "event_type"; + + /** + * The timestamp in milliseconds of when this event happened + */ + String TIMESTAMP_COLUMN = "origination_timestamp"; + + /** + * The participant that generated this event + */ + String SOURCE_PARTICIPANT_ID_COLUMN = "source_participant"; + + /** + * The receiving participant of this event if this was an + * {@link android.telephony.ims.RcsGroupThreadParticipantJoinedEvent} or + * {@link android.telephony.ims.RcsGroupThreadParticipantLeftEvent} + */ + String DESTINATION_PARTICIPANT_ID_COLUMN = "destination_participant"; + + /** + * The URI for the new icon of the group thread if this was an + * {@link android.telephony.ims.RcsGroupThreadIconChangedEvent} + */ + String NEW_ICON_URI_COLUMN = "new_icon_uri"; + + /** + * The URI for the new name of the group thread if this was an + * {@link android.telephony.ims.RcsGroupThreadNameChangedEvent} + */ + String NEW_NAME_COLUMN = "new_name"; + } + + /** + * The table that {@link android.telephony.ims.RcsParticipantAliasChangedEvent} gets + * persisted to + */ + interface RcsParticipantEventColumns { + /** + * The path that should be used for referring to + * {@link android.telephony.ims.RcsParticipantAliasChangedEvent}s in + * {@link com.android.providers.telephony.RcsProvider} URIs. + */ + String ALIAS_CHANGE_EVENT_URI_PART = "alias_change_event"; + + /** + * The new alias of the participant + */ + String NEW_ALIAS_COLUMN = "new_alias"; + } + + /** + * These values are used in {@link com.android.providers.telephony.RcsProvider} to determine + * what kind of event is present in the storage. + */ + interface RcsEventTypes { + /** + * Integer constant that is stored in the + * {@link com.android.providers.telephony.RcsProvider} database that denotes the event + * is of type {@link android.telephony.ims.RcsParticipantAliasChangedEvent} + */ + int PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE = 1; + + /** + * Integer constant that is stored in the + * {@link com.android.providers.telephony.RcsProvider} database that denotes the event + * is of type {@link android.telephony.ims.RcsGroupThreadParticipantJoinedEvent} + */ + int PARTICIPANT_JOINED_EVENT_TYPE = 2; + + /** + * Integer constant that is stored in the + * {@link com.android.providers.telephony.RcsProvider} database that denotes the event + * is of type {@link android.telephony.ims.RcsGroupThreadParticipantLeftEvent} + */ + int PARTICIPANT_LEFT_EVENT_TYPE = 4; + + /** + * Integer constant that is stored in the + * {@link com.android.providers.telephony.RcsProvider} database that denotes the event + * is of type {@link android.telephony.ims.RcsGroupThreadIconChangedEvent} + */ + int ICON_CHANGED_EVENT_TYPE = 8; + + /** + * Integer constant that is stored in the + * {@link com.android.providers.telephony.RcsProvider} database that denotes the event + * is of type {@link android.telephony.ims.RcsGroupThreadNameChangedEvent} + */ + int NAME_CHANGED_EVENT_TYPE = 16; + } + + /** + * The view that allows unified querying across all events + */ + interface RcsUnifiedEventHelper extends RcsParticipantEventColumns, RcsThreadEventColumns { + /** + * The path that should be used for referring to + * {@link android.telephony.ims.RcsEvent}s in + * {@link com.android.providers.telephony.RcsProvider} URIs. + */ + String RCS_EVENT_QUERY_URI_PATH = "event"; + + /** + * The URI to query {@link android.telephony.ims.RcsEvent}s via the content provider. + */ + Uri RCS_EVENT_QUERY_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY, + RCS_EVENT_QUERY_URI_PATH); + } + + /** + * Allows RCS specific canonical address handling. + */ + interface RcsCanonicalAddressHelper { + /** + * Returns the canonical address ID for a canonical address, if now row exists, this + * will add a row and return its ID. This helper works against the same table used by + * the SMS and MMS threads, but is accessible only by the phone process for use by RCS + * message storage. + * + * @throws IllegalArgumentException if unable to retrieve or create the canonical + * address entry. + */ + static long getOrCreateCanonicalAddressId( + ContentResolver contentResolver, String canonicalAddress) { + + Uri.Builder uriBuilder = CONTENT_AND_AUTHORITY.buildUpon(); + uriBuilder.appendPath("canonical-address"); + uriBuilder.appendQueryParameter("address", canonicalAddress); + Uri uri = uriBuilder.build(); + + try (Cursor cursor = contentResolver.query(uri, null, null, null)) { + if (cursor != null && cursor.moveToFirst()) { + return cursor.getLong(cursor.getColumnIndex(CanonicalAddressesColumns._ID)); + } else { + Rlog.e(TAG, "getOrCreateCanonicalAddressId returned no rows"); + } + } + + Rlog.e(TAG, "getOrCreateCanonicalAddressId failed"); + throw new IllegalArgumentException( + "Unable to find or allocate a canonical address ID"); + } + } + } + + /** * Contains all MMS messages. */ public static final class Mms implements BaseMmsColumns { diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java index 9c64cf6ddd8f..75165afe097e 100644 --- a/telephony/java/android/telephony/AccessNetworkConstants.java +++ b/telephony/java/android/telephony/AccessNetworkConstants.java @@ -54,8 +54,15 @@ public final class AccessNetworkConstants { */ @SystemApi public static final class TransportType { + /** + * Invalid transport type. + * @hide + */ + public static final int INVALID = -1; + /** Wireless Wide Area Networks (i.e. Cellular) */ public static final int WWAN = 1; + /** Wireless Local Area Networks (i.e. Wifi) */ public static final int WLAN = 2; @@ -65,6 +72,7 @@ public final class AccessNetworkConstants { /** @hide */ public static String toString(int type) { switch (type) { + case INVALID: return "INVALID"; case WWAN: return "WWAN"; case WLAN: return "WLAN"; default: return Integer.toString(type); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 190e82b6cc5b..5c5d8569f3e8 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -2371,6 +2371,14 @@ public class CarrierConfigManager { "support_emergency_dialer_shortcut_bool"; /** + * Support ASCII 7-BIT encoding for long SMS. This carrier config is used to enable + * this feature. + * @hide + */ + public static final String KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL = + "ascii_7_bit_support_for_long_message_bool"; + + /** * Controls RSRP threshold at which OpportunisticNetworkService will decide whether * the opportunistic network is good enough for internet data. */ @@ -2780,6 +2788,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_CALL_WAITING_OVER_UT_WARNING_BOOL, false); sDefaults.putBoolean(KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_EMERGENCY_DIALER_SHORTCUT_BOOL, true); + sDefaults.putBoolean(KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL, false); /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108); /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */ diff --git a/telephony/java/android/telephony/CarrierRestrictionRules.java b/telephony/java/android/telephony/CarrierRestrictionRules.java index 37847aef9167..d47b55ca4372 100644 --- a/telephony/java/android/telephony/CarrierRestrictionRules.java +++ b/telephony/java/android/telephony/CarrierRestrictionRules.java @@ -27,6 +27,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * Contains the list of carrier restrictions. @@ -93,6 +94,9 @@ public final class CarrierRestrictionRules implements Parcelable { value = {CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED, CARRIER_RESTRICTION_DEFAULT_ALLOWED}) public @interface CarrierRestrictionDefault {} + /* Wild character for comparison */ + private static final char WILD_CHARACTER = '?'; + private List<CarrierIdentifier> mAllowedCarriers; private List<CarrierIdentifier> mExcludedCarriers; @CarrierRestrictionDefault @@ -166,6 +170,124 @@ public final class CarrierRestrictionRules implements Parcelable { } /** + * Tests an array of carriers with the carrier restriction configuration. The list of carrier + * ids passed as argument does not need to be the same as currently present in the device. + * + * @param carrierIds list of {@link CarrierIdentifier}, one for each SIM slot on the device + * @return a list of boolean with the same size as input, indicating if each + * {@link CarrierIdentifier} is allowed or not. + */ + public List<Boolean> isCarrierIdentifiersAllowed(@NonNull List<CarrierIdentifier> carrierIds) { + ArrayList<Boolean> result = new ArrayList<>(carrierIds.size()); + + // First calculate the result for each slot independently + for (int i = 0; i < carrierIds.size(); i++) { + boolean inAllowedList = isCarrierIdInList(carrierIds.get(i), mAllowedCarriers); + boolean inExcludedList = isCarrierIdInList(carrierIds.get(i), mExcludedCarriers); + if (mCarrierRestrictionDefault == CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED) { + result.add((inAllowedList && !inExcludedList) ? true : false); + } else { + result.add((inExcludedList && !inAllowedList) ? false : true); + } + } + // Apply the multi-slot policy, if needed. + if (mMultiSimPolicy == MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT) { + for (boolean b : result) { + if (b) { + result.replaceAll(x -> true); + break; + } + } + } + return result; + } + + /** + * Indicates if a certain carrier {@code id} is present inside a {@code list} + * + * @return true if the carrier {@code id} is present, false otherwise + */ + private static boolean isCarrierIdInList(CarrierIdentifier id, List<CarrierIdentifier> list) { + for (CarrierIdentifier listItem : list) { + // Compare MCC and MNC + if (!patternMatch(id.getMcc(), listItem.getMcc()) + || !patternMatch(id.getMnc(), listItem.getMnc())) { + continue; + } + + // Compare SPN. Comparison is on the complete strings, case insensitive and with wild + // characters. + String listItemValue = convertNullToEmpty(listItem.getSpn()); + String idValue = convertNullToEmpty(id.getSpn()); + if (!listItemValue.isEmpty()) { + if (!patternMatch(idValue, listItemValue)) { + continue; + } + } + + // The IMSI of the configuration can be shorter than actual IMSI in the SIM card. + listItemValue = convertNullToEmpty(listItem.getImsi()); + idValue = convertNullToEmpty(id.getImsi()); + if (!patternMatch( + idValue.substring(0, Math.min(idValue.length(), listItemValue.length())), + listItemValue)) { + continue; + } + + // The GID1 of the configuration can be shorter than actual GID1 in the SIM card. + listItemValue = convertNullToEmpty(listItem.getGid1()); + idValue = convertNullToEmpty(id.getGid1()); + if (!patternMatch( + idValue.substring(0, Math.min(idValue.length(), listItemValue.length())), + listItemValue)) { + continue; + } + + // The GID2 of the configuration can be shorter than actual GID2 in the SIM card. + listItemValue = convertNullToEmpty(listItem.getGid2()); + idValue = convertNullToEmpty(id.getGid2()); + if (!patternMatch( + idValue.substring(0, Math.min(idValue.length(), listItemValue.length())), + listItemValue)) { + continue; + } + + // Valid match was found in the list + return true; + } + return false; + } + + private static String convertNullToEmpty(String value) { + return Objects.toString(value, ""); + } + + /** + * Performs a case insensitive string comparison against a given pattern. The character '?' + * is used in the pattern as wild character in the comparison. The string must have the same + * length as the pattern. + * + * @param str string to match + * @param pattern string containing the pattern + * @return true in case of match, false otherwise + */ + private static boolean patternMatch(String str, String pattern) { + if (str.length() != pattern.length()) { + return false; + } + String lowerCaseStr = str.toLowerCase(); + String lowerCasePattern = pattern.toLowerCase(); + + for (int i = 0; i < lowerCasePattern.length(); i++) { + if (lowerCasePattern.charAt(i) != lowerCaseStr.charAt(i) + && lowerCasePattern.charAt(i) != WILD_CHARACTER) { + return false; + } + } + return true; + } + + /** * {@link Parcelable#writeToParcel} */ @Override diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java index 6b1b84cd3458..856f08107fd7 100644 --- a/telephony/java/android/telephony/CellIdentityNr.java +++ b/telephony/java/android/telephony/CellIdentityNr.java @@ -30,6 +30,7 @@ public final class CellIdentityNr extends CellIdentity { private final int mNrArfcn; private final int mPci; private final int mTac; + private final long mNci; /** * @@ -44,11 +45,12 @@ public final class CellIdentityNr extends CellIdentity { * @hide */ public CellIdentityNr(int pci, int tac, int nrArfcn, String mccStr, String mncStr, - String alphal, String alphas) { + long nci, String alphal, String alphas) { super(TAG, CellInfo.TYPE_NR, mccStr, mncStr, alphal, alphas); mPci = pci; mTac = tac; mNrArfcn = nrArfcn; + mNci = nci; } /** @@ -62,7 +64,7 @@ public final class CellIdentityNr extends CellIdentity { @Override public int hashCode() { - return Objects.hash(super.hashCode(), mPci, mTac, mNrArfcn); + return Objects.hash(super.hashCode(), mPci, mTac, mNrArfcn, mNci); } @Override @@ -72,7 +74,17 @@ public final class CellIdentityNr extends CellIdentity { } CellIdentityNr o = (CellIdentityNr) other; - return super.equals(o) && mPci == o.mPci && mTac == o.mTac && mNrArfcn == o.mNrArfcn; + return super.equals(o) && mPci == o.mPci && mTac == o.mTac && mNrArfcn == o.mNrArfcn + && mNci == o.mNci; + } + + /** + * Get the NR Cell Identity. + * + * @return The NR Cell Identity in range [0, 68719476735] or Long.MAX_VALUE if unknown. + */ + public long getNci() { + return mNci; } /** @@ -122,6 +134,7 @@ public final class CellIdentityNr extends CellIdentity { .append(" mNrArfcn = ").append(mNrArfcn) .append(" mMcc = ").append(mMccStr) .append(" mMnc = ").append(mMncStr) + .append(" mNci = ").append(mNci) .append(" mAlphaLong = ").append(mAlphaLong) .append(" mAlphaShort = ").append(mAlphaShort) .append(" }") @@ -134,6 +147,7 @@ public final class CellIdentityNr extends CellIdentity { dest.writeInt(mPci); dest.writeInt(mTac); dest.writeInt(mNrArfcn); + dest.writeLong(mNci); } /** Construct from Parcel, type has already been processed */ @@ -142,6 +156,7 @@ public final class CellIdentityNr extends CellIdentity { mPci = in.readInt(); mTac = in.readInt(); mNrArfcn = in.readInt(); + mNci = in.readLong(); } /** Implement the Parcelable interface */ diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java index 38143335dbf1..dba437a3a007 100644 --- a/telephony/java/android/telephony/CellIdentityTdscdma.java +++ b/telephony/java/android/telephony/CellIdentityTdscdma.java @@ -141,6 +141,14 @@ public final class CellIdentityTdscdma extends CellIdentity { return mCpid; } + /** + * @return 16-bit UMTS Absolute RF Channel Number, + * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable. + */ + public int getUarfcn() { + return mUarfcn; + } + /** @hide */ @Override public int getChannelNumber() { diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java index 30e641d61143..a4207c99ce4d 100644 --- a/telephony/java/android/telephony/CellSignalStrengthGsm.java +++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java @@ -63,6 +63,10 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P public CellSignalStrengthGsm(android.hardware.radio.V1_0.GsmSignalStrength gsm) { // Convert from HAL values as part of construction. this(getRssiDbmFromAsu(gsm.signalStrength), gsm.bitErrorRate, gsm.timingAdvance); + + if (mRssi == CellInfo.UNAVAILABLE) { + setDefaultValues(); + } } /** @hide */ diff --git a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java index 6f52b853d23b..5ae89b0f8b3d 100644 --- a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java +++ b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java @@ -72,6 +72,10 @@ public final class CellSignalStrengthTdscdma extends CellSignalStrength implemen // Convert from HAL values as part of construction. this(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, tdscdma.rscp != CellInfo.UNAVAILABLE ? -tdscdma.rscp : tdscdma.rscp); + + if (mRssi == CellInfo.UNAVAILABLE && mRscp == CellInfo.UNAVAILABLE) { + setDefaultValues(); + } } /** @hide */ @@ -79,6 +83,10 @@ public final class CellSignalStrengthTdscdma extends CellSignalStrength implemen // Convert from HAL values as part of construction. this(getRssiDbmFromAsu(tdscdma.signalStrength), tdscdma.bitErrorRate, getRscpDbmFromAsu(tdscdma.rscp)); + + if (mRssi == CellInfo.UNAVAILABLE && mRscp == CellInfo.UNAVAILABLE) { + setDefaultValues(); + } } /** @hide */ diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java index 0760407171ae..efa3647f0e9b 100644 --- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java +++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java @@ -92,8 +92,12 @@ public final class CellSignalStrengthWcdma extends CellSignalStrength implements /** @hide */ public CellSignalStrengthWcdma(android.hardware.radio.V1_0.WcdmaSignalStrength wcdma) { // Convert from HAL values as part of construction. - this(getRssiDbmFromAsu(wcdma.signalStrength), - wcdma.bitErrorRate, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE); + this(getRssiDbmFromAsu(wcdma.signalStrength), wcdma.bitErrorRate, + CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE); + + if (mRssi == CellInfo.UNAVAILABLE && mRscp == CellInfo.UNAVAILABLE) { + setDefaultValues(); + } } /** @hide */ @@ -103,6 +107,10 @@ public final class CellSignalStrengthWcdma extends CellSignalStrength implements wcdma.base.bitErrorRate, getRscpDbmFromAsu(wcdma.rscp), getEcNoDbFromAsu(wcdma.ecno)); + + if (mRssi == CellInfo.UNAVAILABLE && mRscp == CellInfo.UNAVAILABLE) { + setDefaultValues(); + } } /** @hide */ diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java index 559d47b5d3a1..6e6d59e62148 100644 --- a/telephony/java/android/telephony/NetworkRegistrationState.java +++ b/telephony/java/android/telephony/NetworkRegistrationState.java @@ -27,6 +27,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.Objects; +import java.util.stream.Collectors; /** * Description of a mobile network registration state @@ -360,7 +361,34 @@ public class NetworkRegistrationState implements Parcelable { return 0; } - private static String regStateToString(int regState) { + /** + * Convert service type to string + * + * @hide + * + * @param serviceType The service type + * @return The service type in string format + */ + public static String serviceTypeToString(@ServiceType int serviceType) { + switch (serviceType) { + case SERVICE_TYPE_VOICE: return "VOICE"; + case SERVICE_TYPE_DATA: return "DATA"; + case SERVICE_TYPE_SMS: return "SMS"; + case SERVICE_TYPE_VIDEO: return "VIDEO"; + case SERVICE_TYPE_EMERGENCY: return "EMERGENCY"; + } + return "Unknown service type " + serviceType; + } + + /** + * Convert registration state to string + * + * @hide + * + * @param regState The registration state + * @return The reg state in string + */ + public static String regStateToString(@RegState int regState) { switch (regState) { case REG_STATE_NOT_REG_NOT_SEARCHING: return "NOT_REG_NOT_SEARCHING"; case REG_STATE_HOME: return "HOME"; @@ -389,14 +417,17 @@ public class NetworkRegistrationState implements Parcelable { public String toString() { return new StringBuilder("NetworkRegistrationState{") .append(" domain=").append((mDomain == DOMAIN_CS) ? "CS" : "PS") - .append("transportType=").append(mTransportType) + .append(" transportType=").append(TransportType.toString(mTransportType)) .append(" regState=").append(regStateToString(mRegState)) - .append(" roamingType=").append(mRoamingType) + .append(" roamingType=").append(ServiceState.roamingTypeToString(mRoamingType)) .append(" accessNetworkTechnology=") .append(TelephonyManager.getNetworkTypeName(mAccessNetworkTechnology)) .append(" rejectCause=").append(mRejectCause) .append(" emergencyEnabled=").append(mEmergencyOnly) - .append(" supportedServices=").append(mAvailableServices) + .append(" availableServices=").append("[" + (mAvailableServices != null + ? Arrays.stream(mAvailableServices) + .mapToObj(type -> serviceTypeToString(type)) + .collect(Collectors.joining(",")) : null) + "]") .append(" cellIdentity=").append(mCellIdentity) .append(" voiceSpecificStates=").append(mVoiceSpecificStates) .append(" dataSpecificStates=").append(mDataSpecificStates) diff --git a/telephony/java/android/telephony/PhoneCapability.java b/telephony/java/android/telephony/PhoneCapability.java index 2ebfa53ead23..21dad77d29b7 100644 --- a/telephony/java/android/telephony/PhoneCapability.java +++ b/telephony/java/android/telephony/PhoneCapability.java @@ -33,15 +33,17 @@ public class PhoneCapability implements Parcelable { public final int maxActiveVoiceCalls; public final int maxActiveData; public final int max5G; + public final boolean validationBeforeSwitchSupported; public final List<ModemInfo> logicalModemList; public PhoneCapability(int maxActiveVoiceCalls, int maxActiveData, int max5G, - List<ModemInfo> logicalModemList) { + List<ModemInfo> logicalModemList, boolean validationBeforeSwitchSupported) { this.maxActiveVoiceCalls = maxActiveVoiceCalls; this.maxActiveData = maxActiveData; this.max5G = max5G; // Make sure it's not null. this.logicalModemList = logicalModemList == null ? new ArrayList<>() : logicalModemList; + this.validationBeforeSwitchSupported = validationBeforeSwitchSupported; } @Override @@ -55,13 +57,15 @@ public class PhoneCapability implements Parcelable { maxActiveVoiceCalls = in.readInt(); maxActiveData = in.readInt(); max5G = in.readInt(); + validationBeforeSwitchSupported = in.readBoolean(); logicalModemList = new ArrayList<>(); in.readList(logicalModemList, ModemInfo.class.getClassLoader()); } @Override public int hashCode() { - return Objects.hash(maxActiveVoiceCalls, maxActiveData, max5G, logicalModemList); + return Objects.hash(maxActiveVoiceCalls, maxActiveData, max5G, logicalModemList, + validationBeforeSwitchSupported); } @Override @@ -79,6 +83,7 @@ public class PhoneCapability implements Parcelable { return (maxActiveVoiceCalls == s.maxActiveVoiceCalls && maxActiveData == s.maxActiveData && max5G == s.max5G + && validationBeforeSwitchSupported == s.validationBeforeSwitchSupported && logicalModemList.equals(s.logicalModemList)); } @@ -96,6 +101,7 @@ public class PhoneCapability implements Parcelable { dest.writeInt(maxActiveVoiceCalls); dest.writeInt(maxActiveData); dest.writeInt(max5G); + dest.writeBoolean(validationBeforeSwitchSupported); dest.writeList(logicalModemList); } diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java index fea1b7b08a20..3ce646cb400b 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/telephony/java/android/telephony/PhoneStateListener.java @@ -45,7 +45,8 @@ import java.util.concurrent.Executor; * <p> * Override the methods for the state that you wish to receive updates for, and * pass your PhoneStateListener object, along with bitwise-or of the LISTEN_ - * flags to {@link TelephonyManager#listen TelephonyManager.listen()}. + * flags to {@link TelephonyManager#listen TelephonyManager.listen()}. Methods are + * called when the state changes, as well as once on initial registration. * <p> * Note that access to some telephony information is * permission-protected. Your application won't receive updates for protected diff --git a/telephony/java/android/telephony/RadioAccessFamily.java b/telephony/java/android/telephony/RadioAccessFamily.java index 0d94c4dd3686..c1786befb096 100644 --- a/telephony/java/android/telephony/RadioAccessFamily.java +++ b/telephony/java/android/telephony/RadioAccessFamily.java @@ -17,12 +17,15 @@ package android.telephony; import android.annotation.UnsupportedAppUsage; +import android.hardware.radio.V1_0.RadioTechnology; +import android.hardware.radio.V1_4.CellInfo.Info; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import com.android.internal.telephony.RILConstants; + /** * Object to indicate the phone radio type and access technology. * @@ -33,32 +36,34 @@ public class RadioAccessFamily implements Parcelable { /** * TODO: get rid of RAF definition in RadioAccessFamily and * use {@link TelephonyManager.NetworkTypeBitMask} + * TODO: public definition {@link TelephonyManager.NetworkTypeBitMask} is long. + * TODO: Convert from int to long everywhere including HAL definitions. */ // 2G - public static final int RAF_UNKNOWN = TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN; - public static final int RAF_GSM = TelephonyManager.NETWORK_TYPE_BITMASK_GSM; - public static final int RAF_GPRS = TelephonyManager.NETWORK_TYPE_BITMASK_GPRS; - public static final int RAF_EDGE = TelephonyManager.NETWORK_TYPE_BITMASK_EDGE; - public static final int RAF_IS95A = TelephonyManager.NETWORK_TYPE_BITMASK_CDMA; - public static final int RAF_IS95B = TelephonyManager.NETWORK_TYPE_BITMASK_CDMA; - public static final int RAF_1xRTT = TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT; + public static final int RAF_UNKNOWN = (int) TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN; + public static final int RAF_GSM = (int) TelephonyManager.NETWORK_TYPE_BITMASK_GSM; + public static final int RAF_GPRS = (int) TelephonyManager.NETWORK_TYPE_BITMASK_GPRS; + public static final int RAF_EDGE = (int) TelephonyManager.NETWORK_TYPE_BITMASK_EDGE; + public static final int RAF_IS95A = (int) TelephonyManager.NETWORK_TYPE_BITMASK_CDMA; + public static final int RAF_IS95B = (int) TelephonyManager.NETWORK_TYPE_BITMASK_CDMA; + public static final int RAF_1xRTT = (int) TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT; // 3G - public static final int RAF_EVDO_0 = TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_0; - public static final int RAF_EVDO_A = TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_A; - public static final int RAF_EVDO_B = TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_B; - public static final int RAF_EHRPD = TelephonyManager.NETWORK_TYPE_BITMASK_EHRPD; - public static final int RAF_HSUPA = TelephonyManager.NETWORK_TYPE_BITMASK_HSUPA; - public static final int RAF_HSDPA = TelephonyManager.NETWORK_TYPE_BITMASK_HSDPA; - public static final int RAF_HSPA = TelephonyManager.NETWORK_TYPE_BITMASK_HSPA; - public static final int RAF_HSPAP = TelephonyManager.NETWORK_TYPE_BITMASK_HSPAP; - public static final int RAF_UMTS = TelephonyManager.NETWORK_TYPE_BITMASK_UMTS; - public static final int RAF_TD_SCDMA = TelephonyManager.NETWORK_TYPE_BITMASK_TD_SCDMA; + public static final int RAF_EVDO_0 = (int) TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_0; + public static final int RAF_EVDO_A = (int) TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_A; + public static final int RAF_EVDO_B = (int) TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_B; + public static final int RAF_EHRPD = (int) TelephonyManager.NETWORK_TYPE_BITMASK_EHRPD; + public static final int RAF_HSUPA = (int) TelephonyManager.NETWORK_TYPE_BITMASK_HSUPA; + public static final int RAF_HSDPA = (int) TelephonyManager.NETWORK_TYPE_BITMASK_HSDPA; + public static final int RAF_HSPA = (int) TelephonyManager.NETWORK_TYPE_BITMASK_HSPA; + public static final int RAF_HSPAP = (int) TelephonyManager.NETWORK_TYPE_BITMASK_HSPAP; + public static final int RAF_UMTS = (int) TelephonyManager.NETWORK_TYPE_BITMASK_UMTS; + public static final int RAF_TD_SCDMA = (int) TelephonyManager.NETWORK_TYPE_BITMASK_TD_SCDMA; // 4G - public static final int RAF_LTE = TelephonyManager.NETWORK_TYPE_BITMASK_LTE; - public static final int RAF_LTE_CA = TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA; + public static final int RAF_LTE = (int) TelephonyManager.NETWORK_TYPE_BITMASK_LTE; + public static final int RAF_LTE_CA = (int) TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA; // 5G - public static final int RAF_NR = TelephonyManager.NETWORK_TYPE_BITMASK_NR; + public static final int RAF_NR = (int) TelephonyManager.NETWORK_TYPE_BITMASK_NR; // Grouping of RAFs // 2G @@ -147,20 +152,20 @@ public class RadioAccessFamily implements Parcelable { /** * Implement the Parcelable interface. */ - public static final Creator<RadioAccessFamily> CREATOR = - new Creator<RadioAccessFamily>() { + public static final Creator<android.telephony.RadioAccessFamily> CREATOR = + new Creator<android.telephony.RadioAccessFamily>() { @Override - public RadioAccessFamily createFromParcel(Parcel in) { + public android.telephony.RadioAccessFamily createFromParcel(Parcel in) { int phoneId = in.readInt(); int radioAccessFamily = in.readInt(); - return new RadioAccessFamily(phoneId, radioAccessFamily); + return new android.telephony.RadioAccessFamily(phoneId, radioAccessFamily); } @Override - public RadioAccessFamily[] newArray(int size) { - return new RadioAccessFamily[size]; + public android.telephony.RadioAccessFamily[] newArray(int size) { + return new android.telephony.RadioAccessFamily[size]; } }; @@ -391,76 +396,78 @@ public class RadioAccessFamily implements Parcelable { } /** - * convert RAF from {@link ServiceState.RilRadioTechnology} bitmask to + * convert RAF from {@link android.hardware.radio.V1_0.RadioAccessFamily} to * {@link TelephonyManager.NetworkTypeBitMask}, the bitmask represented by - * {@link TelephonyManager.NetworkType}. Reasons are {@link TelephonyManager.NetworkType} are - * public while {@link ServiceState.RilRadioTechnology} are hidden. We - * don't want to expose two sets of definition to public. + * {@link TelephonyManager.NetworkType}. * - * @param raf bitmask represented by {@link ServiceState.RilRadioTechnology} + * @param raf {@link android.hardware.radio.V1_0.RadioAccessFamily} * @return {@link TelephonyManager.NetworkTypeBitMask} */ public static int convertToNetworkTypeBitMask(int raf) { int networkTypeRaf = 0; - if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_GSM)) != 0) { + if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.GSM) != 0) { networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_GSM; } - if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_GPRS)) != 0) { + if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.GPRS) != 0) { networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_GPRS; } - if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EDGE)) != 0) { + if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.EDGE) != 0) { networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EDGE; } // convert both IS95A/IS95B to CDMA as network mode doesn't support CDMA - if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_IS95A)) != 0) { + if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.IS95A) != 0) { networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_CDMA; } - if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_IS95B)) != 0) { + if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.IS95B) != 0) { networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_CDMA; } - if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT)) != 0) { + if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.ONE_X_RTT) != 0) { networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT; } - if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0)) != 0) { + if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.EVDO_0) != 0) { networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_0; } - if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A)) != 0) { + if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.EVDO_A) != 0) { networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_A; } - if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B)) != 0) { + if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.EVDO_B) != 0) { networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_B; } - if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD)) != 0) { + if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.EHRPD) != 0) { networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EHRPD; } - if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA)) != 0) { + if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.HSUPA) != 0) { networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_HSUPA; } - if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA)) != 0) { + if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.HSDPA) != 0) { networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_HSDPA; } - if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSPA)) != 0) { + if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.HSPA) != 0) { networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_HSPA; } - if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP)) != 0) { + if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.HSPAP) != 0) { networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_HSPAP; } - if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_UMTS)) != 0) { + if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.UMTS) != 0) { networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_UMTS; } - if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA)) != 0) { + if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.TD_SCDMA) != 0) { networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_TD_SCDMA; } - if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_LTE)) != 0) { + if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.LTE) != 0) { networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_LTE; } - if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA)) != 0) { + if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.LTE_CA) != 0) { networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA; } - if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_NR)) != 0) { + if ((raf & android.hardware.radio.V1_4.RadioAccessFamily.NR) != 0) { networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_NR; } + // TODO: need hal definition + if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN)) != 0) { + networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_IWLAN; + } return (networkTypeRaf == 0) ? TelephonyManager.NETWORK_TYPE_UNKNOWN : networkTypeRaf; } diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 008051095583..a1aee6d8217f 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -888,6 +888,24 @@ public class ServiceState implements Parcelable { } /** + * Convert roaming type to string + * + * @param roamingType roaming type + * @return The roaming type in string format + * + * @hide + */ + public static String roamingTypeToString(@RoamingType int roamingType) { + switch (roamingType) { + case ROAMING_TYPE_NOT_ROAMING: return "NOT_ROAMING"; + case ROAMING_TYPE_UNKNOWN: return "UNKNOWN"; + case ROAMING_TYPE_DOMESTIC: return "DOMESTIC"; + case ROAMING_TYPE_INTERNATIONAL: return "INTERNATIONAL"; + } + return "Unknown roaming type " + roamingType; + } + + /** * Convert radio technology to String * * @param rt radioTechnology diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index 91e24a955013..52a2085f03bd 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -187,15 +187,7 @@ public class SmsMessage { int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(); String format = (PHONE_TYPE_CDMA == activePhone) ? SmsConstants.FORMAT_3GPP2 : SmsConstants.FORMAT_3GPP; - message = createFromPdu(pdu, format); - - if (null == message || null == message.mWrappedSmsMessage) { - // decoding pdu failed based on activePhone type, must be other format - format = (PHONE_TYPE_CDMA == activePhone) ? - SmsConstants.FORMAT_3GPP : SmsConstants.FORMAT_3GPP2; - message = createFromPdu(pdu, format); - } - return message; + return createFromPdu(pdu, format); } /** @@ -211,11 +203,18 @@ public class SmsMessage { * {@link android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION} intent */ public static SmsMessage createFromPdu(byte[] pdu, String format) { - SmsMessageBase wrappedMessage; + return createFromPdu(pdu, format, true); + } + + private static SmsMessage createFromPdu(byte[] pdu, String format, + boolean fallbackToOtherFormat) { if (pdu == null) { Rlog.i(LOG_TAG, "createFromPdu(): pdu is null"); return null; } + SmsMessageBase wrappedMessage; + String otherFormat = SmsConstants.FORMAT_3GPP2.equals(format) ? SmsConstants.FORMAT_3GPP : + SmsConstants.FORMAT_3GPP2; if (SmsConstants.FORMAT_3GPP2.equals(format)) { wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu); } else if (SmsConstants.FORMAT_3GPP.equals(format)) { @@ -228,8 +227,12 @@ public class SmsMessage { if (wrappedMessage != null) { return new SmsMessage(wrappedMessage); } else { - Rlog.e(LOG_TAG, "createFromPdu(): wrappedMessage is null"); - return null; + if (fallbackToOtherFormat) { + return createFromPdu(pdu, otherFormat, false); + } else { + Rlog.e(LOG_TAG, "createFromPdu(): wrappedMessage is null"); + return null; + } } } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 845d23ee4cc2..79606eb72202 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -865,7 +865,8 @@ public class SubscriptionManager { } /** - * Callback invoked when there is any change to any SubscriptionInfo. Typically + * Callback invoked when there is any change to any SubscriptionInfo, as well as once on + * registering for changes with {@link #addOnSubscriptionsChangedListener}. Typically * this method would invoke {@link #getActiveSubscriptionInfoList} */ public void onSubscriptionsChanged() { @@ -917,7 +918,9 @@ public class SubscriptionManager { /** * Register for changes to the list of active {@link SubscriptionInfo} records or to the * individual records themselves. When a change occurs the onSubscriptionsChanged method of - * the listener will be invoked immediately if there has been a notification. + * the listener will be invoked immediately if there has been a notification. The + * onSubscriptionChanged method will also be triggered once initially when calling this + * function. * * @param listener an instance of {@link OnSubscriptionsChangedListener} with * onSubscriptionsChanged overridden. @@ -2048,6 +2051,8 @@ public class SubscriptionManager { putPhoneIdAndSubIdExtra(intent, phoneId, subIds[0]); } else { logd("putPhoneIdAndSubIdExtra: no valid subs"); + intent.putExtra(PhoneConstants.PHONE_KEY, phoneId); + intent.putExtra(PhoneConstants.SLOT_KEY, phoneId); } } @@ -2860,6 +2865,95 @@ public class SubscriptionManager { } } + /** + * Enabled or disable a subscription. This is currently used in the settings page. + * + * <p> + * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required + * + * @param enable whether user is turning it on or off. + * @param subscriptionId Subscription to be enabled or disabled. + * It could be a eSIM or pSIM subscription. + * + * @return whether the operation is successful. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public boolean setSubscriptionEnabled(int subscriptionId, boolean enable) { + if (VDBG) { + logd("setSubscriptionActivated subId= " + subscriptionId + " enable " + enable); + } + try { + ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + if (iSub != null) { + return iSub.setSubscriptionEnabled(enable, subscriptionId); + } + } catch (RemoteException ex) { + // ignore it + } + + return false; + } + + /** + * Returns whether the subscription is enabled or not. This is different from activated + * or deactivated for two aspects. 1) For when user disables a physical subscription, we + * actually disable the modem because we can't switch off the subscription. 2) For eSIM, + * user may enable one subscription but the system may activate another temporarily. In this + * case, user enabled one is different from current active one. + + * @param subscriptionId The subscription it asks about. + * @return whether it's enabled or not. {@code true} if user set this subscription enabled + * earlier, or user never set subscription enable / disable on this slot explicitly, and + * this subscription is currently active. Otherwise, it returns {@code false}. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public boolean isSubscriptionEnabled(int subscriptionId) { + try { + ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + if (iSub != null) { + return iSub.isSubscriptionEnabled(subscriptionId); + } + } catch (RemoteException ex) { + // ignore it + } + + return false; + } + + /** + * Get which subscription is enabled on this slot. See {@link #isSubscriptionEnabled(int)} + * for more details. + * + * @param slotIndex which slot it asks about. + * @return which subscription is enabled on this slot. If there's no enabled subscription + * in this slot, it will return {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public int getEnabledSubscriptionId(int slotIndex) { + int subId = INVALID_SUBSCRIPTION_ID; + + try { + ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + if (iSub != null) { + subId = iSub.getEnabledSubscriptionId(slotIndex); + } + } catch (RemoteException ex) { + // ignore it + } + + if (VDBG) logd("getEnabledSubscriptionId, subId = " + subId); + return subId; + } + private interface CallISubMethodHelper { int callMethod(ISub iSub) throws RemoteException; } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 6461e063a954..d2345cd79dce 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -23,6 +23,7 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.IntDef; +import android.annotation.LongDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -71,6 +72,7 @@ import android.telephony.ims.feature.MmTelFeature; import android.telephony.ims.stub.ImsRegistrationImplBase; import android.text.TextUtils; import android.util.Log; +import android.util.Pair; import com.android.ims.internal.IImsServiceFeatureCallback; import com.android.internal.annotations.VisibleForTesting; @@ -3248,6 +3250,35 @@ public class TelephonyManager { } } + /** + * Get the mapping from logical slots to physical slots. The mapping represent by a pair list. + * The key of the piar is the logical slot id and the value of the pair is the physical + * slots id mapped to this logical slot id. + * + * @return an pair list indicates the mapping from logical slots to physical slots. The size of + * the list should be {@link #getPhoneCount()} if success, otherwise return an empty list. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @NonNull + public List<Pair<Integer, Integer>> getLogicalToPhysicalSlotMapping() { + List<Pair<Integer, Integer>> slotMapping = new ArrayList<>(); + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + int[] slotMappingArray = telephony.getSlotsMapping(); + for (int i = 0; i < slotMappingArray.length; i++) { + slotMapping.add(new Pair(i, slotMappingArray[i])); + } + } + } catch (RemoteException e) { + Log.e(TAG, "getSlotsMapping RemoteException", e); + } + return slotMapping; + } + // // // Subscriber Info @@ -4863,12 +4894,13 @@ public class TelephonyManager { * * <p>Apps targeting {@link android.os.Build.VERSION_CODES#Q Android Q} or higher will no * longer trigger a refresh of the cached CellInfo by invoking this API. Instead, those apps - * will receive the latest cached results. Apps targeting + * will receive the latest cached results, which may not be current. Apps targeting * {@link android.os.Build.VERSION_CODES#Q Android Q} or higher that wish to request updated * CellInfo should call - * {android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()} and - * listen for responses via {@link android.telephony.PhoneStateListener#onCellInfoChanged - * onCellInfoChanged()}. + * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}; + * however, in all cases, updates will be rate-limited and are not guaranteed. To determine the + * recency of CellInfo data, callers should check + * {@link android.telephony.CellInfo#getTimeStamp CellInfo#getTimeStamp()}. * * <p>This method returns valid data for devices with * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. In cases @@ -6526,6 +6558,37 @@ public class TelephonyManager { } /** + * Get the preferred network type bitmap. + * + * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return The bitmap of preferred network types. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SystemApi + public @NetworkTypeBitMask long getPreferredNetworkTypeBitmap() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return (long) RadioAccessFamily.getRafFromNetworkType( + telephony.getPreferredNetworkType(getSubId())); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "getPreferredNetworkTypeBitmap RemoteException", ex); + } catch (NullPointerException ex) { + Rlog.e(TAG, "getPreferredNetworkTypeBitmap NPE", ex); + } + return 0; + } + + /** * Sets the network selection mode to automatic. * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the @@ -6752,6 +6815,38 @@ public class TelephonyManager { } /** + * Set the preferred network type bitmap. + * + * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param networkTypeBitmap The bitmap of preferred network types. + * @return true on success; false on any failure. + * @hide + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @SystemApi + public boolean setPreferredNetworkTypeBitmap(@NetworkTypeBitMask long networkTypeBitmap) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.setPreferredNetworkType( + getSubId(), RadioAccessFamily.getNetworkTypeFromRaf( + (int) networkTypeBitmap)); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "setPreferredNetworkType RemoteException", ex); + } catch (NullPointerException ex) { + Rlog.e(TAG, "setPreferredNetworkType NPE", ex); + } + return false; + } + + /** * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA. * * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). @@ -9634,7 +9729,7 @@ public class TelephonyManager { /** @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, prefix = {"NETWORK_TYPE_BITMASK_"}, + @LongDef(flag = true, prefix = {"NETWORK_TYPE_BITMASK_"}, value = {NETWORK_TYPE_BITMASK_UNKNOWN, NETWORK_TYPE_BITMASK_GSM, NETWORK_TYPE_BITMASK_GPRS, @@ -9663,118 +9758,125 @@ public class TelephonyManager { * @hide */ @SystemApi - public static final int NETWORK_TYPE_BITMASK_UNKNOWN = (1 << NETWORK_TYPE_UNKNOWN); + public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L; /** * network type bitmask indicating the support of radio tech GSM. * @hide */ @SystemApi - public static final int NETWORK_TYPE_BITMASK_GSM = (1 << NETWORK_TYPE_GSM); + public static final long NETWORK_TYPE_BITMASK_GSM = (1 << (NETWORK_TYPE_GSM -1)); /** * network type bitmask indicating the support of radio tech GPRS. * @hide */ @SystemApi - public static final int NETWORK_TYPE_BITMASK_GPRS = (1 << NETWORK_TYPE_GPRS); + public static final long NETWORK_TYPE_BITMASK_GPRS = (1 << (NETWORK_TYPE_GPRS -1)); /** * network type bitmask indicating the support of radio tech EDGE. * @hide */ @SystemApi - public static final int NETWORK_TYPE_BITMASK_EDGE = (1 << NETWORK_TYPE_EDGE); + public static final long NETWORK_TYPE_BITMASK_EDGE = (1 << (NETWORK_TYPE_EDGE -1)); /** * network type bitmask indicating the support of radio tech CDMA(IS95A/IS95B). * @hide */ @SystemApi - public static final int NETWORK_TYPE_BITMASK_CDMA = (1 << NETWORK_TYPE_CDMA); + public static final long NETWORK_TYPE_BITMASK_CDMA = (1 << (NETWORK_TYPE_CDMA -1)); /** * network type bitmask indicating the support of radio tech 1xRTT. * @hide */ @SystemApi - public static final int NETWORK_TYPE_BITMASK_1xRTT = (1 << NETWORK_TYPE_1xRTT); + public static final long NETWORK_TYPE_BITMASK_1xRTT = (1 << (NETWORK_TYPE_1xRTT - 1)); // 3G /** * network type bitmask indicating the support of radio tech EVDO 0. * @hide */ @SystemApi - public static final int NETWORK_TYPE_BITMASK_EVDO_0 = (1 << NETWORK_TYPE_EVDO_0); + public static final long NETWORK_TYPE_BITMASK_EVDO_0 = (1 << (NETWORK_TYPE_EVDO_0 -1)); /** * network type bitmask indicating the support of radio tech EVDO A. * @hide */ @SystemApi - public static final int NETWORK_TYPE_BITMASK_EVDO_A = (1 << NETWORK_TYPE_EVDO_A); + public static final long NETWORK_TYPE_BITMASK_EVDO_A = (1 << (NETWORK_TYPE_EVDO_A - 1)); /** * network type bitmask indicating the support of radio tech EVDO B. * @hide */ @SystemApi - public static final int NETWORK_TYPE_BITMASK_EVDO_B = (1 << NETWORK_TYPE_EVDO_B); + public static final long NETWORK_TYPE_BITMASK_EVDO_B = (1 << (NETWORK_TYPE_EVDO_B -1)); /** * network type bitmask indicating the support of radio tech EHRPD. * @hide */ @SystemApi - public static final int NETWORK_TYPE_BITMASK_EHRPD = (1 << NETWORK_TYPE_EHRPD); + public static final long NETWORK_TYPE_BITMASK_EHRPD = (1 << (NETWORK_TYPE_EHRPD -1)); /** * network type bitmask indicating the support of radio tech HSUPA. * @hide */ @SystemApi - public static final int NETWORK_TYPE_BITMASK_HSUPA = (1 << NETWORK_TYPE_HSUPA); + public static final long NETWORK_TYPE_BITMASK_HSUPA = (1 << (NETWORK_TYPE_HSUPA -1)); /** * network type bitmask indicating the support of radio tech HSDPA. * @hide */ @SystemApi - public static final int NETWORK_TYPE_BITMASK_HSDPA = (1 << NETWORK_TYPE_HSDPA); + public static final long NETWORK_TYPE_BITMASK_HSDPA = (1 << (NETWORK_TYPE_HSDPA -1)); /** * network type bitmask indicating the support of radio tech HSPA. * @hide */ @SystemApi - public static final int NETWORK_TYPE_BITMASK_HSPA = (1 << NETWORK_TYPE_HSPA); + public static final long NETWORK_TYPE_BITMASK_HSPA = (1 << (NETWORK_TYPE_HSPA -1)); /** * network type bitmask indicating the support of radio tech HSPAP. * @hide */ @SystemApi - public static final int NETWORK_TYPE_BITMASK_HSPAP = (1 << NETWORK_TYPE_HSPAP); + public static final long NETWORK_TYPE_BITMASK_HSPAP = (1 << (NETWORK_TYPE_HSPAP -1)); /** * network type bitmask indicating the support of radio tech UMTS. * @hide */ @SystemApi - public static final int NETWORK_TYPE_BITMASK_UMTS = (1 << NETWORK_TYPE_UMTS); + public static final long NETWORK_TYPE_BITMASK_UMTS = (1 << (NETWORK_TYPE_UMTS -1)); /** * network type bitmask indicating the support of radio tech TD_SCDMA. * @hide */ @SystemApi - public static final int NETWORK_TYPE_BITMASK_TD_SCDMA = (1 << NETWORK_TYPE_TD_SCDMA); + public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = (1 << (NETWORK_TYPE_TD_SCDMA -1)); // 4G /** * network type bitmask indicating the support of radio tech LTE. * @hide */ @SystemApi - public static final int NETWORK_TYPE_BITMASK_LTE = (1 << NETWORK_TYPE_LTE); + public static final long NETWORK_TYPE_BITMASK_LTE = (1 << (NETWORK_TYPE_LTE -1)); /** * network type bitmask indicating the support of radio tech LTE CA (carrier aggregation). * @hide */ @SystemApi - public static final int NETWORK_TYPE_BITMASK_LTE_CA = (1 << NETWORK_TYPE_LTE_CA); + public static final long NETWORK_TYPE_BITMASK_LTE_CA = (1 << (NETWORK_TYPE_LTE_CA -1)); /** * network type bitmask indicating the support of radio tech NR(New Radio) 5G. * @hide */ @SystemApi - public static final int NETWORK_TYPE_BITMASK_NR = (1 << NETWORK_TYPE_NR); + public static final long NETWORK_TYPE_BITMASK_NR = (1 << (NETWORK_TYPE_NR -1)); + + /** + * network type bitmask indicating the support of radio tech IWLAN. + * @hide + */ + @SystemApi + public static final long NETWORK_TYPE_BITMASK_IWLAN = (1 << (NETWORK_TYPE_IWLAN -1)); /** * @return Modem supported radio access family bitmask @@ -9785,11 +9887,11 @@ public class TelephonyManager { */ @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public @NetworkTypeBitMask int getSupportedRadioAccessFamily() { + public @NetworkTypeBitMask long getSupportedRadioAccessFamily() { try { ITelephony telephony = getITelephony(); if (telephony != null) { - return telephony.getRadioAccessFamily(getSlotIndex(), getOpPackageName()); + return (long) telephony.getRadioAccessFamily(getSlotIndex(), getOpPackageName()); } else { // This can happen when the ITelephony interface is not up yet. return NETWORK_TYPE_BITMASK_UNKNOWN; @@ -10178,11 +10280,14 @@ public class TelephonyManager { /** * Get whether reboot is required or not after making changes to modem configurations. - * @Return {@code True} if reboot is required after making changes to modem configurations, - * otherwise return {@code False}. + * The modem configuration change refers to switching from single SIM configuration to DSDS + * or the other way around. + * @Return {@code true} if reboot is required after making changes to modem configurations, + * otherwise return {@code false}. * * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isRebootRequiredForModemConfigChange() { try { diff --git a/telephony/java/android/telephony/ims/Rcs1To1Thread.java b/telephony/java/android/telephony/ims/Rcs1To1Thread.java index 709b3aa0f804..d4a78ffb77db 100644 --- a/telephony/java/android/telephony/ims/Rcs1To1Thread.java +++ b/telephony/java/android/telephony/ims/Rcs1To1Thread.java @@ -15,42 +15,70 @@ */ package android.telephony.ims; -import android.os.Parcel; +import android.annotation.NonNull; +import android.annotation.WorkerThread; /** * Rcs1To1Thread represents a single RCS conversation thread with a total of two - * {@link RcsParticipant}s. - * @hide - TODO(sahinc) make this public + * {@link RcsParticipant}s. Please see Section 5 (1-to-1 Messaging) - GSMA RCC.71 (RCS Universal + * Profile Service Definition Document) */ public class Rcs1To1Thread extends RcsThread { + private int mThreadId; + + /** + * Public constructor only for RcsMessageStoreController to initialize new threads. + * + * @hide + */ public Rcs1To1Thread(int threadId) { super(threadId); + mThreadId = threadId; } - public static final Creator<Rcs1To1Thread> CREATOR = new Creator<Rcs1To1Thread>() { - @Override - public Rcs1To1Thread createFromParcel(Parcel in) { - return new Rcs1To1Thread(in); - } - - @Override - public Rcs1To1Thread[] newArray(int size) { - return new Rcs1To1Thread[size]; - } - }; + /** + * @return Returns {@code false} as this is always a 1 to 1 thread. + */ + @Override + public boolean isGroup() { + return false; + } - protected Rcs1To1Thread(Parcel in) { - super(in); + /** + * {@link Rcs1To1Thread}s can fall back to SMS as a back-up protocol. This function returns the + * thread id to be used to query {@code content://mms-sms/conversation/#} to get the fallback + * thread. + * + * @return The thread id to be used to query the mms-sms authority + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public long getFallbackThreadId() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.get1To1ThreadFallbackThreadId(mThreadId)); } - @Override - public int describeContents() { - return 0; + /** + * If the RCS client allows falling back to SMS, it needs to create an MMS-SMS thread in the + * SMS/MMS Provider( see {@link android.provider.Telephony.MmsSms#CONTENT_CONVERSATIONS_URI}. + * Use this function to link the {@link Rcs1To1Thread} to the MMS-SMS thread. This function + * also updates the storage. + * + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setFallbackThreadId(long fallbackThreadId) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.set1To1ThreadFallbackThreadId(mThreadId, fallbackThreadId)); } - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(RCS_1_TO_1_TYPE); - super.writeToParcel(dest, flags); + /** + * @return Returns the {@link RcsParticipant} that receives the messages sent in this thread. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @NonNull + @WorkerThread + public RcsParticipant getRecipient() throws RcsMessageStoreException { + return new RcsParticipant( + RcsControllerCall.call(iRcs -> iRcs.get1To1ThreadOtherParticipantId(mThreadId))); } } diff --git a/telephony/java/android/telephony/ims/RcsControllerCall.java b/telephony/java/android/telephony/ims/RcsControllerCall.java new file mode 100644 index 000000000000..5512c4c7b19d --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsControllerCall.java @@ -0,0 +1,64 @@ +/* + * 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.telephony.ims; + +import android.content.Context; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.telephony.ims.aidl.IRcs; + +/** + * A wrapper class around RPC calls that {@link RcsMessageStore} APIs to minimize boilerplate code. + * + * @hide - not meant for public use + */ +class RcsControllerCall { + static <R> R call(RcsServiceCall<R> serviceCall) throws RcsMessageStoreException { + IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_RCS_SERVICE)); + if (iRcs == null) { + throw new RcsMessageStoreException("Could not connect to RCS storage service"); + } + + try { + return serviceCall.methodOnIRcs(iRcs); + } catch (RemoteException exception) { + throw new RcsMessageStoreException(exception.getMessage()); + } + } + + static void callWithNoReturn(RcsServiceCallWithNoReturn serviceCall) + throws RcsMessageStoreException { + IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_RCS_SERVICE)); + if (iRcs == null) { + throw new RcsMessageStoreException("Could not connect to RCS storage service"); + } + + try { + serviceCall.methodOnIRcs(iRcs); + } catch (RemoteException exception) { + throw new RcsMessageStoreException(exception.getMessage()); + } + } + + interface RcsServiceCall<R> { + R methodOnIRcs(IRcs iRcs) throws RemoteException; + } + + interface RcsServiceCallWithNoReturn { + void methodOnIRcs(IRcs iRcs) throws RemoteException; + } +} diff --git a/telephony/java/android/telephony/ims/RcsPart.aidl b/telephony/java/android/telephony/ims/RcsEvent.aidl index 8b8077d57676..08974e0a771c 100644 --- a/telephony/java/android/telephony/ims/RcsPart.aidl +++ b/telephony/java/android/telephony/ims/RcsEvent.aidl @@ -17,4 +17,4 @@ package android.telephony.ims; -parcelable RcsPart; +parcelable RcsEvent; diff --git a/telephony/java/android/telephony/ims/RcsEvent.java b/telephony/java/android/telephony/ims/RcsEvent.java new file mode 100644 index 000000000000..df62277f9ac1 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsEvent.java @@ -0,0 +1,61 @@ +/* + * 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.telephony.ims; + +import android.os.Parcel; + +/** + * The base class for events that can happen on {@link RcsParticipant}s and {@link RcsThread}s. + */ +public abstract class RcsEvent { + /** + * @hide + */ + protected final long mTimestamp; + + protected RcsEvent(long timestamp) { + mTimestamp = timestamp; + } + + /** + * @return Returns the time of when this event happened. The timestamp is defined as + * milliseconds passed after midnight, January 1, 1970 UTC + */ + public long getTimestamp() { + return mTimestamp; + } + + /** + * Persists the event to the data store + * + * @hide + */ + abstract void persist() throws RcsMessageStoreException; + + /** + * @hide + */ + RcsEvent(Parcel in) { + mTimestamp = in.readLong(); + } + + /** + * @hide + */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(mTimestamp); + } +} diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessage.aidl b/telephony/java/android/telephony/ims/RcsEventQueryParams.aidl index 6552a82c9072..f18c4dfd2dcd 100644 --- a/telephony/java/android/telephony/ims/RcsIncomingMessage.aidl +++ b/telephony/java/android/telephony/ims/RcsEventQueryParams.aidl @@ -17,4 +17,4 @@ package android.telephony.ims; -parcelable RcsIncomingMessage; +parcelable RcsEventQueryParams; diff --git a/telephony/java/android/telephony/ims/RcsEventQueryParams.java b/telephony/java/android/telephony/ims/RcsEventQueryParams.java new file mode 100644 index 000000000000..9dbfe4393213 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsEventQueryParams.java @@ -0,0 +1,319 @@ +/* + * 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.telephony.ims; + +import static android.provider.Telephony.RcsColumns.RcsEventTypes.ICON_CHANGED_EVENT_TYPE; +import static android.provider.Telephony.RcsColumns.RcsEventTypes.NAME_CHANGED_EVENT_TYPE; +import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE; +import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_JOINED_EVENT_TYPE; +import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_LEFT_EVENT_TYPE; + +import android.annotation.CheckResult; +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.security.InvalidParameterException; + +/** + * The parameters to pass into + * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} in order to select a + * subset of {@link RcsEvent}s present in the message store. + */ +public final class RcsEventQueryParams implements Parcelable { + /** + * Flag to be used with {@link Builder#setEventType(int)} to make + * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return all types of + * {@link RcsEvent}s + */ + public static final int ALL_EVENTS = -1; + + /** + * Flag to be used with {@link Builder#setEventType(int)} to make + * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return sub-types of + * {@link RcsGroupThreadEvent}s + */ + public static final int ALL_GROUP_THREAD_EVENTS = 0; + + /** + * Flag to be used with {@link Builder#setEventType(int)} to make + * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only + * {@link RcsParticipantAliasChangedEvent}s + */ + public static final int PARTICIPANT_ALIAS_CHANGED_EVENT = + PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE; + + /** + * Flag to be used with {@link Builder#setEventType(int)} to make + * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only + * {@link RcsGroupThreadParticipantJoinedEvent}s + */ + public static final int GROUP_THREAD_PARTICIPANT_JOINED_EVENT = + PARTICIPANT_JOINED_EVENT_TYPE; + + /** + * Flag to be used with {@link Builder#setEventType(int)} to make + * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only + * {@link RcsGroupThreadParticipantLeftEvent}s + */ + public static final int GROUP_THREAD_PARTICIPANT_LEFT_EVENT = + PARTICIPANT_LEFT_EVENT_TYPE; + + /** + * Flag to be used with {@link Builder#setEventType(int)} to make + * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only + * {@link RcsGroupThreadNameChangedEvent}s + */ + public static final int GROUP_THREAD_NAME_CHANGED_EVENT = NAME_CHANGED_EVENT_TYPE; + + /** + * Flag to be used with {@link Builder#setEventType(int)} to make + * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only + * {@link RcsGroupThreadIconChangedEvent}s + */ + public static final int GROUP_THREAD_ICON_CHANGED_EVENT = ICON_CHANGED_EVENT_TYPE; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({ALL_EVENTS, ALL_GROUP_THREAD_EVENTS, PARTICIPANT_ALIAS_CHANGED_EVENT, + GROUP_THREAD_PARTICIPANT_JOINED_EVENT, GROUP_THREAD_PARTICIPANT_LEFT_EVENT, + GROUP_THREAD_NAME_CHANGED_EVENT, GROUP_THREAD_ICON_CHANGED_EVENT}) + public @interface EventType { + } + + /** + * Flag to be used with {@link Builder#setSortProperty(int)} that makes the result set sorted + * in the order of creation for faster query results. + */ + public static final int SORT_BY_CREATION_ORDER = 0; + + /** + * Flag to be used with {@link Builder#setSortProperty(int)} that makes the result set sorted + * with respect to {@link RcsEvent#getTimestamp()} + */ + public static final int SORT_BY_TIMESTAMP = 1; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_TIMESTAMP}) + public @interface SortingProperty { + } + + /** + * The key to pass into a Bundle, for usage in RcsProvider.query(Bundle) + * @hide - not meant for public use + */ + public static final String EVENT_QUERY_PARAMETERS_KEY = "event_query_parameters"; + + // Which types of events the results should be limited to + private @EventType int mEventType; + // The property which the results should be sorted against + private int mSortingProperty; + // Whether the results should be sorted in ascending order + private boolean mIsAscending; + // The number of results that should be returned with this query + private int mLimit; + // The thread that the results are limited to + private int mThreadId; + + RcsEventQueryParams(@EventType int eventType, int threadId, + @SortingProperty int sortingProperty, boolean isAscending, int limit) { + mEventType = eventType; + mSortingProperty = sortingProperty; + mIsAscending = isAscending; + mLimit = limit; + mThreadId = threadId; + } + + /** + * @return Returns the type of {@link RcsEvent}s that this {@link RcsEventQueryParams} is + * set to query for. + */ + public @EventType int getEventType() { + return mEventType; + } + + /** + * @return Returns the number of {@link RcsEvent}s to be returned from the query. A value of + * 0 means there is no set limit. + */ + public int getLimit() { + return mLimit; + } + + /** + * @return Returns the property where the results should be sorted against. + * @see SortingProperty + */ + public int getSortingProperty() { + return mSortingProperty; + } + + /** + * @return Returns {@code true} if the result set will be sorted in ascending order, + * {@code false} if it will be sorted in descending order. + */ + public boolean getSortDirection() { + return mIsAscending; + } + + /** + * @return Returns the ID of the {@link RcsGroupThread} that the results are limited to. As this + * API exposes an ID, it should stay hidden. + * + * @hide + */ + public int getThreadId() { + return mThreadId; + } + + /** + * A helper class to build the {@link RcsEventQueryParams}. + */ + public static class Builder { + private @EventType int mEventType; + private @SortingProperty int mSortingProperty; + private boolean mIsAscending; + private int mLimit = 100; + private int mThreadId; + + /** + * Creates a new builder for {@link RcsEventQueryParams} to be used in + * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} + */ + public Builder() { + // empty implementation + } + + /** + * Desired number of events to be returned from the query. Passing in 0 will return all + * existing events at once. The limit defaults to 100. + * + * @param limit The number to limit the query result to. + * @return The same instance of the builder to chain parameters. + * @throws InvalidParameterException If the given limit is negative. + */ + @CheckResult + public Builder setResultLimit(@IntRange(from = 0) int limit) + throws InvalidParameterException { + if (limit < 0) { + throw new InvalidParameterException("The query limit must be non-negative"); + } + + mLimit = limit; + return this; + } + + /** + * Sets the type of events to be returned from the query. + * + * @param eventType The type of event to be returned. + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setEventType(@EventType int eventType) { + mEventType = eventType; + return this; + } + + /** + * Sets the property where the results should be sorted against. Defaults to + * {@link RcsEventQueryParams.SortingProperty#SORT_BY_CREATION_ORDER} + * + * @param sortingProperty against which property the results should be sorted + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setSortProperty(@SortingProperty int sortingProperty) { + mSortingProperty = sortingProperty; + return this; + } + + /** + * Sets whether the results should be sorted ascending or descending + * + * @param isAscending whether the results should be sorted ascending + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setSortDirection(boolean isAscending) { + mIsAscending = isAscending; + return this; + } + + /** + * Limits the results to the given {@link RcsGroupThread}. Setting this value prevents + * returning any instances of {@link RcsParticipantAliasChangedEvent}. + * + * @param groupThread The thread to limit the results to. + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setGroupThread(@NonNull RcsGroupThread groupThread) { + mThreadId = groupThread.getThreadId(); + return this; + } + + /** + * Builds the {@link RcsEventQueryParams} to use in + * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} + * + * @return An instance of {@link RcsEventQueryParams} to use with the event query. + */ + public RcsEventQueryParams build() { + return new RcsEventQueryParams(mEventType, mThreadId, mSortingProperty, + mIsAscending, mLimit); + } + } + + private RcsEventQueryParams(Parcel in) { + mEventType = in.readInt(); + mThreadId = in.readInt(); + mSortingProperty = in.readInt(); + mIsAscending = in.readBoolean(); + mLimit = in.readInt(); + } + + public static final Creator<RcsEventQueryParams> CREATOR = + new Creator<RcsEventQueryParams>() { + @Override + public RcsEventQueryParams createFromParcel(Parcel in) { + return new RcsEventQueryParams(in); + } + + @Override + public RcsEventQueryParams[] newArray(int size) { + return new RcsEventQueryParams[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mEventType); + dest.writeInt(mThreadId); + dest.writeInt(mSortingProperty); + dest.writeBoolean(mIsAscending); + dest.writeInt(mLimit); + } +} diff --git a/telephony/java/android/telephony/ims/Rcs1To1Thread.aidl b/telephony/java/android/telephony/ims/RcsEventQueryResult.aidl index 9fdc41d2bd5f..7d133350973c 100644 --- a/telephony/java/android/telephony/ims/Rcs1To1Thread.aidl +++ b/telephony/java/android/telephony/ims/RcsEventQueryResult.aidl @@ -17,4 +17,4 @@ package android.telephony.ims; -parcelable Rcs1To1Thread; +parcelable RcsEventQueryResult; diff --git a/telephony/java/android/telephony/ims/RcsEventQueryResult.java b/telephony/java/android/telephony/ims/RcsEventQueryResult.java new file mode 100644 index 000000000000..c30e4ccd7aa2 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsEventQueryResult.java @@ -0,0 +1,89 @@ +/* + * 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.telephony.ims; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.List; + +/** + * The result of a {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} + * call. This class allows getting the token for querying the next batch of events in order to + * prevent handling large amounts of data at once. + */ +public final class RcsEventQueryResult implements Parcelable { + private RcsQueryContinuationToken mContinuationToken; + private List<RcsEvent> mEvents; + + /** + * Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController} + * to create query results + * + * @hide + */ + public RcsEventQueryResult( + RcsQueryContinuationToken continuationToken, + List<RcsEvent> events) { + mContinuationToken = continuationToken; + mEvents = events; + } + + /** + * Returns a token to call + * {@link RcsMessageStore#getRcsEvents(RcsQueryContinuationToken)} + * to get the next batch of {@link RcsEvent}s. + */ + public RcsQueryContinuationToken getContinuationToken() { + return mContinuationToken; + } + + /** + * Returns all the {@link RcsEvent}s in the current query result. Call {@link + * RcsMessageStore#getRcsEvents(RcsQueryContinuationToken)} to get the next batch + * of {@link RcsEvent}s. + */ + public List<RcsEvent> getEvents() { + return mEvents; + } + + private RcsEventQueryResult(Parcel in) { + mContinuationToken = in.readParcelable(RcsQueryContinuationToken.class.getClassLoader()); + } + + public static final Creator<RcsEventQueryResult> CREATOR = new Creator<RcsEventQueryResult>() { + @Override + public RcsEventQueryResult createFromParcel(Parcel in) { + return new RcsEventQueryResult(in); + } + + @Override + public RcsEventQueryResult[] newArray(int size) { + return new RcsEventQueryResult[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(mContinuationToken, flags); + } +} diff --git a/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.aidl b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.aidl new file mode 100644 index 000000000000..155219038d7b --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.aidl @@ -0,0 +1,20 @@ +/* + * + * 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.telephony.ims; + +parcelable RcsFileTransferCreationParams; diff --git a/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java new file mode 100644 index 000000000000..14af8ea63a67 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java @@ -0,0 +1,358 @@ +/* + * 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.telephony.ims; + +import android.annotation.CheckResult; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Pass an instance of this class to + * {@link RcsMessage#insertFileTransfer(RcsFileTransferCreationParams)} create an + * {@link RcsFileTransferPart} and save it into storage. + */ +public final class RcsFileTransferCreationParams implements Parcelable { + private String mRcsFileTransferSessionId; + private Uri mContentUri; + private String mContentMimeType; + private long mFileSize; + private long mTransferOffset; + private int mWidth; + private int mHeight; + private long mMediaDuration; + private Uri mPreviewUri; + private String mPreviewMimeType; + private @RcsFileTransferPart.RcsFileTransferStatus int mFileTransferStatus; + + /** + * @return Returns the globally unique RCS file transfer session ID for the + * {@link RcsFileTransferPart} to be created + */ + public String getRcsFileTransferSessionId() { + return mRcsFileTransferSessionId; + } + + /** + * @return Returns the URI for the content of the {@link RcsFileTransferPart} to be created + */ + public Uri getContentUri() { + return mContentUri; + } + + /** + * @return Returns the MIME type for the content of the {@link RcsFileTransferPart} to be + * created + */ + public String getContentMimeType() { + return mContentMimeType; + } + + /** + * @return Returns the file size in bytes for the {@link RcsFileTransferPart} to be created + */ + public long getFileSize() { + return mFileSize; + } + + /** + * @return Returns the transfer offset for the {@link RcsFileTransferPart} to be created. The + * file transfer offset is defined as how many bytes have been successfully transferred to the + * receiver of this file transfer. + */ + public long getTransferOffset() { + return mTransferOffset; + } + + /** + * @return Returns the width of the {@link RcsFileTransferPart} to be created. The value is in + * pixels. + */ + public int getWidth() { + return mWidth; + } + + /** + * @return Returns the height of the {@link RcsFileTransferPart} to be created. The value is in + * pixels. + */ + public int getHeight() { + return mHeight; + } + + /** + * @return Returns the duration of the {@link RcsFileTransferPart} to be created. + */ + public long getMediaDuration() { + return mMediaDuration; + } + + /** + * @return Returns the URI of the preview of the content of the {@link RcsFileTransferPart} to + * be created. This should only be used for multi-media files. + */ + public Uri getPreviewUri() { + return mPreviewUri; + } + + /** + * @return Returns the MIME type of the preview of the content of the + * {@link RcsFileTransferPart} to be created. This should only be used for multi-media files. + */ + public String getPreviewMimeType() { + return mPreviewMimeType; + } + + /** + * @return Returns the status of the {@link RcsFileTransferPart} to be created. + */ + public @RcsFileTransferPart.RcsFileTransferStatus int getFileTransferStatus() { + return mFileTransferStatus; + } + + /** + * @hide + */ + RcsFileTransferCreationParams(Builder builder) { + mRcsFileTransferSessionId = builder.mRcsFileTransferSessionId; + mContentUri = builder.mContentUri; + mContentMimeType = builder.mContentMimeType; + mFileSize = builder.mFileSize; + mTransferOffset = builder.mTransferOffset; + mWidth = builder.mWidth; + mHeight = builder.mHeight; + mMediaDuration = builder.mLength; + mPreviewUri = builder.mPreviewUri; + mPreviewMimeType = builder.mPreviewMimeType; + mFileTransferStatus = builder.mFileTransferStatus; + } + + /** + * A builder to create instances of {@link RcsFileTransferCreationParams} + */ + public class Builder { + private String mRcsFileTransferSessionId; + private Uri mContentUri; + private String mContentMimeType; + private long mFileSize; + private long mTransferOffset; + private int mWidth; + private int mHeight; + private long mLength; + private Uri mPreviewUri; + private String mPreviewMimeType; + private @RcsFileTransferPart.RcsFileTransferStatus int mFileTransferStatus; + + /** + * Sets the globally unique RCS file transfer session ID for the {@link RcsFileTransferPart} + * to be created + * + * @param sessionId The RCS file transfer session ID + * @return The same instance of {@link Builder} to chain methods + */ + @CheckResult + public Builder setFileTransferSessionId(String sessionId) { + mRcsFileTransferSessionId = sessionId; + return this; + } + + /** + * Sets the URI for the content of the {@link RcsFileTransferPart} to be created + * + * @param contentUri The URI for the file + * @return The same instance of {@link Builder} to chain methods + */ + @CheckResult + public Builder setContentUri(Uri contentUri) { + mContentUri = contentUri; + return this; + } + + /** + * Sets the MIME type for the content of the {@link RcsFileTransferPart} to be created + * + * @param contentType The MIME type of the file + * @return The same instance of {@link Builder} to chain methods + */ + @CheckResult + public Builder setContentMimeType(String contentType) { + mContentMimeType = contentType; + return this; + } + + /** + * Sets the file size for the {@link RcsFileTransferPart} to be created + * + * @param size The size of the file in bytes + * @return The same instance of {@link Builder} to chain methods + */ + @CheckResult + public Builder setFileSize(long size) { + mFileSize = size; + return this; + } + + /** + * Sets the transfer offset for the {@link RcsFileTransferPart} to be created. The file + * transfer offset is defined as how many bytes have been successfully transferred to the + * receiver of this file transfer. + * + * @param offset The transfer offset in bytes + * @return The same instance of {@link Builder} to chain methods + */ + @CheckResult + public Builder setTransferOffset(long offset) { + mTransferOffset = offset; + return this; + } + + /** + * Sets the width of the {@link RcsFileTransferPart} to be created. This should only be used + * for multi-media files. + * + * @param width The width of the multi-media file in pixels. + * @return The same instance of {@link Builder} to chain methods + */ + @CheckResult + public Builder setWidth(int width) { + mWidth = width; + return this; + } + + /** + * Sets the height of the {@link RcsFileTransferPart} to be created. This should only be + * used for multi-media files. + * + * @param height The height of the multi-media file in pixels. + * @return The same instance of {@link Builder} to chain methods + */ + @CheckResult + public Builder setHeight(int height) { + mHeight = height; + return this; + } + + /** + * Sets the length of the {@link RcsFileTransferPart} to be created. This should only be + * used for multi-media files such as audio or video. + * + * @param length The length of the multi-media file in milliseconds + * @return The same instance of {@link Builder} to chain methods + */ + @CheckResult + public Builder setMediaDuration(long length) { + mLength = length; + return this; + } + + /** + * Sets the URI of the preview of the content of the {@link RcsFileTransferPart} to be + * created. This should only be used for multi-media files. + * + * @param previewUri The URI of the preview of the file transfer + * @return The same instance of {@link Builder} to chain methods + */ + @CheckResult + public Builder setPreviewUri(Uri previewUri) { + mPreviewUri = previewUri; + return this; + } + + /** + * Sets the MIME type of the preview of the content of the {@link RcsFileTransferPart} to + * be created. This should only be used for multi-media files. + * + * @param previewType The MIME type of the preview of the file transfer + * @return The same instance of {@link Builder} to chain methods + */ + @CheckResult + public Builder setPreviewMimeType(String previewType) { + mPreviewMimeType = previewType; + return this; + } + + /** + * Sets the status of the {@link RcsFileTransferPart} to be created. + * + * @param status The status of the file transfer + * @return The same instance of {@link Builder} to chain methods + */ + @CheckResult + public Builder setFileTransferStatus( + @RcsFileTransferPart.RcsFileTransferStatus int status) { + mFileTransferStatus = status; + return this; + } + + /** + * Creates an instance of {@link RcsFileTransferCreationParams} with the given + * parameters. + * + * @return The same instance of {@link Builder} to chain methods + * @see RcsMessage#insertFileTransfer(RcsFileTransferCreationParams) + */ + public RcsFileTransferCreationParams build() { + return new RcsFileTransferCreationParams(this); + } + } + + private RcsFileTransferCreationParams(Parcel in) { + mRcsFileTransferSessionId = in.readString(); + mContentUri = in.readParcelable(Uri.class.getClassLoader()); + mContentMimeType = in.readString(); + mFileSize = in.readLong(); + mTransferOffset = in.readLong(); + mWidth = in.readInt(); + mHeight = in.readInt(); + mMediaDuration = in.readLong(); + mPreviewUri = in.readParcelable(Uri.class.getClassLoader()); + mPreviewMimeType = in.readString(); + mFileTransferStatus = in.readInt(); + } + + public static final Creator<RcsFileTransferCreationParams> CREATOR = + new Creator<RcsFileTransferCreationParams>() { + @Override + public RcsFileTransferCreationParams createFromParcel(Parcel in) { + return new RcsFileTransferCreationParams(in); + } + + @Override + public RcsFileTransferCreationParams[] newArray(int size) { + return new RcsFileTransferCreationParams[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mRcsFileTransferSessionId); + dest.writeParcelable(mContentUri, flags); + dest.writeString(mContentMimeType); + dest.writeLong(mFileSize); + dest.writeLong(mTransferOffset); + dest.writeInt(mWidth); + dest.writeInt(mHeight); + dest.writeLong(mMediaDuration); + dest.writeParcelable(mPreviewUri, flags); + dest.writeString(mPreviewMimeType); + dest.writeInt(mFileTransferStatus); + } +} diff --git a/telephony/java/android/telephony/ims/RcsFileTransferPart.java b/telephony/java/android/telephony/ims/RcsFileTransferPart.java index 39c58dd9c15b..9531c2e2f981 100644 --- a/telephony/java/android/telephony/ims/RcsFileTransferPart.java +++ b/telephony/java/android/telephony/ims/RcsFileTransferPart.java @@ -15,34 +15,344 @@ */ package android.telephony.ims; -import android.os.Parcel; +import android.annotation.IntDef; +import android.annotation.Nullable; +import android.annotation.WorkerThread; +import android.net.Uri; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** - * A part of a composite {@link RcsMessage} that holds a file transfer. - * @hide - TODO(sahinc) make this public + * A part of a composite {@link RcsMessage} that holds a file transfer. Please see Section 7 + * (File Transfer) - GSMA RCC.71 (RCS Universal Profile Service Definition Document) */ -public class RcsFileTransferPart extends RcsPart { - public static final Creator<RcsFileTransferPart> CREATOR = new Creator<RcsFileTransferPart>() { - @Override - public RcsFileTransferPart createFromParcel(Parcel in) { - return new RcsFileTransferPart(in); - } +public class RcsFileTransferPart { + /** + * The status to indicate that this {@link RcsFileTransferPart} is not set yet. + */ + public static final int NOT_SET = 0; + + /** + * The status to indicate that this {@link RcsFileTransferPart} is a draft and is not in the + * process of sending yet. + */ + public static final int DRAFT = 1; + + /** + * The status to indicate that this {@link RcsFileTransferPart} is actively being sent right + * now. + */ + public static final int SENDING = 2; + + /** + * The status to indicate that this {@link RcsFileTransferPart} was being sent, but the user has + * paused the sending process. + */ + public static final int SENDING_PAUSED = 3; + + /** + * The status to indicate that this {@link RcsFileTransferPart} was attempted, but failed to + * send. + */ + public static final int SENDING_FAILED = 4; + + /** + * The status to indicate that this {@link RcsFileTransferPart} is permanently cancelled to + * send. + */ + public static final int SENDING_CANCELLED = 5; + + /** + * The status to indicate that this {@link RcsFileTransferPart} is actively being downloaded + * right now. + */ + public static final int DOWNLOADING = 6; + + /** + * The status to indicate that this {@link RcsFileTransferPart} was being downloaded, but the + * user paused the downloading process. + */ + public static final int DOWNLOADING_PAUSED = 7; - @Override - public RcsFileTransferPart[] newArray(int size) { - return new RcsFileTransferPart[size]; - } - }; + /** + * The status to indicate that this {@link RcsFileTransferPart} was attempted, but failed to + * download. + */ + public static final int DOWNLOADING_FAILED = 8; + + /** + * The status to indicate that this {@link RcsFileTransferPart} is permanently cancelled to + * download. + */ + public static final int DOWNLOADING_CANCELLED = 9; + + /** + * The status to indicate that this {@link RcsFileTransferPart} was successfully sent or + * received. + */ + public static final int SUCCEEDED = 10; + + @IntDef({ + DRAFT, SENDING, SENDING_PAUSED, SENDING_FAILED, SENDING_CANCELLED, DOWNLOADING, + DOWNLOADING_PAUSED, DOWNLOADING_FAILED, DOWNLOADING_CANCELLED, SUCCEEDED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RcsFileTransferStatus { + } + + private int mId; + + /** + * @hide + */ + RcsFileTransferPart(int id) { + mId = id; + } + + /** + * @hide + */ + public void setId(int id) { + mId = id; + } + + /** + * @hide + */ + public int getId() { + return mId; + } + + /** + * Sets the RCS file transfer session ID for this file transfer and persists into storage. + * + * @param sessionId The session ID to be used for this file transfer. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setFileTransferSessionId(String sessionId) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferSessionId(mId, sessionId)); + } + + /** + * @return Returns the file transfer session ID. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public String getFileTransferSessionId() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getFileTransferSessionId(mId)); + } + + /** + * Sets the content URI for this file transfer and persists into storage. The file transfer + * should be reachable using this URI. + * + * @param contentUri The URI for this file transfer. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setContentUri(Uri contentUri) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferContentUri(mId, contentUri)); + } + + /** + * @return Returns the URI for this file transfer + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @Nullable + @WorkerThread + public Uri getContentUri() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getFileTransferContentUri(mId)); + } + + /** + * Sets the MIME type of this file transfer and persists into storage. Whether this type + * actually matches any known or supported types is not checked. + * + * @param contentMimeType The type of this file transfer. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setContentMimeType(String contentMimeType) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setFileTransferContentType(mId, contentMimeType)); + } + + /** + * @return Returns the content type of this file transfer + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + @Nullable + public String getContentMimeType() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getFileTransferContentType(mId)); + } + + /** + * Sets the content length (i.e. file size) for this file transfer and persists into storage. + * + * @param contentLength The content length of this file transfer + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setFileSize(long contentLength) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setFileTransferFileSize(mId, contentLength)); + } + + /** + * @return Returns the content length (i.e. file size) for this file transfer. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public long getFileSize() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getFileTransferFileSize(mId)); + } + + /** + * Sets the transfer offset for this file transfer and persists into storage. The file transfer + * offset is defined as how many bytes have been successfully transferred to the receiver of + * this file transfer. + * + * @param transferOffset The transfer offset for this file transfer. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setTransferOffset(long transferOffset) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setFileTransferTransferOffset(mId, transferOffset)); + } + + /** + * @return Returns the number of bytes that have successfully transferred. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public long getTransferOffset() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getFileTransferTransferOffset(mId)); + } + + /** + * Sets the status for this file transfer and persists into storage. + * + * @param status The status of this file transfer. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setFileTransferStatus(@RcsFileTransferStatus int status) + throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferStatus(mId, status)); + } + + /** + * @return Returns the status of this file transfer. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public @RcsFileTransferStatus int getFileTransferStatus() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getFileTransferStatus(mId)); + } + + /** + * @return Returns the width of this multi-media message part in pixels. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public int getWidth() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getFileTransferWidth(mId)); + } + + /** + * Sets the width of this RCS multi-media message part and persists into storage. + * + * @param width The width value in pixels + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setWidth(int width) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferWidth(mId, width)); + } + + /** + * @return Returns the height of this multi-media message part in pixels. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public int getHeight() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getFileTransferHeight(mId)); + } + + /** + * Sets the height of this RCS multi-media message part and persists into storage. + * + * @param height The height value in pixels + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setHeight(int height) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferHeight(mId, height)); + } + + /** + * @return Returns the length of this multi-media file (e.g. video or audio) in milliseconds. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public long getLength() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getFileTransferLength(mId)); + } + + /** + * Sets the length of this multi-media file (e.g. video or audio) and persists into storage. + * + * @param length The length of the file in milliseconds. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setLength(long length) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferLength(mId, length)); + } + + /** + * @return Returns the URI for the preview of this multi-media file (e.g. an image thumbnail for + * a video) + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public Uri getPreviewUri() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getFileTransferPreviewUri(mId)); + } - protected RcsFileTransferPart(Parcel in) { + /** + * Sets the URI for the preview of this multi-media file and persists into storage. + * + * @param previewUri The URI to access to the preview file. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setPreviewUri(Uri previewUri) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferPreviewUri(mId, previewUri)); } - @Override - public int describeContents() { - return 0; + /** + * @return Returns the MIME type of this multi-media file's preview. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public String getPreviewMimeType() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getFileTransferPreviewType(mId)); } - @Override - public void writeToParcel(Parcel dest, int flags) { + /** + * Sets the MIME type for this multi-media file's preview and persists into storage. + * + * @param previewMimeType The MIME type for the preview + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setPreviewMimeType(String previewMimeType) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setFileTransferPreviewType(mId, previewMimeType)); } } diff --git a/telephony/java/android/telephony/ims/RcsGroupThread.java b/telephony/java/android/telephony/ims/RcsGroupThread.java index d954b2d70ac3..6e17bc2a685f 100644 --- a/telephony/java/android/telephony/ims/RcsGroupThread.java +++ b/telephony/java/android/telephony/ims/RcsGroupThread.java @@ -15,38 +15,189 @@ */ package android.telephony.ims; -import android.os.Parcel; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.WorkerThread; +import android.net.Uri; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; /** * RcsGroupThread represents a single RCS conversation thread where {@link RcsParticipant}s can join - * or leave. - * @hide - TODO(sahinc) make this public + * or leave. Please see Section 6 (Group Chat) - GSMA RCC.71 (RCS Universal Profile Service + * Definition Document) */ public class RcsGroupThread extends RcsThread { - public static final Creator<RcsGroupThread> CREATOR = new Creator<RcsGroupThread>() { - @Override - public RcsGroupThread createFromParcel(Parcel in) { - return new RcsGroupThread(in); + /** + * Public constructor only for RcsMessageStoreController to initialize new threads. + * + * @hide + */ + public RcsGroupThread(int threadId) { + super(threadId); + } + + /** + * @return Returns {@code true} as this is always a group thread + */ + @Override + public boolean isGroup() { + return true; + } + + /** + * @return Returns the given name of this {@link RcsGroupThread}. Please see US6-2 - GSMA RCC.71 + * (RCS Universal Profile Service Definition Document) + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @Nullable + @WorkerThread + public String getGroupName() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getGroupThreadName(mThreadId)); + } + + /** + * Sets the name of this {@link RcsGroupThread} and saves it into storage. Please see US6-2 - + * GSMA RCC.71 (RCS Universal Profile Service Definition Document) + * + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setGroupName(String groupName) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setGroupThreadName(mThreadId, groupName)); + } + + /** + * @return Returns a URI that points to the group's icon {@link RcsGroupThread}. Please see + * US6-2 - GSMA RCC.71 (RCS Universal Profile Service Definition Document) + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @Nullable + public Uri getGroupIcon() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getGroupThreadIcon(mThreadId)); + } + + /** + * Sets the icon for this {@link RcsGroupThread} and saves it into storage. Please see US6-2 - + * GSMA RCC.71 (RCS Universal Profile Service Definition Document) + * + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setGroupIcon(@Nullable Uri groupIcon) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setGroupThreadIcon(mThreadId, groupIcon)); + } + + /** + * @return Returns the owner of this thread or {@code null} if there doesn't exist an owner + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @Nullable + @WorkerThread + public RcsParticipant getOwner() throws RcsMessageStoreException { + return new RcsParticipant(RcsControllerCall.call( + iRcs -> iRcs.getGroupThreadOwner(mThreadId))); + } + + /** + * Sets the owner of this {@link RcsGroupThread} and saves it into storage. This is intended to + * be used for selecting a new owner for a group thread if the owner leaves the thread. The + * owner needs to be in the list of existing participants. + * + * @param participant The new owner of the thread. {@code null} values are allowed. + * @throws RcsMessageStoreException if the operation could not be persisted into storage + */ + @WorkerThread + public void setOwner(@Nullable RcsParticipant participant) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setGroupThreadOwner(mThreadId, participant.getId())); + } + + /** + * Adds a new {@link RcsParticipant} to this group thread and persists into storage. If the user + * is actively participating in this {@link RcsGroupThread}, an {@link RcsParticipant} on behalf + * of them should be added. + * + * @param participant The new participant to be added to the thread. + * @throws RcsMessageStoreException if the operation could not be persisted into storage + */ + @WorkerThread + public void addParticipant(@NonNull RcsParticipant participant) + throws RcsMessageStoreException { + if (participant == null) { + return; } - @Override - public RcsGroupThread[] newArray(int size) { - return new RcsGroupThread[size]; + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.addParticipantToGroupThread(mThreadId, participant.getId())); + } + + /** + * Removes an {@link RcsParticipant} from this group thread and persists into storage. If the + * removed participant was the owner of this group, the owner will become null. + * + * @throws RcsMessageStoreException if the operation could not be persisted into storage + */ + @WorkerThread + public void removeParticipant(@NonNull RcsParticipant participant) + throws RcsMessageStoreException { + if (participant == null) { + return; } - }; - protected RcsGroupThread(Parcel in) { - super(in); + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.removeParticipantFromGroupThread(mThreadId, participant.getId())); } - @Override - public int describeContents() { - return 0; + /** + * Returns the set of {@link RcsParticipant}s that contribute to this group thread. The + * returned set does not support modifications, please use + * {@link RcsGroupThread#addParticipant(RcsParticipant)} + * and {@link RcsGroupThread#removeParticipant(RcsParticipant)} instead. + * + * @return the immutable set of {@link RcsParticipant} in this group thread. + * @throws RcsMessageStoreException if the values could not be read from the storage + */ + @WorkerThread + @NonNull + public Set<RcsParticipant> getParticipants() throws RcsMessageStoreException { + RcsParticipantQueryParams queryParameters = + new RcsParticipantQueryParams.Builder().setThread(this).build(); + + RcsParticipantQueryResult queryResult = RcsControllerCall.call( + iRcs -> iRcs.getParticipants(queryParameters)); + + List<RcsParticipant> participantList = queryResult.getParticipants(); + Set<RcsParticipant> participantSet = new LinkedHashSet<>(participantList); + return Collections.unmodifiableSet(participantSet); } - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(RCS_GROUP_TYPE); - super.writeToParcel(dest, flags); + /** + * Returns the conference URI for this {@link RcsGroupThread}. Please see 4.4.5.2 - GSMA RCC.53 + * (RCS Device API 1.6 Specification + * + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @Nullable + @WorkerThread + public Uri getConferenceUri() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getGroupThreadConferenceUri(mThreadId)); + } + + /** + * Sets the conference URI for this {@link RcsGroupThread} and persists into storage. Please see + * 4.4.5.2 - GSMA RCC.53 (RCS Device API 1.6 Specification + * + * @param conferenceUri The URI as String to be used as the conference URI. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @Nullable + @WorkerThread + public void setConferenceUri(Uri conferenceUri) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setGroupThreadConferenceUri(mThreadId, conferenceUri)); } } diff --git a/telephony/java/android/telephony/ims/RcsLocationPart.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.aidl index 4fe5ca97a30d..77a23722f080 100644 --- a/telephony/java/android/telephony/ims/RcsLocationPart.aidl +++ b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.aidl @@ -1,5 +1,4 @@ /* - * * Copyright 2019, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,4 +16,4 @@ package android.telephony.ims; -parcelable RcsLocationPart; +parcelable RcsGroupThreadEvent; diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java new file mode 100644 index 000000000000..609b1740a536 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java @@ -0,0 +1,68 @@ +/* + * 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.telephony.ims; + +import android.annotation.NonNull; +import android.os.Parcel; + +/** + * An event that happened on an {@link RcsGroupThread}. + */ +public abstract class RcsGroupThreadEvent extends RcsEvent { + private final int mRcsGroupThreadId; + private final int mOriginatingParticipantId; + + RcsGroupThreadEvent(long timestamp, int rcsGroupThreadId, + int originatingParticipantId) { + super(timestamp); + mRcsGroupThreadId = rcsGroupThreadId; + mOriginatingParticipantId = originatingParticipantId; + } + + /** + * @return Returns the {@link RcsGroupThread} that this event happened on. + */ + @NonNull + public RcsGroupThread getRcsGroupThread() { + return new RcsGroupThread(mRcsGroupThreadId); + } + + /** + * @return Returns the {@link RcsParticipant} that performed the event. + */ + @NonNull + public RcsParticipant getOriginatingParticipant() { + return new RcsParticipant(mOriginatingParticipantId); + } + + /** + * @hide + */ + RcsGroupThreadEvent(Parcel in) { + super(in); + mRcsGroupThreadId = in.readInt(); + mOriginatingParticipantId = in.readInt(); + } + + /** + * @hide + */ + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(mRcsGroupThreadId); + dest.writeInt(mOriginatingParticipantId); + } +} diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.aidl new file mode 100644 index 000000000000..daea7922f3df --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.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.telephony.ims; + +parcelable RcsGroupThreadIconChangedEvent; diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java new file mode 100644 index 000000000000..e768439d6cfa --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java @@ -0,0 +1,109 @@ +/* + * 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.telephony.ims; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * An event that indicates an {@link RcsGroupThread}'s icon was changed. Please see R6-2-5 - GSMA + * RCC.71 (RCS Universal Profile Service Definition Document) + */ +public final class RcsGroupThreadIconChangedEvent extends RcsGroupThreadEvent implements + Parcelable { + private final Uri mNewIcon; + + /** + * Creates a new {@link RcsGroupThreadIconChangedEvent}. This event is not persisted into + * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called. + * + * @param timestamp The timestamp of when this event happened, in milliseconds passed after + * midnight, January 1st, 1970 UTC + * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on + * @param originatingParticipant The {@link RcsParticipant} that changed the + * {@link RcsGroupThread}'s icon. + * @param newIcon {@link Uri} to the new icon of this {@link RcsGroupThread} + * @see RcsMessageStore#persistRcsEvent(RcsEvent) + */ + public RcsGroupThreadIconChangedEvent(long timestamp, @NonNull RcsGroupThread rcsGroupThread, + @NonNull RcsParticipant originatingParticipant, @Nullable Uri newIcon) { + super(timestamp, rcsGroupThread.getThreadId(), originatingParticipant.getId()); + mNewIcon = newIcon; + } + + /** + * @hide - internal constructor for queries + */ + public RcsGroupThreadIconChangedEvent(long timestamp, int rcsGroupThreadId, + int originatingParticipantId, @Nullable Uri newIcon) { + super(timestamp, rcsGroupThreadId, originatingParticipantId); + mNewIcon = newIcon; + } + + /** + * @return Returns the {@link Uri} to the icon of the {@link RcsGroupThread} after this + * {@link RcsGroupThreadIconChangedEvent} occured. + */ + @Nullable + public Uri getNewIcon() { + return mNewIcon; + } + + /** + * Persists the event to the data store. + * + * @hide - not meant for public use. + */ + @Override + public void persist() throws RcsMessageStoreException { + // TODO ensure failure throws + RcsControllerCall.call(iRcs -> iRcs.createGroupThreadIconChangedEvent( + getTimestamp(), getRcsGroupThread().getThreadId(), + getOriginatingParticipant().getId(), mNewIcon)); + } + + public static final Creator<RcsGroupThreadIconChangedEvent> CREATOR = + new Creator<RcsGroupThreadIconChangedEvent>() { + @Override + public RcsGroupThreadIconChangedEvent createFromParcel(Parcel in) { + return new RcsGroupThreadIconChangedEvent(in); + } + + @Override + public RcsGroupThreadIconChangedEvent[] newArray(int size) { + return new RcsGroupThreadIconChangedEvent[size]; + } + }; + + private RcsGroupThreadIconChangedEvent(Parcel in) { + super(in); + mNewIcon = in.readParcelable(Uri.class.getClassLoader()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeParcelable(mNewIcon, flags); + } +} diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.aidl new file mode 100644 index 000000000000..3ed9bd11dc70 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.aidl @@ -0,0 +1,20 @@ +/* + * + * 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.telephony.ims; + +parcelable RcsGroupThreadNameChangedEvent; diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java new file mode 100644 index 000000000000..02030bc6a2ec --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java @@ -0,0 +1,107 @@ +/* + * 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.telephony.ims; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * An event that indicates an {@link RcsGroupThread}'s name was changed. Please see R6-2-5 - GSMA + * RCC.71 (RCS Universal Profile Service Definition Document) + */ +public final class RcsGroupThreadNameChangedEvent extends RcsGroupThreadEvent implements + Parcelable { + private final String mNewName; + + /** + * Creates a new {@link RcsGroupThreadNameChangedEvent}. This event is not persisted into + * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called. + * + * @param timestamp The timestamp of when this event happened, in milliseconds passed after + * midnight, January 1st, 1970 UTC + * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on + * @param originatingParticipant The {@link RcsParticipant} that changed the + * {@link RcsGroupThread}'s icon. + * @param newName The new name of the {@link RcsGroupThread} + * @see RcsMessageStore#persistRcsEvent(RcsEvent) + */ + public RcsGroupThreadNameChangedEvent(long timestamp, @NonNull RcsGroupThread rcsGroupThread, + @NonNull RcsParticipant originatingParticipant, @Nullable String newName) { + super(timestamp, rcsGroupThread.getThreadId(), originatingParticipant.getId()); + mNewName = newName; + } + + /** + * @hide - internal constructor for queries + */ + public RcsGroupThreadNameChangedEvent(long timestamp, int rcsGroupThreadId, + int originatingParticipantId, @Nullable String newName) { + super(timestamp, rcsGroupThreadId, originatingParticipantId); + mNewName = newName; + } + + /** + * @return Returns the name of this {@link RcsGroupThread} after this + * {@link RcsGroupThreadNameChangedEvent} happened. + */ + @Nullable + public String getNewName() { + return mNewName; + } + + /** + * Persists the event to the data store. + * + * @hide - not meant for public use. + */ + @Override + public void persist() throws RcsMessageStoreException { + RcsControllerCall.call(iRcs -> iRcs.createGroupThreadNameChangedEvent( + getTimestamp(), getRcsGroupThread().getThreadId(), + getOriginatingParticipant().getId(), mNewName)); + } + + public static final Creator<RcsGroupThreadNameChangedEvent> CREATOR = + new Creator<RcsGroupThreadNameChangedEvent>() { + @Override + public RcsGroupThreadNameChangedEvent createFromParcel(Parcel in) { + return new RcsGroupThreadNameChangedEvent(in); + } + + @Override + public RcsGroupThreadNameChangedEvent[] newArray(int size) { + return new RcsGroupThreadNameChangedEvent[size]; + } + }; + + private RcsGroupThreadNameChangedEvent(Parcel in) { + super(in); + mNewName = in.readString(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeString(mNewName); + } +} diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.aidl new file mode 100644 index 000000000000..420abffa067a --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.aidl @@ -0,0 +1,20 @@ +/* + * + * Copyright 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.telephony.ims; + +parcelable RcsGroupThreadParticipantJoinedEvent; diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java new file mode 100644 index 000000000000..0d1a5730f0a0 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java @@ -0,0 +1,107 @@ +/* + * 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.telephony.ims; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * An event that indicates an RCS participant has joined an {@link RcsThread}. Please see US6-3 - + * GSMA RCC.71 (RCS Universal Profile Service Definition Document) + */ +public final class RcsGroupThreadParticipantJoinedEvent extends RcsGroupThreadEvent implements + Parcelable { + private final int mJoinedParticipantId; + + /** + * Creates a new {@link RcsGroupThreadParticipantJoinedEvent}. This event is not persisted into + * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called. + * + * @param timestamp The timestamp of when this event happened, in milliseconds passed after + * midnight, January 1st, 1970 UTC + * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on + * @param originatingParticipant The {@link RcsParticipant} that added or invited the new + * {@link RcsParticipant} into the {@link RcsGroupThread} + * @param joinedParticipant The new {@link RcsParticipant} that joined the + * {@link RcsGroupThread} + * @see RcsMessageStore#persistRcsEvent(RcsEvent) + */ + public RcsGroupThreadParticipantJoinedEvent(long timestamp, + @NonNull RcsGroupThread rcsGroupThread, @NonNull RcsParticipant originatingParticipant, + @NonNull RcsParticipant joinedParticipant) { + super(timestamp, rcsGroupThread.getThreadId(), originatingParticipant.getId()); + mJoinedParticipantId = joinedParticipant.getId(); + } + + /** + * @hide - internal constructor for queries + */ + public RcsGroupThreadParticipantJoinedEvent(long timestamp, int rcsGroupThreadId, + int originatingParticipantId, int joinedParticipantId) { + super(timestamp, rcsGroupThreadId, originatingParticipantId); + mJoinedParticipantId = joinedParticipantId; + } + + /** + * @return Returns the {@link RcsParticipant} that joined the associated {@link RcsGroupThread} + */ + public RcsParticipant getJoinedParticipant() { + return new RcsParticipant(mJoinedParticipantId); + } + + /** + * Persists the event to the data store. + * + * @hide - not meant for public use. + */ + @Override + public void persist() throws RcsMessageStoreException { + RcsControllerCall.call( + iRcs -> iRcs.createGroupThreadParticipantJoinedEvent(getTimestamp(), + getRcsGroupThread().getThreadId(), getOriginatingParticipant().getId(), + getJoinedParticipant().getId())); + } + + public static final Creator<RcsGroupThreadParticipantJoinedEvent> CREATOR = + new Creator<RcsGroupThreadParticipantJoinedEvent>() { + @Override + public RcsGroupThreadParticipantJoinedEvent createFromParcel(Parcel in) { + return new RcsGroupThreadParticipantJoinedEvent(in); + } + + @Override + public RcsGroupThreadParticipantJoinedEvent[] newArray(int size) { + return new RcsGroupThreadParticipantJoinedEvent[size]; + } + }; + + private RcsGroupThreadParticipantJoinedEvent(Parcel in) { + super(in); + mJoinedParticipantId = in.readInt(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(mJoinedParticipantId); + } +} diff --git a/telephony/java/android/telephony/ims/RcsMessage.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.aidl index b32cd1208c40..ff139ac0ab1e 100644 --- a/telephony/java/android/telephony/ims/RcsMessage.aidl +++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.aidl @@ -17,4 +17,4 @@ package android.telephony.ims; -parcelable RcsMessage; +parcelable RcsGroupThreadParticipantLeftEvent; diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java new file mode 100644 index 000000000000..cd525086749a --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java @@ -0,0 +1,106 @@ +/* + * 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.telephony.ims; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * An event that indicates an RCS participant has left an {@link RcsThread}. Please see US6-23 - + * GSMA RCC.71 (RCS Universal Profile Service Definition Document) + */ +public final class RcsGroupThreadParticipantLeftEvent extends RcsGroupThreadEvent implements + Parcelable { + private final int mLeavingParticipantId; + + /** + * Creates a new {@link RcsGroupThreadParticipantLeftEvent}. his event is not persisted into + * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called. + * + * @param timestamp The timestamp of when this event happened, in milliseconds passed after + * midnight, January 1st, 1970 UTC + * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on + * @param originatingParticipant The {@link RcsParticipant} that removed the + * {@link RcsParticipant} from the {@link RcsGroupThread}. It is + * possible that originatingParticipant and leavingParticipant are + * the same (i.e. {@link RcsParticipant} left the group + * themselves) + * @param leavingParticipant The {@link RcsParticipant} that left the {@link RcsGroupThread} + * @see RcsMessageStore#persistRcsEvent(RcsEvent) + */ + public RcsGroupThreadParticipantLeftEvent(long timestamp, + @NonNull RcsGroupThread rcsGroupThread, @NonNull RcsParticipant originatingParticipant, + @NonNull RcsParticipant leavingParticipant) { + super(timestamp, rcsGroupThread.getThreadId(), originatingParticipant.getId()); + mLeavingParticipantId = leavingParticipant.getId(); + } + + /** + * @hide - internal constructor for queries + */ + public RcsGroupThreadParticipantLeftEvent(long timestamp, int rcsGroupThreadId, + int originatingParticipantId, int leavingParticipantId) { + super(timestamp, rcsGroupThreadId, originatingParticipantId); + mLeavingParticipantId = leavingParticipantId; + } + + /** + * @return Returns the {@link RcsParticipant} that left the associated {@link RcsGroupThread} + * after this {@link RcsGroupThreadParticipantLeftEvent} happened. + */ + @NonNull + public RcsParticipant getLeavingParticipantId() { + return new RcsParticipant(mLeavingParticipantId); + } + + @Override + public void persist() throws RcsMessageStoreException { + RcsControllerCall.call( + iRcs -> iRcs.createGroupThreadParticipantJoinedEvent(getTimestamp(), + getRcsGroupThread().getThreadId(), getOriginatingParticipant().getId(), + getLeavingParticipantId().getId())); + } + + public static final Creator<RcsGroupThreadParticipantLeftEvent> CREATOR = + new Creator<RcsGroupThreadParticipantLeftEvent>() { + @Override + public RcsGroupThreadParticipantLeftEvent createFromParcel(Parcel in) { + return new RcsGroupThreadParticipantLeftEvent(in); + } + + @Override + public RcsGroupThreadParticipantLeftEvent[] newArray(int size) { + return new RcsGroupThreadParticipantLeftEvent[size]; + } + }; + + private RcsGroupThreadParticipantLeftEvent(Parcel in) { + super(in); + mLeavingParticipantId = in.readInt(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(mLeavingParticipantId); + } +} diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessage.java b/telephony/java/android/telephony/ims/RcsIncomingMessage.java index f39e06db068a..61911abd00c5 100644 --- a/telephony/java/android/telephony/ims/RcsIncomingMessage.java +++ b/telephony/java/android/telephony/ims/RcsIncomingMessage.java @@ -15,34 +15,80 @@ */ package android.telephony.ims; -import android.os.Parcel; +import android.annotation.WorkerThread; /** * This is a single instance of a message received over RCS. - * @hide - TODO(sahinc) make this public */ public class RcsIncomingMessage extends RcsMessage { - public static final Creator<RcsIncomingMessage> CREATOR = new Creator<RcsIncomingMessage>() { - @Override - public RcsIncomingMessage createFromParcel(Parcel in) { - return new RcsIncomingMessage(in); - } - - @Override - public RcsIncomingMessage[] newArray(int size) { - return new RcsIncomingMessage[size]; - } - }; - - protected RcsIncomingMessage(Parcel in) { + /** + * @hide + */ + RcsIncomingMessage(int id) { + super(id); } - @Override - public int describeContents() { - return 0; + /** + * Sets the timestamp of arrival for this message and persists into storage. The timestamp is + * defined as milliseconds passed after midnight, January 1, 1970 UTC + * + * @param arrivalTimestamp The timestamp to set to. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setArrivalTimestamp(long arrivalTimestamp) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setMessageArrivalTimestamp(mId, true, arrivalTimestamp)); + } + + /** + * @return Returns the timestamp of arrival for this message. The timestamp is defined as + * milliseconds passed after midnight, January 1, 1970 UTC + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public long getArrivalTimestamp() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getMessageArrivalTimestamp(mId, true)); + } + + /** + * Sets the timestamp of when the user saw this message and persists into storage. The timestamp + * is defined as milliseconds passed after midnight, January 1, 1970 UTC + * + * @param notifiedTimestamp The timestamp to set to. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setSeenTimestamp(long notifiedTimestamp) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setMessageSeenTimestamp(mId, true, notifiedTimestamp)); + } + + /** + * @return Returns the timestamp of when the user saw this message. The timestamp is defined as + * milliseconds passed after midnight, January 1, 1970 UTC + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public long getSeenTimestamp() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getMessageSeenTimestamp(mId, true)); + } + + /** + * @return Returns the sender of this incoming message. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public RcsParticipant getSenderParticipant() throws RcsMessageStoreException { + return new RcsParticipant( + RcsControllerCall.call(iRcs -> iRcs.getSenderParticipant(mId))); } + /** + * @return Returns {@code true} as this is an incoming message + */ @Override - public void writeToParcel(Parcel dest, int flags) { + public boolean isIncoming() { + return true; } } diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.aidl b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.aidl new file mode 100644 index 000000000000..1f1d4f68213a --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.aidl @@ -0,0 +1,20 @@ +/* + * + * 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.telephony.ims; + +parcelable RcsIncomingMessageCreationParams; diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java new file mode 100644 index 000000000000..61dedbc1578a --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java @@ -0,0 +1,178 @@ +/* + * 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.telephony.ims; + +import android.annotation.CheckResult; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * {@link RcsIncomingMessageCreationParams} is a collection of parameters that should be passed + * into {@link RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)} to generate an + * {@link RcsIncomingMessage} on that {@link RcsThread} + */ +public final class RcsIncomingMessageCreationParams extends RcsMessageCreationParams implements + Parcelable { + // The arrival timestamp for the RcsIncomingMessage to be created + private final long mArrivalTimestamp; + // The seen timestamp for the RcsIncomingMessage to be created + private final long mSeenTimestamp; + // The participant that sent this incoming message + private final int mSenderParticipantId; + + /** + * Builder to help create an {@link RcsIncomingMessageCreationParams} + * + * @see RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams) + */ + public static class Builder extends RcsMessageCreationParams.Builder { + private RcsParticipant mSenderParticipant; + private long mArrivalTimestamp; + private long mSeenTimestamp; + + /** + * Creates a {@link Builder} to create an instance of + * {@link RcsIncomingMessageCreationParams} + * + * @param originationTimestamp The timestamp of {@link RcsMessage} creation. The origination + * timestamp value in milliseconds passed after midnight, + * January 1, 1970 UTC + * @param arrivalTimestamp The timestamp of arrival, defined as milliseconds passed after + * midnight, January 1, 1970 UTC + * @param subscriptionId The subscription ID that was used to send or receive this + * {@link RcsMessage} + */ + public Builder(long originationTimestamp, long arrivalTimestamp, int subscriptionId) { + super(originationTimestamp, subscriptionId); + mArrivalTimestamp = arrivalTimestamp; + } + + /** + * Sets the {@link RcsParticipant} that send this {@link RcsIncomingMessage} + * + * @param senderParticipant The {@link RcsParticipant} that sent this + * {@link RcsIncomingMessage} + * @return The same instance of {@link Builder} to chain methods. + */ + @CheckResult + public Builder setSenderParticipant(RcsParticipant senderParticipant) { + mSenderParticipant = senderParticipant; + return this; + } + + /** + * Sets the time of the arrival of this {@link RcsIncomingMessage} + + * @return The same instance of {@link Builder} to chain methods. + * @see RcsIncomingMessage#setArrivalTimestamp(long) + */ + @CheckResult + public Builder setArrivalTimestamp(long arrivalTimestamp) { + mArrivalTimestamp = arrivalTimestamp; + return this; + } + + /** + * Sets the time of the when this user saw the {@link RcsIncomingMessage} + * @param seenTimestamp The seen timestamp , defined as milliseconds passed after midnight, + * January 1, 1970 UTC + * @return The same instance of {@link Builder} to chain methods. + * @see RcsIncomingMessage#setSeenTimestamp(long) + */ + @CheckResult + public Builder setSeenTimestamp(long seenTimestamp) { + mSeenTimestamp = seenTimestamp; + return this; + } + + /** + * Creates parameters for creating a new incoming message. + * @return A new instance of {@link RcsIncomingMessageCreationParams} to create a new + * {@link RcsIncomingMessage} + */ + public RcsIncomingMessageCreationParams build() { + return new RcsIncomingMessageCreationParams(this); + } + } + + private RcsIncomingMessageCreationParams(Builder builder) { + super(builder); + mArrivalTimestamp = builder.mArrivalTimestamp; + mSeenTimestamp = builder.mSeenTimestamp; + mSenderParticipantId = builder.mSenderParticipant.getId(); + } + + private RcsIncomingMessageCreationParams(Parcel in) { + super(in); + mArrivalTimestamp = in.readLong(); + mSeenTimestamp = in.readLong(); + mSenderParticipantId = in.readInt(); + } + + /** + * @return Returns the arrival timestamp for the {@link RcsIncomingMessage} to be created. + * Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC + */ + public long getArrivalTimestamp() { + return mArrivalTimestamp; + } + + /** + * @return Returns the seen timestamp for the {@link RcsIncomingMessage} to be created. + * Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC + */ + public long getSeenTimestamp() { + return mSeenTimestamp; + } + + /** + * Helper getter for {@link com.android.internal.telephony.ims.RcsMessageStoreController} to + * create {@link RcsIncomingMessage}s + * + * Since the API doesn't expose any ID's to API users, this should be hidden. + * @hide + */ + public int getSenderParticipantId() { + return mSenderParticipantId; + } + + public static final Creator<RcsIncomingMessageCreationParams> CREATOR = + new Creator<RcsIncomingMessageCreationParams>() { + @Override + public RcsIncomingMessageCreationParams createFromParcel(Parcel in) { + return new RcsIncomingMessageCreationParams(in); + } + + @Override + public RcsIncomingMessageCreationParams[] newArray(int size) { + return new RcsIncomingMessageCreationParams[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest); + dest.writeLong(mArrivalTimestamp); + dest.writeLong(mSeenTimestamp); + dest.writeInt(mSenderParticipantId); + } +} diff --git a/telephony/java/android/telephony/ims/RcsLocationPart.java b/telephony/java/android/telephony/ims/RcsLocationPart.java deleted file mode 100644 index 19be4ceaf688..000000000000 --- a/telephony/java/android/telephony/ims/RcsLocationPart.java +++ /dev/null @@ -1,48 +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.telephony.ims; - -import android.os.Parcel; - -/** - * A part of a composite {@link RcsMessage} that holds a location - * @hide - TODO(sahinc) make this public - */ -public class RcsLocationPart extends RcsPart { - public static final Creator<RcsLocationPart> CREATOR = new Creator<RcsLocationPart>() { - @Override - public RcsLocationPart createFromParcel(Parcel in) { - return new RcsLocationPart(in); - } - - @Override - public RcsLocationPart[] newArray(int size) { - return new RcsLocationPart[size]; - } - }; - - protected RcsLocationPart(Parcel in) { - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - } -} diff --git a/telephony/java/android/telephony/ims/RcsManager.java b/telephony/java/android/telephony/ims/RcsManager.java index df108c88e3b0..22e4b2249c36 100644 --- a/telephony/java/android/telephony/ims/RcsManager.java +++ b/telephony/java/android/telephony/ims/RcsManager.java @@ -20,15 +20,21 @@ import android.content.Context; /** * The manager class for RCS related utilities. - * @hide */ @SystemService(Context.TELEPHONY_RCS_SERVICE) public class RcsManager { + /** + * @hide + */ + public RcsManager() { + // empty constructor + } + private static final RcsMessageStore sRcsMessageStoreInstance = new RcsMessageStore(); /** - * Returns an instance of RcsMessageStore. + * Returns an instance of {@link RcsMessageStore} */ public RcsMessageStore getRcsMessageStore() { return sRcsMessageStoreInstance; diff --git a/telephony/java/android/telephony/ims/RcsMessage.java b/telephony/java/android/telephony/ims/RcsMessage.java index d46685c4a572..32274131a5ad 100644 --- a/telephony/java/android/telephony/ims/RcsMessage.java +++ b/telephony/java/android/telephony/ims/RcsMessage.java @@ -15,11 +15,315 @@ */ package android.telephony.ims; -import android.os.Parcelable; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.WorkerThread; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; /** * This is a single instance of a message sent or received over RCS. - * @hide - TODO(sahinc) make this public */ -public abstract class RcsMessage implements Parcelable { +public abstract class RcsMessage { + /** + * The value to indicate that this {@link RcsMessage} does not have any location information. + */ + public static final double LOCATION_NOT_SET = Double.MIN_VALUE; + + /** + * The status to indicate that this {@link RcsMessage}s status is not set yet. + */ + public static final int NOT_SET = 0; + + /** + * The status to indicate that this {@link RcsMessage} is a draft and is not in the process of + * sending yet. + */ + public static final int DRAFT = 1; + + /** + * The status to indicate that this {@link RcsMessage} was successfully sent. + */ + public static final int QUEUED = 2; + + /** + * The status to indicate that this {@link RcsMessage} is actively being sent. + */ + public static final int SENDING = 3; + + /** + * The status to indicate that this {@link RcsMessage} was successfully sent. + */ + public static final int SENT = 4; + + /** + * The status to indicate that this {@link RcsMessage} failed to send in an attempt before, and + * now being retried. + */ + public static final int RETRYING = 5; + + /** + * The status to indicate that this {@link RcsMessage} has permanently failed to send. + */ + public static final int FAILED = 6; + + /** + * The status to indicate that this {@link RcsMessage} was successfully received. + */ + public static final int RECEIVED = 7; + + /** + * The status to indicate that this {@link RcsMessage} was seen. + */ + public static final int SEEN = 9; + + /** + * @hide + */ + protected final int mId; + + @IntDef({ + DRAFT, QUEUED, SENDING, SENT, RETRYING, FAILED, RECEIVED, SEEN + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RcsMessageStatus { + } + + RcsMessage(int id) { + mId = id; + } + + /** + * Returns the row Id from the common message. + * + * @hide + */ + public int getId() { + return mId; + } + + /** + * @return Returns the subscription ID that this {@link RcsMessage} was sent from, or delivered + * to. + * @throws RcsMessageStoreException if the value could not be read from the storage + * @see android.telephony.SubscriptionInfo#getSubscriptionId + */ + public int getSubscriptionId() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getMessageSubId(mId, isIncoming())); + } + + /** + * Sets the subscription ID that this {@link RcsMessage} was sent from, or delivered to and + * persists it into storage. + * + * @param subId The subscription ID to persists into storage. + * @throws RcsMessageStoreException if the value could not be persisted into storage + * @see android.telephony.SubscriptionInfo#getSubscriptionId + */ + @WorkerThread + public void setSubscriptionId(int subId) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setMessageSubId(mId, isIncoming(), subId)); + } + + /** + * Sets the status of this message and persists it into storage. Please see + * {@link RcsFileTransferPart#setFileTransferStatus(int)} to set statuses around file transfers. + * + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setStatus(@RcsMessageStatus int rcsMessageStatus) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setMessageStatus(mId, isIncoming(), rcsMessageStatus)); + } + + /** + * @return Returns the status of this message. Please see + * {@link RcsFileTransferPart#setFileTransferStatus(int)} to set statuses around file transfers. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public @RcsMessageStatus int getStatus() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getMessageStatus(mId, isIncoming())); + } + + /** + * Sets the origination timestamp of this message and persists it into storage. Origination is + * defined as when the sender tapped the send button. + * + * @param timestamp The origination timestamp value in milliseconds passed after midnight, + * January 1, 1970 UTC + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setOriginationTimestamp(long timestamp) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setMessageOriginationTimestamp(mId, isIncoming(), timestamp)); + } + + /** + * @return Returns the origination timestamp of this message in milliseconds passed after + * midnight, January 1, 1970 UTC. Origination is defined as when the sender tapped the send + * button. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public long getOriginationTimestamp() throws RcsMessageStoreException { + return RcsControllerCall.call( + iRcs -> iRcs.getMessageOriginationTimestamp(mId, isIncoming())); + } + + /** + * Sets the globally unique RCS message identifier for this message and persists it into + * storage. This function does not confirm that this message id is unique. Please see 4.4.5.2 + * - GSMA RCC.53 (RCS Device API 1.6 Specification + * + * @param rcsMessageGlobalId The globally RCS message identifier + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setRcsMessageId(String rcsMessageGlobalId) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setGlobalMessageIdForMessage(mId, isIncoming(), rcsMessageGlobalId)); + } + + /** + * @return Returns the globally unique RCS message identifier for this message. Please see + * 4.4.5.2 - GSMA RCC.53 (RCS Device API 1.6 Specification + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public String getRcsMessageId() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getGlobalMessageIdForMessage(mId, isIncoming())); + } + + /** + * @return Returns the user visible text included in this message. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public String getText() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getTextForMessage(mId, isIncoming())); + } + + /** + * Sets the user visible text for this message and persists in storage. + * + * @param text The text this message now has + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setText(String text) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setTextForMessage(mId, isIncoming(), text)); + } + + /** + * @return Returns the associated latitude for this message, or + * {@link RcsMessage#LOCATION_NOT_SET} if it does not contain a location. + * + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public double getLatitude() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getLatitudeForMessage(mId, isIncoming())); + } + + /** + * Sets the latitude for this message and persists in storage. + * + * @param latitude The latitude for this location message. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setLatitude(double latitude) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setLatitudeForMessage(mId, isIncoming(), latitude)); + } + + /** + * @return Returns the associated longitude for this message, or + * {@link RcsMessage#LOCATION_NOT_SET} if it does not contain a location. + * + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public double getLongitude() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getLongitudeForMessage(mId, isIncoming())); + } + + /** + * Sets the longitude for this message and persists in storage. + * + * @param longitude The longitude for this location message. + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setLongitude(double longitude) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.setLongitudeForMessage(mId, isIncoming(), longitude)); + } + + /** + * Attaches an {@link RcsFileTransferPart} to this message and persists into storage. + * + * @param fileTransferCreationParameters The parameters to be used to create the + * {@link RcsFileTransferPart} + * @return A new instance of {@link RcsFileTransferPart} + * @throws RcsMessageStoreException if the file transfer could not be persisted into storage. + */ + @NonNull + @WorkerThread + public RcsFileTransferPart insertFileTransfer( + RcsFileTransferCreationParams fileTransferCreationParameters) + throws RcsMessageStoreException { + return new RcsFileTransferPart(RcsControllerCall.call( + iRcs -> iRcs.storeFileTransfer(mId, isIncoming(), fileTransferCreationParameters))); + } + + /** + * @return Returns all the {@link RcsFileTransferPart}s associated with this message in an + * unmodifiable set. + * @throws RcsMessageStoreException if the file transfers could not be read from the storage + */ + @NonNull + @WorkerThread + public Set<RcsFileTransferPart> getFileTransferParts() throws RcsMessageStoreException { + Set<RcsFileTransferPart> fileTransferParts = new HashSet<>(); + + int[] fileTransferIds = RcsControllerCall.call( + iRcs -> iRcs.getFileTransfersAttachedToMessage(mId, isIncoming())); + + for (int fileTransfer : fileTransferIds) { + fileTransferParts.add(new RcsFileTransferPart(fileTransfer)); + } + + return Collections.unmodifiableSet(fileTransferParts); + } + + /** + * Removes a {@link RcsFileTransferPart} from this message, and deletes it in storage. + * + * @param fileTransferPart The part to delete. + * @throws RcsMessageStoreException if the file transfer could not be removed from storage + */ + @WorkerThread + public void removeFileTransferPart(@NonNull RcsFileTransferPart fileTransferPart) + throws RcsMessageStoreException { + if (fileTransferPart == null) { + return; + } + + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.deleteFileTransfer(fileTransferPart.getId())); + } + + /** + * @return Returns {@code true} if this message was received on this device, {@code false} if it + * was sent. + */ + public abstract boolean isIncoming(); } diff --git a/telephony/java/android/telephony/ims/RcsMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsMessageCreationParams.java new file mode 100644 index 000000000000..c46c605d861d --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsMessageCreationParams.java @@ -0,0 +1,240 @@ +/* + * 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.telephony.ims; + +import static android.telephony.ims.RcsMessage.LOCATION_NOT_SET; + +import android.annotation.CheckResult; +import android.annotation.Nullable; +import android.os.Parcel; + +/** + * The collection of parameters to be passed into + * {@link RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)} and + * {@link RcsThread#addOutgoingMessage(RcsOutgoingMessageCreationParams)} to create and persist + * {@link RcsMessage}s on an {@link RcsThread} + */ +public class RcsMessageCreationParams { + // The globally unique id of the RcsMessage to be created. + private final String mRcsMessageGlobalId; + + // The subscription that this message was/will be received/sent from. + private final int mSubId; + // The sending/receiving status of the message + private final @RcsMessage.RcsMessageStatus int mMessageStatus; + // The timestamp of message creation + private final long mOriginationTimestamp; + // The user visible content of the message + private final String mText; + // The latitude of the message if this is a location message + private final double mLatitude; + // The longitude of the message if this is a location message + private final double mLongitude; + + /** + * @return Returns the globally unique RCS Message ID for the {@link RcsMessage} to be created. + * Please see 4.4.5.2 - GSMA RCC.53 (RCS Device API 1.6 Specification + */ + @Nullable + public String getRcsMessageGlobalId() { + return mRcsMessageGlobalId; + } + + /** + * @return Returns the subscription ID that was used to send or receive the {@link RcsMessage} + * to be created. + */ + public int getSubId() { + return mSubId; + } + + /** + * @return Returns the status for the {@link RcsMessage} to be created. + * @see RcsMessage.RcsMessageStatus + */ + public int getMessageStatus() { + return mMessageStatus; + } + + /** + * @return Returns the origination timestamp of the {@link RcsMessage} to be created in + * milliseconds passed after midnight, January 1, 1970 UTC. Origination is defined as when + * the sender tapped the send button. + */ + public long getOriginationTimestamp() { + return mOriginationTimestamp; + } + + /** + * @return Returns the user visible text contained in the {@link RcsMessage} to be created + */ + @Nullable + public String getText() { + return mText; + } + + /** + * @return Returns the latitude of the {@link RcsMessage} to be created, or + * {@link RcsMessage#LOCATION_NOT_SET} if the message does not contain a location. + */ + public double getLatitude() { + return mLatitude; + } + + /** + * @return Returns the longitude of the {@link RcsMessage} to be created, or + * {@link RcsMessage#LOCATION_NOT_SET} if the message does not contain a location. + */ + public double getLongitude() { + return mLongitude; + } + + /** + * The base builder for creating {@link RcsMessage}s on {@link RcsThread}s. + * + * @see RcsIncomingMessageCreationParams + */ + public static class Builder { + private String mRcsMessageGlobalId; + private int mSubId; + private @RcsMessage.RcsMessageStatus int mMessageStatus; + private long mOriginationTimestamp; + private String mText; + private double mLatitude = LOCATION_NOT_SET; + private double mLongitude = LOCATION_NOT_SET; + + /** + * @hide + */ + public Builder(long originationTimestamp, int subscriptionId) { + mOriginationTimestamp = originationTimestamp; + mSubId = subscriptionId; + } + + /** + * Sets the status of the {@link RcsMessage} to be built. + * + * @param rcsMessageStatus The status to be set + * @return The same instance of {@link Builder} to chain methods + * @see RcsMessage#setStatus(int) + */ + @CheckResult + public Builder setStatus(@RcsMessage.RcsMessageStatus int rcsMessageStatus) { + mMessageStatus = rcsMessageStatus; + return this; + } + + /** + * Sets the globally unique RCS message identifier for the {@link RcsMessage} to be built. + * This function does not confirm that this message id is unique. Please see 4.4.5.2 - GSMA + * RCC.53 (RCS Device API 1.6 Specification) + * + * @param rcsMessageId The ID to be set + * @return The same instance of {@link Builder} to chain methods + * @see RcsMessage#setRcsMessageId(String) + */ + @CheckResult + public Builder setRcsMessageId(String rcsMessageId) { + mRcsMessageGlobalId = rcsMessageId; + return this; + } + + /** + * Sets the text of the {@link RcsMessage} to be built. + * + * @param text The user visible text of the message + * @return The same instance of {@link Builder} to chain methods + * @see RcsMessage#setText(String) + */ + @CheckResult + public Builder setText(String text) { + mText = text; + return this; + } + + /** + * Sets the latitude of the {@link RcsMessage} to be built. Please see US5-24 - GSMA RCC.71 + * (RCS Universal Profile Service Definition Document) + * + * @param latitude The latitude of the location information associated with this message. + * @return The same instance of {@link Builder} to chain methods + * @see RcsMessage#setLatitude(double) + */ + @CheckResult + public Builder setLatitude(double latitude) { + mLatitude = latitude; + return this; + } + + /** + * Sets the longitude of the {@link RcsMessage} to be built. Please see US5-24 - GSMA RCC.71 + * (RCS Universal Profile Service Definition Document) + * + * @param longitude The longitude of the location information associated with this message. + * @return The same instance of {@link Builder} to chain methods + * @see RcsMessage#setLongitude(double) + */ + @CheckResult + public Builder setLongitude(double longitude) { + mLongitude = longitude; + return this; + } + + /** + * @return Builds and returns a newly created {@link RcsMessageCreationParams} + */ + public RcsMessageCreationParams build() { + return new RcsMessageCreationParams(this); + } + } + + protected RcsMessageCreationParams(Builder builder) { + mRcsMessageGlobalId = builder.mRcsMessageGlobalId; + mSubId = builder.mSubId; + mMessageStatus = builder.mMessageStatus; + mOriginationTimestamp = builder.mOriginationTimestamp; + mText = builder.mText; + mLatitude = builder.mLatitude; + mLongitude = builder.mLongitude; + } + + /** + * @hide + */ + RcsMessageCreationParams(Parcel in) { + mRcsMessageGlobalId = in.readString(); + mSubId = in.readInt(); + mMessageStatus = in.readInt(); + mOriginationTimestamp = in.readLong(); + mText = in.readString(); + mLatitude = in.readDouble(); + mLongitude = in.readDouble(); + } + + /** + * @hide + */ + public void writeToParcel(Parcel dest) { + dest.writeString(mRcsMessageGlobalId); + dest.writeInt(mSubId); + dest.writeInt(mMessageStatus); + dest.writeLong(mOriginationTimestamp); + dest.writeString(mText); + dest.writeDouble(mLatitude); + dest.writeDouble(mLongitude); + } +} diff --git a/telephony/java/android/telephony/ims/RcsGroupThread.aidl b/telephony/java/android/telephony/ims/RcsMessageQueryParams.aidl index c4ce5299e512..e9cbd9cc4ebe 100644 --- a/telephony/java/android/telephony/ims/RcsGroupThread.aidl +++ b/telephony/java/android/telephony/ims/RcsMessageQueryParams.aidl @@ -17,4 +17,4 @@ package android.telephony.ims; -parcelable RcsGroupThread; +parcelable RcsMessageQueryParams; diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryParams.java b/telephony/java/android/telephony/ims/RcsMessageQueryParams.java new file mode 100644 index 000000000000..535a597f5e1e --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsMessageQueryParams.java @@ -0,0 +1,359 @@ +/* + * 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.telephony.ims; + +import android.annotation.CheckResult; +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.security.InvalidParameterException; + +/** + * The parameters to pass into + * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} in order to select a + * subset of {@link RcsMessage}s present in the message store. + */ +public final class RcsMessageQueryParams implements Parcelable { + /** + * @hide - not meant for public use + */ + public static final int THREAD_ID_NOT_SET = -1; + + /** + * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should + * be sorted in the same order of {@link RcsMessage}s that got persisted into storage for faster + * results. + */ + public static final int SORT_BY_CREATION_ORDER = 0; + + /** + * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should + * be sorted according to the timestamp of {@link RcsMessage#getOriginationTimestamp()} + */ + public static final int SORT_BY_TIMESTAMP = 1; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_TIMESTAMP}) + public @interface SortingProperty { + } + + /** + * Bitmask flag to be used with {@link Builder#setMessageType(int)} to make + * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return + * {@link RcsIncomingMessage}s. + */ + public static final int MESSAGE_TYPE_INCOMING = 0x0001; + + /** + * Bitmask flag to be used with {@link Builder#setMessageType(int)} to make + * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return + * {@link RcsOutgoingMessage}s. + */ + public static final int MESSAGE_TYPE_OUTGOING = 0x0002; + + /** + * Bitmask flag to be used with {@link Builder#setFileTransferPresence(int)} to make + * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return {@link RcsMessage}s + * that have an {@link RcsFileTransferPart} attached. + */ + public static final int MESSAGES_WITH_FILE_TRANSFERS = 0x0004; + + /** + * Bitmask flag to be used with {@link Builder#setFileTransferPresence(int)} to make + * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return {@link RcsMessage}s + * that don't have an {@link RcsFileTransferPart} attached. + */ + public static final int MESSAGES_WITHOUT_FILE_TRANSFERS = 0x0008; + + /** + * @hide - not meant for public use + */ + public static final String MESSAGE_QUERY_PARAMETERS_KEY = "message_query_parameters"; + + // Whether the result should be filtered against incoming or outgoing messages + private int mMessageType; + // Whether the result should have file transfer messages attached or not + private int mFileTransferPresence; + // The SQL "Like" clause to filter messages + private String mMessageLike; + // The property the messages should be sorted against + private @SortingProperty int mSortingProperty; + // Whether the messages should be sorted in ascending order + private boolean mIsAscending; + // The number of results that should be returned with this query + private int mLimit; + // The thread that the results should be limited to + private int mThreadId; + + RcsMessageQueryParams(int messageType, int fileTransferPresence, String messageLike, + int threadId, @SortingProperty int sortingProperty, boolean isAscending, int limit) { + mMessageType = messageType; + mFileTransferPresence = fileTransferPresence; + mMessageLike = messageLike; + mSortingProperty = sortingProperty; + mIsAscending = isAscending; + mLimit = limit; + mThreadId = threadId; + } + + /** + * @return Returns the type of {@link RcsMessage}s that this {@link RcsMessageQueryParams} + * is set to query for. + */ + public int getMessageType() { + return mMessageType; + } + + /** + * @return Returns whether the result query should return {@link RcsMessage}s with + * {@link RcsFileTransferPart}s or not + */ + public int getFileTransferPresence() { + return mFileTransferPresence; + } + + /** + * @return Returns the SQL-inspired "LIKE" clause that will be used to match {@link RcsMessage}s + */ + public String getMessageLike() { + return mMessageLike; + } + + /** + * @return Returns the number of {@link RcsThread}s to be returned from the query. A value of + * 0 means there is no set limit. + */ + public int getLimit() { + return mLimit; + } + + /** + * @return Returns the property that will be used to sort the result against. + * @see SortingProperty + */ + public @SortingProperty int getSortingProperty() { + return mSortingProperty; + } + + /** + * @return Returns {@code true} if the result set will be sorted in ascending order, + * {@code false} if it will be sorted in descending order. + */ + public boolean getSortDirection() { + return mIsAscending; + } + + /** + * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get + * the thread that the result query should be limited to. + * + * As we do not expose any sort of integer ID's to public usage, this should be hidden. + * + * @hide - not meant for public use + */ + public int getThreadId() { + return mThreadId; + } + + /** + * A helper class to build the {@link RcsMessageQueryParams}. + */ + public static class Builder { + private @SortingProperty int mSortingProperty; + private int mMessageType; + private int mFileTransferPresence; + private String mMessageLike; + private boolean mIsAscending; + private int mLimit = 100; + private int mThreadId = THREAD_ID_NOT_SET; + + /** + * Creates a new builder for {@link RcsMessageQueryParams} to be used in + * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} + * + */ + public Builder() { + // empty implementation + } + + /** + * Desired number of threads to be returned from the query. Passing in 0 will return all + * existing threads at once. The limit defaults to 100. + * + * @param limit The number to limit the query result to. + * @return The same instance of the builder to chain parameters. + * @throws InvalidParameterException If the given limit is negative. + */ + @CheckResult + public Builder setResultLimit(@IntRange(from = 0) int limit) + throws InvalidParameterException { + if (limit < 0) { + throw new InvalidParameterException("The query limit must be non-negative"); + } + + mLimit = limit; + return this; + } + + /** + * Sets the type of messages to be returned from the query. + * + * @param messageType The type of message to be returned. + * @return The same instance of the builder to chain parameters. + * @see RcsMessageQueryParams#MESSAGE_TYPE_INCOMING + * @see RcsMessageQueryParams#MESSAGE_TYPE_OUTGOING + */ + @CheckResult + public Builder setMessageType(int messageType) { + mMessageType = messageType; + return this; + } + + /** + * Sets whether file transfer messages should be included in the query result or not. + * + * @param fileTransferPresence Whether file transfers should be included in the result + * @return The same instance of the builder to chain parameters. + * @see RcsMessageQueryParams#MESSAGES_WITH_FILE_TRANSFERS + * @see RcsMessageQueryParams#MESSAGES_WITHOUT_FILE_TRANSFERS + */ + @CheckResult + public Builder setFileTransferPresence(int fileTransferPresence) { + mFileTransferPresence = fileTransferPresence; + return this; + } + + /** + * Sets an SQL-inspired "like" clause to match with messages. Using a percent sign ('%') + * wildcard matches any sequence of zero or more characters. Using an underscore ('_') + * wildcard matches any single character. Not using any wildcards would only perform a + * string match. The input string is case-insensitive. + * + * The input "Wh%" would match messages "who", "where" and "what", while the input "Wh_" + * would only match "who" + * + * @param messageLike The "like" clause for matching {@link RcsMessage}s. + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setMessageLike(String messageLike) { + mMessageLike = messageLike; + return this; + } + + /** + * Sets the property where the results should be sorted against. Defaults to + * {@link RcsMessageQueryParams.SortingProperty#SORT_BY_CREATION_ORDER} + * + * @param sortingProperty against which property the results should be sorted + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setSortProperty(@SortingProperty int sortingProperty) { + mSortingProperty = sortingProperty; + return this; + } + + /** + * Sets whether the results should be sorted ascending or descending + * + * @param isAscending whether the results should be sorted ascending + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setSortDirection(boolean isAscending) { + mIsAscending = isAscending; + return this; + } + + /** + * Limits the results to the given thread. + * + * @param thread the {@link RcsThread} that results should be limited to. If set to + * {@code null}, messages on all threads will be queried + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setThread(@Nullable RcsThread thread) { + if (thread == null) { + mThreadId = THREAD_ID_NOT_SET; + } else { + mThreadId = thread.getThreadId(); + } + return this; + } + + /** + * Builds the {@link RcsMessageQueryParams} to use in + * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} + * + * @return An instance of {@link RcsMessageQueryParams} to use with the message + * query. + */ + public RcsMessageQueryParams build() { + return new RcsMessageQueryParams(mMessageType, mFileTransferPresence, mMessageLike, + mThreadId, mSortingProperty, mIsAscending, mLimit); + } + } + + /** + * Parcelable boilerplate below. + */ + private RcsMessageQueryParams(Parcel in) { + mMessageType = in.readInt(); + mFileTransferPresence = in.readInt(); + mMessageLike = in.readString(); + mSortingProperty = in.readInt(); + mIsAscending = in.readBoolean(); + mLimit = in.readInt(); + mThreadId = in.readInt(); + } + + public static final Creator<RcsMessageQueryParams> CREATOR = + new Creator<RcsMessageQueryParams>() { + @Override + public RcsMessageQueryParams createFromParcel(Parcel in) { + return new RcsMessageQueryParams(in); + } + + @Override + public RcsMessageQueryParams[] newArray(int size) { + return new RcsMessageQueryParams[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mMessageType); + dest.writeInt(mFileTransferPresence); + dest.writeString(mMessageLike); + dest.writeInt(mSortingProperty); + dest.writeBoolean(mIsAscending); + dest.writeInt(mLimit); + dest.writeInt(mThreadId); + } +} diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryResult.aidl b/telephony/java/android/telephony/ims/RcsMessageQueryResult.aidl new file mode 100644 index 000000000000..a73ba50b6591 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsMessageQueryResult.aidl @@ -0,0 +1,20 @@ +/* + * + * 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.telephony.ims; + +parcelable RcsMessageQueryResult; diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryResult.java b/telephony/java/android/telephony/ims/RcsMessageQueryResult.java new file mode 100644 index 000000000000..3514b48e80a1 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsMessageQueryResult.java @@ -0,0 +1,113 @@ +/* + * 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.telephony.ims; + +import static android.provider.Telephony.RcsColumns.RcsUnifiedMessageColumns.MESSAGE_TYPE_INCOMING; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.ims.RcsTypeIdPair; + +import java.util.ArrayList; +import java.util.List; + +/** + * The result of a {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} + * call. This class allows getting the token for querying the next batch of messages in order to + * prevent handling large amounts of data at once. + */ +public final class RcsMessageQueryResult implements Parcelable { + // The token to continue the query to get the next batch of results + private RcsQueryContinuationToken mContinuationToken; + // The message type and message ID pairs for all the messages in this query result + private List<RcsTypeIdPair> mMessageTypeIdPairs; + + /** + * Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController} + * to create query results + * + * @hide + */ + public RcsMessageQueryResult( + RcsQueryContinuationToken continuationToken, + List<RcsTypeIdPair> messageTypeIdPairs) { + mContinuationToken = continuationToken; + mMessageTypeIdPairs = messageTypeIdPairs; + } + + /** + * Returns a token to call + * {@link RcsMessageStore#getRcsMessages(RcsQueryContinuationToken)} + * to get the next batch of {@link RcsMessage}s. + */ + @Nullable + public RcsQueryContinuationToken getContinuationToken() { + return mContinuationToken; + } + + /** + * Returns all the {@link RcsMessage}s in the current query result. Call {@link + * RcsMessageStore#getRcsMessages(RcsQueryContinuationToken)} to get the next batch + * of {@link RcsMessage}s. + */ + @NonNull + public List<RcsMessage> getMessages() { + List<RcsMessage> messages = new ArrayList<>(); + for (RcsTypeIdPair typeIdPair : mMessageTypeIdPairs) { + if (typeIdPair.getType() == MESSAGE_TYPE_INCOMING) { + messages.add(new RcsIncomingMessage(typeIdPair.getId())); + } else { + messages.add(new RcsOutgoingMessage(typeIdPair.getId())); + } + } + + return messages; + } + + private RcsMessageQueryResult(Parcel in) { + mContinuationToken = in.readParcelable( + RcsQueryContinuationToken.class.getClassLoader()); + in.readTypedList(mMessageTypeIdPairs, RcsTypeIdPair.CREATOR); + } + + public static final Creator<RcsMessageQueryResult> CREATOR = + new Creator<RcsMessageQueryResult>() { + @Override + public RcsMessageQueryResult createFromParcel(Parcel in) { + return new RcsMessageQueryResult(in); + } + + @Override + public RcsMessageQueryResult[] newArray(int size) { + return new RcsMessageQueryResult[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(mContinuationToken, flags); + dest.writeTypedList(mMessageTypeIdPairs); + } +} diff --git a/telephony/java/android/telephony/ims/RcsManager.aidl b/telephony/java/android/telephony/ims/RcsMessageSnippet.aidl index 63bc71c5ee46..99b8eb704e00 100644 --- a/telephony/java/android/telephony/ims/RcsManager.aidl +++ b/telephony/java/android/telephony/ims/RcsMessageSnippet.aidl @@ -17,4 +17,4 @@ package android.telephony.ims; -parcelable RcsManager; +parcelable RcsMessageSnippet; diff --git a/telephony/java/android/telephony/ims/RcsMessageSnippet.java b/telephony/java/android/telephony/ims/RcsMessageSnippet.java new file mode 100644 index 000000000000..b0b930c56e91 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsMessageSnippet.java @@ -0,0 +1,96 @@ +/* + * 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.telephony.ims; + +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.ims.RcsMessage.RcsMessageStatus; + +/** + * An immutable summary of the latest {@link RcsMessage} on an {@link RcsThread} + */ +public final class RcsMessageSnippet implements Parcelable { + private final String mText; + private final @RcsMessageStatus int mStatus; + private final long mTimestamp; + + /** + * @hide + */ + public RcsMessageSnippet(String text, @RcsMessageStatus int status, long timestamp) { + mText = text; + mStatus = status; + mTimestamp = timestamp; + } + + /** + * @return Returns the text of the {@link RcsMessage} with highest origination timestamp value + * (i.e. latest) in this thread + */ + @Nullable + public String getSnippetText() { + return mText; + } + + /** + * @return Returns the status of the {@link RcsMessage} with highest origination timestamp value + * (i.e. latest) in this thread + */ + public @RcsMessageStatus int getSnippetStatus() { + return mStatus; + } + + /** + * @return Returns the timestamp of the {@link RcsMessage} with highest origination timestamp + * value (i.e. latest) in this thread + */ + public long getSnippetTimestamp() { + return mTimestamp; + } + + private RcsMessageSnippet(Parcel in) { + mText = in.readString(); + mStatus = in.readInt(); + mTimestamp = in.readLong(); + } + + public static final Creator<RcsMessageSnippet> CREATOR = + new Creator<RcsMessageSnippet>() { + @Override + public RcsMessageSnippet createFromParcel(Parcel in) { + return new RcsMessageSnippet(in); + } + + @Override + public RcsMessageSnippet[] newArray(int size) { + return new RcsMessageSnippet[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mText); + dest.writeInt(mStatus); + dest.writeLong(mTimestamp); + } +} diff --git a/telephony/java/android/telephony/ims/RcsMessageStore.java b/telephony/java/android/telephony/ims/RcsMessageStore.java index 1bf6ffd81ca0..d811c6e93a56 100644 --- a/telephony/java/android/telephony/ims/RcsMessageStore.java +++ b/telephony/java/android/telephony/ims/RcsMessageStore.java @@ -16,106 +16,221 @@ package android.telephony.ims; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.WorkerThread; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.telephony.Rlog; -import android.telephony.ims.aidl.IRcs; +import android.net.Uri; + +import java.util.List; /** * RcsMessageStore is the application interface to RcsProvider and provides access methods to * RCS related database tables. - * @hide - TODO make this public */ public class RcsMessageStore { - static final String TAG = "RcsMessageStore"; - /** * Returns the first chunk of existing {@link RcsThread}s in the common storage. + * * @param queryParameters Parameters to specify to return a subset of all RcsThreads. * Passing a value of null will return all threads. + * @throws RcsMessageStoreException if the query could not be completed on the storage */ @WorkerThread - public RcsThreadQueryResult getRcsThreads(@Nullable RcsThreadQueryParameters queryParameters) { - try { - IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs")); - if (iRcs != null) { - return iRcs.getRcsThreads(queryParameters); - } - } catch (RemoteException re) { - Rlog.e(TAG, "RcsMessageStore: Exception happened during getRcsThreads", re); - } - - return null; + @NonNull + public RcsThreadQueryResult getRcsThreads(@Nullable RcsThreadQueryParams queryParameters) + throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getRcsThreads(queryParameters)); } /** * Returns the next chunk of {@link RcsThread}s in the common storage. + * * @param continuationToken A token to continue the query to get the next chunk. This is - * obtained through {@link RcsThreadQueryResult#nextChunkToken}. + * obtained through {@link RcsThreadQueryResult#getContinuationToken}. + * @throws RcsMessageStoreException if the query could not be completed on the storage */ @WorkerThread - public RcsThreadQueryResult getRcsThreads(RcsThreadQueryContinuationToken continuationToken) { - try { - IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs")); - if (iRcs != null) { - return iRcs.getRcsThreadsWithToken(continuationToken); - } - } catch (RemoteException re) { - Rlog.e(TAG, "RcsMessageStore: Exception happened during getRcsThreads", re); - } + @NonNull + public RcsThreadQueryResult getRcsThreads(@NonNull RcsQueryContinuationToken continuationToken) + throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getRcsThreadsWithToken(continuationToken)); + } + + /** + * Returns the first chunk of existing {@link RcsParticipant}s in the common storage. + * + * @param queryParameters Parameters to specify to return a subset of all RcsParticipants. + * Passing a value of null will return all participants. + * @throws RcsMessageStoreException if the query could not be completed on the storage + */ + @WorkerThread + @NonNull + public RcsParticipantQueryResult getRcsParticipants( + @Nullable RcsParticipantQueryParams queryParameters) + throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getParticipants(queryParameters)); + } + + /** + * Returns the next chunk of {@link RcsParticipant}s in the common storage. + * + * @param continuationToken A token to continue the query to get the next chunk. This is + * obtained through + * {@link RcsParticipantQueryResult#getContinuationToken} + * @throws RcsMessageStoreException if the query could not be completed on the storage + */ + @WorkerThread + @NonNull + public RcsParticipantQueryResult getRcsParticipants( + @NonNull RcsQueryContinuationToken continuationToken) + throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getParticipantsWithToken(continuationToken)); + } + + /** + * Returns the first chunk of existing {@link RcsMessage}s in the common storage. + * + * @param queryParameters Parameters to specify to return a subset of all RcsMessages. + * Passing a value of null will return all messages. + * @throws RcsMessageStoreException if the query could not be completed on the storage + */ + @WorkerThread + @NonNull + public RcsMessageQueryResult getRcsMessages( + @Nullable RcsMessageQueryParams queryParameters) throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getMessages(queryParameters)); + } + + /** + * Returns the next chunk of {@link RcsMessage}s in the common storage. + * + * @param continuationToken A token to continue the query to get the next chunk. This is + * obtained through {@link RcsMessageQueryResult#getContinuationToken} + * @throws RcsMessageStoreException if the query could not be completed on the storage + */ + @WorkerThread + @NonNull + public RcsMessageQueryResult getRcsMessages( + @NonNull RcsQueryContinuationToken continuationToken) throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getMessagesWithToken(continuationToken)); + } + + /** + * Returns the first chunk of existing {@link RcsEvent}s in the common storage. + * + * @param queryParameters Parameters to specify to return a subset of all RcsEvents. + * Passing a value of null will return all events. + * @throws RcsMessageStoreException if the query could not be completed on the storage + */ + @WorkerThread + @NonNull + public RcsEventQueryResult getRcsEvents( + @Nullable RcsEventQueryParams queryParameters) throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getEvents(queryParameters)); + } - return null; + /** + * Returns the next chunk of {@link RcsEvent}s in the common storage. + * + * @param continuationToken A token to continue the query to get the next chunk. This is + * obtained through {@link RcsEventQueryResult#getContinuationToken}. + * @throws RcsMessageStoreException if the query could not be completed on the storage + */ + @WorkerThread + @NonNull + public RcsEventQueryResult getRcsEvents( + @NonNull RcsQueryContinuationToken continuationToken) throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getEventsWithToken(continuationToken)); + } + + /** + * Persists an {@link RcsEvent} to common storage. + * + * @param persistableEvent The {@link RcsEvent} to persist into storage. + * @throws RcsMessageStoreException if the query could not be completed on the storage + * + * @see RcsGroupThreadNameChangedEvent + * @see RcsGroupThreadIconChangedEvent + * @see RcsGroupThreadParticipantJoinedEvent + * @see RcsGroupThreadParticipantLeftEvent + * @see RcsParticipantAliasChangedEvent + */ + @WorkerThread + @NonNull + public void persistRcsEvent(RcsEvent persistableEvent) throws RcsMessageStoreException { + persistableEvent.persist(); } /** * Creates a new 1 to 1 thread with the given participant and persists it in the storage. + * + * @param recipient The {@link RcsParticipant} that will receive the messages in this thread. + * @return The newly created {@link Rcs1To1Thread} + * @throws RcsMessageStoreException if the thread could not be persisted in the storage */ @WorkerThread - public Rcs1To1Thread createRcs1To1Thread(RcsParticipant recipient) { - try { - IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs")); - if (iRcs != null) { - return iRcs.createRcs1To1Thread(recipient); + @NonNull + public Rcs1To1Thread createRcs1To1Thread(@NonNull RcsParticipant recipient) + throws RcsMessageStoreException { + return new Rcs1To1Thread( + RcsControllerCall.call(iRcs -> iRcs.createRcs1To1Thread(recipient.getId()))); + } + + /** + * Creates a new group thread with the given participants and persists it in the storage. + * + * @throws RcsMessageStoreException if the thread could not be persisted in the storage + */ + @WorkerThread + @NonNull + public RcsGroupThread createGroupThread(@Nullable List<RcsParticipant> recipients, + @Nullable String groupName, @Nullable Uri groupIcon) throws RcsMessageStoreException { + int[] recipientIds = null; + if (recipients != null) { + recipientIds = new int[recipients.size()]; + + for (int i = 0; i < recipients.size(); i++) { + recipientIds[i] = recipients.get(i).getId(); } - } catch (RemoteException re) { - Rlog.e(TAG, "RcsMessageStore: Exception happened during createRcs1To1Thread", re); } - return null; + int[] finalRecipientIds = recipientIds; + return new RcsGroupThread(RcsControllerCall.call( + iRcs -> iRcs.createGroupThread(finalRecipientIds, groupName, groupIcon))); } /** - * Delete the {@link RcsThread} identified by the given threadId. - * @param threadId threadId of the thread to be deleted. + * Delete the given {@link RcsThread} from the storage. + * + * @param thread The thread to be deleted. + * @throws RcsMessageStoreException if the thread could not be deleted from the storage */ @WorkerThread - public void deleteThread(int threadId) { - try { - IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs")); - if (iRcs != null) { - iRcs.deleteThread(threadId); - } - } catch (RemoteException re) { - Rlog.e(TAG, "RcsMessageStore: Exception happened during deleteThread", re); + public void deleteThread(@NonNull RcsThread thread) throws RcsMessageStoreException { + if (thread == null) { + return; + } + + boolean isDeleteSucceeded = RcsControllerCall.call( + iRcs -> iRcs.deleteThread(thread.getThreadId(), thread.getThreadType())); + + if (!isDeleteSucceeded) { + throw new RcsMessageStoreException("Could not delete RcsThread"); } } /** * Creates a new participant and persists it in the storage. + * * @param canonicalAddress The defining address (e.g. phone number) of the participant. + * @param alias The RCS alias for the participant. + * @throws RcsMessageStoreException if the participant could not be created on the storage */ - public RcsParticipant createRcsParticipant(String canonicalAddress) { - try { - IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs")); - if (iRcs != null) { - return iRcs.createRcsParticipant(canonicalAddress); - } - } catch (RemoteException re) { - Rlog.e(TAG, "RcsMessageStore: Exception happened during createRcsParticipant", re); - } - - return null; + @WorkerThread + @NonNull + public RcsParticipant createRcsParticipant(String canonicalAddress, @Nullable String alias) + throws RcsMessageStoreException { + return new RcsParticipant( + RcsControllerCall.call(iRcs -> iRcs.createRcsParticipant(canonicalAddress, alias))); } } diff --git a/telephony/java/android/telephony/ims/RcsParticipantEvent.java b/telephony/java/android/telephony/ims/RcsMessageStoreException.java index 371b8b723d0a..f25bb173be37 100644 --- a/telephony/java/android/telephony/ims/RcsParticipantEvent.java +++ b/telephony/java/android/telephony/ims/RcsMessageStoreException.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 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. @@ -13,13 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.telephony.ims; -import android.os.Parcelable; +package android.telephony.ims; /** - * An event that is associated with an {@link RcsParticipant} - * @hide - TODO(sahinc) make this public + * An exception that happened on {@link RcsMessageStore} or one of the derived storage classes in + * {@link android.telephony.ims} */ -public abstract class RcsParticipantEvent implements Parcelable { +public class RcsMessageStoreException extends Exception { + + /** + * Constructs an {@link RcsMessageStoreException} with the specified detail message. + * @param message The detail message + * @see Throwable#getMessage() + */ + public RcsMessageStoreException(String message) { + super(message); + } } diff --git a/telephony/java/android/telephony/ims/RcsMultiMediaPart.java b/telephony/java/android/telephony/ims/RcsMultiMediaPart.java deleted file mode 100644 index d295fba365f0..000000000000 --- a/telephony/java/android/telephony/ims/RcsMultiMediaPart.java +++ /dev/null @@ -1,50 +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.telephony.ims; - -import android.os.Parcel; - -/** - * A part of a composite {@link RcsMessage} that holds a media that is rendered on the screen - * (i.e. image, video etc) - * @hide - TODO(sahinc) make this public - */ -public class RcsMultiMediaPart extends RcsFileTransferPart { - public static final Creator<RcsMultiMediaPart> CREATOR = new Creator<RcsMultiMediaPart>() { - @Override - public RcsMultiMediaPart createFromParcel(Parcel in) { - return new RcsMultiMediaPart(in); - } - - @Override - public RcsMultiMediaPart[] newArray(int size) { - return new RcsMultiMediaPart[size]; - } - }; - - protected RcsMultiMediaPart(Parcel in) { - super(in); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - } -} diff --git a/telephony/java/android/telephony/ims/RcsMultimediaPart.aidl b/telephony/java/android/telephony/ims/RcsMultimediaPart.aidl deleted file mode 100644 index 5992d95c3b9c..000000000000 --- a/telephony/java/android/telephony/ims/RcsMultimediaPart.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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.telephony.ims; - -parcelable RcsMultimediaPart; diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessage.aidl b/telephony/java/android/telephony/ims/RcsOutgoingMessage.aidl deleted file mode 100644 index 6e0c80f3af81..000000000000 --- a/telephony/java/android/telephony/ims/RcsOutgoingMessage.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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.telephony.ims; - -parcelable RcsOutgoingMessage; diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java index bfb161133618..06fb83268afb 100644 --- a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java +++ b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java @@ -15,34 +15,51 @@ */ package android.telephony.ims; -import android.os.Parcel; +import android.annotation.NonNull; +import android.annotation.WorkerThread; + +import java.util.ArrayList; +import java.util.List; /** * This is a single instance of a message sent over RCS. - * @hide - TODO(sahinc) make this public */ public class RcsOutgoingMessage extends RcsMessage { - public static final Creator<RcsOutgoingMessage> CREATOR = new Creator<RcsOutgoingMessage>() { - @Override - public RcsOutgoingMessage createFromParcel(Parcel in) { - return new RcsOutgoingMessage(in); - } + RcsOutgoingMessage(int id) { + super(id); + } - @Override - public RcsOutgoingMessage[] newArray(int size) { - return new RcsOutgoingMessage[size]; - } - }; + /** + * @return Returns the {@link RcsOutgoingMessageDelivery}s associated with this message. Please + * note that the deliveries returned for the {@link RcsOutgoingMessage} may not always match the + * {@link RcsParticipant}s on the {@link RcsGroupThread} as the group recipients may have + * changed. + * @throws RcsMessageStoreException if the outgoing deliveries could not be read from storage. + */ + @NonNull + @WorkerThread + public List<RcsOutgoingMessageDelivery> getOutgoingDeliveries() + throws RcsMessageStoreException { + int[] deliveryParticipants; + List<RcsOutgoingMessageDelivery> messageDeliveries = new ArrayList<>(); - protected RcsOutgoingMessage(Parcel in) { - } + deliveryParticipants = RcsControllerCall.call( + iRcs -> iRcs.getMessageRecipients(mId)); - @Override - public int describeContents() { - return 0; + if (deliveryParticipants != null) { + for (Integer deliveryParticipant : deliveryParticipants) { + messageDeliveries.add(new RcsOutgoingMessageDelivery(deliveryParticipant, mId)); + } + } + + return messageDeliveries; } + /** + * @return Returns {@code false} as this is not an incoming message. + */ @Override - public void writeToParcel(Parcel dest, int flags) { + public boolean isIncoming() { + return false; } } diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.aidl b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.aidl new file mode 100644 index 000000000000..0c38d9f5766b --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.aidl @@ -0,0 +1,20 @@ +/* + * + * 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.telephony.ims; + +parcelable RcsOutgoingMessageCreationParams; diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java new file mode 100644 index 000000000000..979634a069df --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.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.telephony.ims; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * {@link RcsOutgoingMessageCreationParams} is a collection of parameters that should be passed + * into {@link RcsThread#addOutgoingMessage(RcsOutgoingMessageCreationParams)} to generate an + * {@link RcsOutgoingMessage} on that {@link RcsThread} + */ +public final class RcsOutgoingMessageCreationParams extends RcsMessageCreationParams + implements Parcelable { + /** + * A builder to instantiate and persist an {@link RcsOutgoingMessage} + */ + public static class Builder extends RcsMessageCreationParams.Builder { + + /** + * Creates a new {@link Builder} to create an instance of + * {@link RcsOutgoingMessageCreationParams}. + * + * @param originationTimestamp The timestamp of {@link RcsMessage} creation. The origination + * timestamp value in milliseconds passed after midnight, + * January 1, 1970 UTC + * @param subscriptionId The subscription ID that was used to send or receive this + * {@link RcsMessage} + * @see android.telephony.SubscriptionInfo#getSubscriptionId() + */ + public Builder(long originationTimestamp, int subscriptionId) { + super(originationTimestamp, subscriptionId); + } + + /** + * Creates configuration parameters for a new message. + */ + public RcsOutgoingMessageCreationParams build() { + return new RcsOutgoingMessageCreationParams(this); + } + } + + private RcsOutgoingMessageCreationParams(Builder builder) { + super(builder); + } + + private RcsOutgoingMessageCreationParams(Parcel in) { + super(in); + } + + public static final Creator<RcsOutgoingMessageCreationParams> CREATOR = + new Creator<RcsOutgoingMessageCreationParams>() { + @Override + public RcsOutgoingMessageCreationParams createFromParcel(Parcel in) { + return new RcsOutgoingMessageCreationParams(in); + } + + @Override + public RcsOutgoingMessageCreationParams[] newArray(int size) { + return new RcsOutgoingMessageCreationParams[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest); + } +} diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java b/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java new file mode 100644 index 000000000000..1c87b13f0dfb --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java @@ -0,0 +1,129 @@ +/* + * 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.telephony.ims; + +import android.annotation.NonNull; +import android.annotation.WorkerThread; + +/** + * This class holds the delivery information of an {@link RcsOutgoingMessage} for each + * {@link RcsParticipant} that the message was intended for. + */ +public class RcsOutgoingMessageDelivery { + // The participant that this delivery is intended for + private final int mRecipientId; + // The message this delivery is associated with + private final int mRcsOutgoingMessageId; + + /** + * Constructor to be used with RcsOutgoingMessage.getDelivery() + * + * @hide + */ + RcsOutgoingMessageDelivery(int recipientId, int messageId) { + mRecipientId = recipientId; + mRcsOutgoingMessageId = messageId; + } + + /** + * Sets the delivery time of this outgoing delivery and persists into storage. + * + * @param deliveredTimestamp The timestamp to set to delivery. It is defined as milliseconds + * passed after midnight, January 1, 1970 UTC + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setDeliveredTimestamp(long deliveredTimestamp) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setOutgoingDeliveryDeliveredTimestamp( + mRcsOutgoingMessageId, mRecipientId, deliveredTimestamp)); + } + + /** + * @return Returns the delivered timestamp of the associated message to the associated + * participant. Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC. + * Returns 0 if the {@link RcsOutgoingMessage} is not delivered yet. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public long getDeliveredTimestamp() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getOutgoingDeliveryDeliveredTimestamp( + mRcsOutgoingMessageId, mRecipientId)); + } + + /** + * Sets the seen time of this outgoing delivery and persists into storage. + * + * @param seenTimestamp The timestamp to set to delivery. It is defined as milliseconds + * passed after midnight, January 1, 1970 UTC + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setSeenTimestamp(long seenTimestamp) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setOutgoingDeliverySeenTimestamp( + mRcsOutgoingMessageId, mRecipientId, seenTimestamp)); + } + + /** + * @return Returns the seen timestamp of the associated message by the associated + * participant. Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC. + * Returns 0 if the {@link RcsOutgoingMessage} is not seen yet. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public long getSeenTimestamp() throws RcsMessageStoreException { + return RcsControllerCall.call( + iRcs -> iRcs.getOutgoingDeliverySeenTimestamp(mRcsOutgoingMessageId, mRecipientId)); + } + + /** + * Sets the status of this outgoing delivery and persists into storage. + * + * @param status The status of the associated {@link RcsMessage}s delivery to the associated + * {@link RcsParticipant} + * @throws RcsMessageStoreException if the value could not be persisted into storage + */ + @WorkerThread + public void setStatus(@RcsMessage.RcsMessageStatus int status) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setOutgoingDeliveryStatus( + mRcsOutgoingMessageId, mRecipientId, status)); + } + + /** + * @return Returns the status of this outgoing delivery. + * @throws RcsMessageStoreException if the value could not be read from the storage + */ + @WorkerThread + public @RcsMessage.RcsMessageStatus int getStatus() throws RcsMessageStoreException { + return RcsControllerCall.call( + iRcs -> iRcs.getOutgoingDeliveryStatus(mRcsOutgoingMessageId, mRecipientId)); + } + + /** + * @return Returns the recipient associated with this delivery. + */ + @NonNull + public RcsParticipant getRecipient() { + return new RcsParticipant(mRecipientId); + } + + /** + * @return Returns the {@link RcsOutgoingMessage} associated with this delivery. + */ + @NonNull + public RcsOutgoingMessage getMessage() { + return new RcsOutgoingMessage(mRcsOutgoingMessageId); + } +} diff --git a/telephony/java/android/telephony/ims/RcsParticipant.aidl b/telephony/java/android/telephony/ims/RcsParticipant.aidl deleted file mode 100644 index 1c4436367e54..000000000000 --- a/telephony/java/android/telephony/ims/RcsParticipant.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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.telephony.ims; - -parcelable RcsParticipant; diff --git a/telephony/java/android/telephony/ims/RcsParticipant.java b/telephony/java/android/telephony/ims/RcsParticipant.java index f678ec7e435b..7ba5d8e65f76 100644 --- a/telephony/java/android/telephony/ims/RcsParticipant.java +++ b/telephony/java/android/telephony/ims/RcsParticipant.java @@ -15,33 +15,15 @@ */ package android.telephony.ims; -import static android.telephony.ims.RcsMessageStore.TAG; - -import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.WorkerThread; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.telephony.Rlog; -import android.telephony.ims.aidl.IRcs; -import android.text.TextUtils; - -import com.android.internal.util.Preconditions; /** * RcsParticipant is an RCS capable contact that can participate in {@link RcsThread}s. - * @hide - TODO(sahinc) make this public */ -public class RcsParticipant implements Parcelable { +public class RcsParticipant { // The row ID of this participant in the database private int mId; - // The phone number of this participant - private String mCanonicalAddress; - // The RCS alias of this participant. This is different than the name of the contact in the - // Contacts app - i.e. RCS protocol allows users to define aliases for themselves that doesn't - // require other users to add them as contacts and give them a name. - private String mAlias; /** * Constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController} @@ -49,106 +31,95 @@ public class RcsParticipant implements Parcelable { * * @hide */ - public RcsParticipant(int id, @NonNull String canonicalAddress) { + public RcsParticipant(int id) { mId = id; - mCanonicalAddress = canonicalAddress; } /** - * @return Returns the canonical address (i.e. normalized phone number) for this participant + * @return Returns the canonical address (i.e. normalized phone number) for this + * {@link RcsParticipant} + * @throws RcsMessageStoreException if the value could not be read from the storage */ - public String getCanonicalAddress() { - return mCanonicalAddress; + @Nullable + @WorkerThread + public String getCanonicalAddress() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getRcsParticipantCanonicalAddress(mId)); } /** - * Sets the canonical address for this participant and updates it in storage. - * @param canonicalAddress the canonical address to update to. + * @return Returns the alias for this {@link RcsParticipant}. Alias is usually the real name of + * the person themselves. Please see US5-15 - GSMA RCC.71 (RCS Universal Profile Service + * Definition Document) + * @throws RcsMessageStoreException if the value could not be read from the storage */ + @Nullable @WorkerThread - public void setCanonicalAddress(@NonNull String canonicalAddress) { - Preconditions.checkNotNull(canonicalAddress); - if (canonicalAddress.equals(mCanonicalAddress)) { - return; - } - - mCanonicalAddress = canonicalAddress; - - try { - IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs")); - if (iRcs != null) { - iRcs.updateRcsParticipantCanonicalAddress(mId, mCanonicalAddress); - } - } catch (RemoteException re) { - Rlog.e(TAG, "RcsParticipant: Exception happened during setCanonicalAddress", re); - } + public String getAlias() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getRcsParticipantAlias(mId)); } /** - * @return Returns the alias for this participant. Alias is usually the real name of the person - * themselves. + * Sets the alias for this {@link RcsParticipant} and persists it in storage. Alias is usually + * the real name of the person themselves. Please see US5-15 - GSMA RCC.71 (RCS Universal + * Profile Service Definition Document) + * + * @param alias The alias to set to. + * @throws RcsMessageStoreException if the value could not be persisted into storage */ - public String getAlias() { - return mAlias; + @WorkerThread + public void setAlias(String alias) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setRcsParticipantAlias(mId, alias)); } /** - * Sets the alias for this participant and persists it in storage. Alias is usually the real - * name of the person themselves. + * @return Returns the contact ID for this {@link RcsParticipant}. Contact ID is a unique ID for + * an {@link RcsParticipant} that is RCS provisioned. Please see 4.4.5 - GSMA RCC.53 (RCS Device + * API 1.6 Specification) + * @throws RcsMessageStoreException if the value could not be read from the storage */ + @Nullable @WorkerThread - public void setAlias(String alias) { - if (TextUtils.equals(mAlias, alias)) { - return; - } - mAlias = alias; - - try { - IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs")); - if (iRcs != null) { - iRcs.updateRcsParticipantAlias(mId, mAlias); - } - } catch (RemoteException re) { - Rlog.e(TAG, "RcsParticipant: Exception happened during setCanonicalAddress", re); - } + public String getContactId() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getRcsParticipantContactId(mId)); } /** - * Returns the row id of this participant. This is not meant to be part of the SDK + * Sets the contact ID for this {@link RcsParticipant}. Contact ID is a unique ID for + * an {@link RcsParticipant} that is RCS provisioned. Please see 4.4.5 - GSMA RCC.53 (RCS Device + * API 1.6 Specification) * - * @hide + * @param contactId The contact ID to set to. + * @throws RcsMessageStoreException if the value could not be persisted into storage */ - public int getId() { - return mId; + @WorkerThread + public void setContactId(String contactId) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setRcsParticipantContactId(mId, contactId)); } - public static final Creator<RcsParticipant> CREATOR = new Creator<RcsParticipant>() { - @Override - public RcsParticipant createFromParcel(Parcel in) { - return new RcsParticipant(in); + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; } - - @Override - public RcsParticipant[] newArray(int size) { - return new RcsParticipant[size]; + if (!(obj instanceof RcsParticipant)) { + return false; } - }; + RcsParticipant other = (RcsParticipant) obj; - protected RcsParticipant(Parcel in) { - mId = in.readInt(); - mCanonicalAddress = in.readString(); - mAlias = in.readString(); + return mId == other.mId; } @Override - public int describeContents() { - return 0; + public int hashCode() { + return mId; } - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mId); - dest.writeString(mCanonicalAddress); - dest.writeString(mAlias); + /** + * Returns the row id of this participant. This is not meant to be part of the SDK + * + * @hide + */ + public int getId() { + return mId; } } diff --git a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java index b9ca5a86f84d..c9a2b0d07bc8 100644 --- a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java +++ b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java @@ -15,27 +15,92 @@ */ package android.telephony.ims; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; +import android.os.Parcelable; /** - * An event that indicates an {@link RcsParticipant}'s alias was changed. - * @hide - TODO(sahinc) make this public + * An event that indicates an {@link RcsParticipant}'s alias was changed. Please see US18-2 - GSMA + * RCC.71 (RCS Universal Profile Service Definition Document) */ -public class RcsParticipantAliasChangedEvent extends RcsParticipantEvent { +public final class RcsParticipantAliasChangedEvent extends RcsEvent implements Parcelable { + // The ID of the participant that changed their alias + private final int mParticipantId; + // The new alias of the above participant + private final String mNewAlias; + + /** + * Creates a new {@link RcsParticipantAliasChangedEvent}. This event is not persisted into + * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called. + * + * @param timestamp The timestamp of when this event happened, in milliseconds passed after + * midnight, January 1st, 1970 UTC + * @param participant The {@link RcsParticipant} that got their alias changed + * @param newAlias The new alias the {@link RcsParticipant} has. + * @see RcsMessageStore#persistRcsEvent(RcsEvent) + */ + public RcsParticipantAliasChangedEvent(long timestamp, @NonNull RcsParticipant participant, + @Nullable String newAlias) { + super(timestamp); + mParticipantId = participant.getId(); + mNewAlias = newAlias; + } + + /** + * @hide - internal constructor for queries + */ + public RcsParticipantAliasChangedEvent(long timestamp, int participantId, + @Nullable String newAlias) { + super(timestamp); + mParticipantId = participantId; + mNewAlias = newAlias; + } + + /** + * @return Returns the {@link RcsParticipant} whose alias was changed. + */ + @NonNull + public RcsParticipant getParticipantId() { + return new RcsParticipant(mParticipantId); + } + + /** + * @return Returns the alias of the associated {@link RcsParticipant} after this event happened + */ + @Nullable + public String getNewAlias() { + return mNewAlias; + } + + /** + * Persists the event to the data store. + * + * @hide - not meant for public use. + */ + @Override + public void persist() throws RcsMessageStoreException { + RcsControllerCall.call(iRcs -> iRcs.createParticipantAliasChangedEvent( + getTimestamp(), getParticipantId().getId(), getNewAlias())); + } + public static final Creator<RcsParticipantAliasChangedEvent> CREATOR = new Creator<RcsParticipantAliasChangedEvent>() { - @Override - public RcsParticipantAliasChangedEvent createFromParcel(Parcel in) { - return new RcsParticipantAliasChangedEvent(in); - } + @Override + public RcsParticipantAliasChangedEvent createFromParcel(Parcel in) { + return new RcsParticipantAliasChangedEvent(in); + } - @Override - public RcsParticipantAliasChangedEvent[] newArray(int size) { - return new RcsParticipantAliasChangedEvent[size]; - } - }; + @Override + public RcsParticipantAliasChangedEvent[] newArray(int size) { + return new RcsParticipantAliasChangedEvent[size]; + } + }; - protected RcsParticipantAliasChangedEvent(Parcel in) { + private RcsParticipantAliasChangedEvent(Parcel in) { + super(in); + mNewAlias = in.readString(); + mParticipantId = in.readInt(); } @Override @@ -45,5 +110,8 @@ public class RcsParticipantAliasChangedEvent extends RcsParticipantEvent { @Override public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeString(mNewAlias); + dest.writeInt(mParticipantId); } } diff --git a/telephony/java/android/telephony/ims/RcsParticipantEvent.aidl b/telephony/java/android/telephony/ims/RcsParticipantEvent.aidl deleted file mode 100644 index c0a77897abd5..000000000000 --- a/telephony/java/android/telephony/ims/RcsParticipantEvent.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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.telephony.ims; - -parcelable RcsParticipantEvent; diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryParams.aidl b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.aidl new file mode 100644 index 000000000000..b7c0f93c8c5f --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.aidl @@ -0,0 +1,20 @@ +/* + * + * 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.telephony.ims; + +parcelable RcsParticipantQueryParams; diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java new file mode 100644 index 000000000000..d24d079d7038 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java @@ -0,0 +1,308 @@ +/* + * 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.telephony.ims; + +import android.annotation.CheckResult; +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.security.InvalidParameterException; + +/** + * The parameters to pass into + * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)} in order to select a + * subset of {@link RcsThread}s present in the message store. + */ +public final class RcsParticipantQueryParams implements Parcelable { + /** + * Flag to set with {@link Builder#setSortProperty(int)} to sort the results in the order of + * creation time for faster query results + */ + public static final int SORT_BY_CREATION_ORDER = 0; + + /** + * Flag to set with {@link Builder#setSortProperty(int)} to sort depending on the + * {@link RcsParticipant} aliases + */ + public static final int SORT_BY_ALIAS = 1; + + /** + * Flag to set with {@link Builder#setSortProperty(int)} to sort depending on the + * {@link RcsParticipant} canonical addresses + */ + public static final int SORT_BY_CANONICAL_ADDRESS = 2; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_ALIAS, SORT_BY_CANONICAL_ADDRESS}) + public @interface SortingProperty { + } + + // The SQL "like" statement to filter against participant aliases + private String mAliasLike; + // The SQL "like" statement to filter against canonical addresses + private String mCanonicalAddressLike; + // The property to sort the result against + private @SortingProperty int mSortingProperty; + // Whether to sort the result in ascending order + private boolean mIsAscending; + // The number of results to be returned from the query + private int mLimit; + // Used to limit the results to participants of a single thread + private int mThreadId; + + /** + * @hide + */ + public static final String PARTICIPANT_QUERY_PARAMETERS_KEY = "participant_query_parameters"; + + RcsParticipantQueryParams(int rcsThreadId, String aliasLike, String canonicalAddressLike, + @SortingProperty int sortingProperty, boolean isAscending, + int limit) { + mThreadId = rcsThreadId; + mAliasLike = aliasLike; + mCanonicalAddressLike = canonicalAddressLike; + mSortingProperty = sortingProperty; + mIsAscending = isAscending; + mLimit = limit; + } + + /** + * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get + * the thread that the result query should be limited to. + * + * As we do not expose any sort of integer ID's to public usage, this should be hidden. + * + * @hide - not meant for public use + */ + public int getThreadId() { + return mThreadId; + } + + /** + * @return Returns the SQL-inspired "LIKE" clause that will be used to match + * {@link RcsParticipant}s with respect to their aliases + * + * @see RcsParticipant#getAlias() + */ + public String getAliasLike() { + return mAliasLike; + } + + /** + * @return Returns the SQL-inspired "LIKE" clause that will be used to match + * {@link RcsParticipant}s with respect to their canonical addresses. + * + * @see RcsParticipant#getCanonicalAddress() + */ + public String getCanonicalAddressLike() { + return mCanonicalAddressLike; + } + + /** + * @return Returns the number of {@link RcsParticipant}s to be returned from the query. A value + * of 0 means there is no set limit. + */ + public int getLimit() { + return mLimit; + } + + /** + * @return Returns the property that will be used to sort the result against. + * @see SortingProperty + */ + public int getSortingProperty() { + return mSortingProperty; + } + + /** + * @return Returns {@code true} if the result set will be sorted in ascending order, + * {@code false} if it will be sorted in descending order. + */ + public boolean getSortDirection() { + return mIsAscending; + } + + /** + * A helper class to build the {@link RcsParticipantQueryParams}. + */ + public static class Builder { + private String mAliasLike; + private String mCanonicalAddressLike; + private @SortingProperty int mSortingProperty; + private boolean mIsAscending; + private int mLimit = 100; + private int mThreadId; + + /** + * Creates a new builder for {@link RcsParticipantQueryParams} to be used in + * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)} + */ + public Builder() { + // empty implementation + } + + /** + * Limits the resulting {@link RcsParticipant}s to only the given {@link RcsThread} + * + * @param rcsThread The thread that the participants should be searched in. + * @return The same {@link Builder} to chain methods. + */ + @CheckResult + public Builder setThread(RcsThread rcsThread) { + mThreadId = rcsThread.getThreadId(); + return this; + } + + /** + * Sets an SQL-inspired "like" clause to match with participant aliases. Using a percent + * sign ('%') wildcard matches any sequence of zero or more characters. Using an underscore + * ('_') wildcard matches any single character. Not using any wildcards would only perform a + * string match.The input string is case-insensitive. + * + * The input "An%e" would match {@link RcsParticipant}s with names Anne, Annie, Antonie, + * while the input "An_e" would only match Anne. + * + * @param likeClause The like clause to use for matching {@link RcsParticipant} aliases. + * @return The same {@link Builder} to chain methods + */ + @CheckResult + public Builder setAliasLike(String likeClause) { + mAliasLike = likeClause; + return this; + } + + /** + * Sets an SQL-inspired "like" clause to match with participant addresses. Using a percent + * sign ('%') wildcard matches any sequence of zero or more characters. Using an underscore + * ('_') wildcard matches any single character. Not using any wildcards would only perform a + * string match. The input string is case-insensitive. + * + * The input "+999%111" would match {@link RcsParticipant}s with addresses like "+9995111" + * or "+99955555111", while the input "+999_111" would only match "+9995111". + * + * @param likeClause The like clause to use for matching {@link RcsParticipant} canonical + * addresses. + * @return The same {@link Builder} to chain methods + */ + @CheckResult + public Builder setCanonicalAddressLike(String likeClause) { + mCanonicalAddressLike = likeClause; + return this; + } + + /** + * Desired number of threads to be returned from the query. Passing in 0 will return all + * existing threads at once. The limit defaults to 100. + * + * @param limit The number to limit the query result to. + * @return The same instance of the builder to chain parameters. + * @throws InvalidParameterException If the given limit is negative. + */ + @CheckResult + public Builder setResultLimit(@IntRange(from = 0) int limit) + throws InvalidParameterException { + if (limit < 0) { + throw new InvalidParameterException("The query limit must be non-negative"); + } + + mLimit = limit; + return this; + } + + /** + * Sets the property where the results should be sorted against. Defaults to + * {@link RcsParticipantQueryParams.SortingProperty#SORT_BY_CREATION_ORDER} + * + * @param sortingProperty against which property the results should be sorted + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setSortProperty(@SortingProperty int sortingProperty) { + mSortingProperty = sortingProperty; + return this; + } + + /** + * Sets whether the results should be sorted ascending or descending + * + * @param isAscending whether the results should be sorted ascending + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setSortDirection(boolean isAscending) { + mIsAscending = isAscending; + return this; + } + + /** + * Builds the {@link RcsParticipantQueryParams} to use in + * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)} + * + * @return An instance of {@link RcsParticipantQueryParams} to use with the participant + * query. + */ + public RcsParticipantQueryParams build() { + return new RcsParticipantQueryParams(mThreadId, mAliasLike, mCanonicalAddressLike, + mSortingProperty, mIsAscending, mLimit); + } + } + + /** + * Parcelable boilerplate below. + */ + private RcsParticipantQueryParams(Parcel in) { + mAliasLike = in.readString(); + mCanonicalAddressLike = in.readString(); + mSortingProperty = in.readInt(); + mIsAscending = in.readByte() == 1; + mLimit = in.readInt(); + mThreadId = in.readInt(); + } + + public static final Creator<RcsParticipantQueryParams> CREATOR = + new Creator<RcsParticipantQueryParams>() { + @Override + public RcsParticipantQueryParams createFromParcel(Parcel in) { + return new RcsParticipantQueryParams(in); + } + + @Override + public RcsParticipantQueryParams[] newArray(int size) { + return new RcsParticipantQueryParams[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mAliasLike); + dest.writeString(mCanonicalAddressLike); + dest.writeInt(mSortingProperty); + dest.writeByte((byte) (mIsAscending ? 1 : 0)); + dest.writeInt(mLimit); + dest.writeInt(mThreadId); + } + +} diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryResult.aidl b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.aidl new file mode 100644 index 000000000000..db5c00c8ce00 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.aidl @@ -0,0 +1,20 @@ +/* + * + * 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.telephony.ims; + +parcelable RcsParticipantQueryResult; diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java new file mode 100644 index 000000000000..505f1a55d1f0 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.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.telephony.ims; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; + +/** + * The result of a {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)} + * call. This class allows getting the token for querying the next batch of participants in order to + * prevent handling large amounts of data at once. + */ +public final class RcsParticipantQueryResult implements Parcelable { + // A token for the caller to continue their query for the next batch of results + private RcsQueryContinuationToken mContinuationToken; + // The list of participant IDs returned with this query + private List<Integer> mParticipants; + + /** + * Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController} + * to create query results + * + * @hide + */ + public RcsParticipantQueryResult( + RcsQueryContinuationToken continuationToken, + List<Integer> participants) { + mContinuationToken = continuationToken; + mParticipants = participants; + } + + /** + * Returns a token to call + * {@link RcsMessageStore#getRcsParticipants(RcsQueryContinuationToken)} + * to get the next batch of {@link RcsParticipant}s. + */ + @Nullable + public RcsQueryContinuationToken getContinuationToken() { + return mContinuationToken; + } + + /** + * Returns all the {@link RcsParticipant}s in the current query result. Call {@link + * RcsMessageStore#getRcsParticipants(RcsQueryContinuationToken)} to get the next + * batch of {@link RcsParticipant}s. + */ + @NonNull + public List<RcsParticipant> getParticipants() { + List<RcsParticipant> participantList = new ArrayList<>(); + for (Integer participantId : mParticipants) { + participantList.add(new RcsParticipant(participantId)); + } + + return participantList; + } + + private RcsParticipantQueryResult(Parcel in) { + mContinuationToken = in.readParcelable( + RcsQueryContinuationToken.class.getClassLoader()); + } + + public static final Creator<RcsParticipantQueryResult> CREATOR = + new Creator<RcsParticipantQueryResult>() { + @Override + public RcsParticipantQueryResult createFromParcel(Parcel in) { + return new RcsParticipantQueryResult(in); + } + + @Override + public RcsParticipantQueryResult[] newArray(int size) { + return new RcsParticipantQueryResult[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(mContinuationToken, flags); + } +} diff --git a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.aidl b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.aidl new file mode 100644 index 000000000000..319379a462de --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.aidl @@ -0,0 +1,20 @@ +/* + * + * 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.telephony.ims; + +parcelable RcsQueryContinuationToken; diff --git a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java new file mode 100644 index 000000000000..08643de51d40 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java @@ -0,0 +1,155 @@ +/* + * 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.telephony.ims; + +import android.annotation.IntDef; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * A token for enabling continuation queries. Instances are acquired through + * {@code getContinuationToken} on result objects after initial query is done. + * + * @see RcsEventQueryResult#getContinuationToken() + * @see RcsMessageQueryResult#getContinuationToken() + * @see RcsParticipantQueryResult#getContinuationToken() + * @see RcsThreadQueryResult#getContinuationToken() + */ +public final class RcsQueryContinuationToken implements Parcelable { + /** + * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing + * {@link RcsEvent} queries + */ + public static final int EVENT_QUERY_CONTINUATION_TOKEN_TYPE = 0; + + /** + * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing + * {@link RcsMessage} queries + */ + public static final int MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE = 1; + + /** + * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing + * {@link RcsParticipant} queries + */ + public static final int PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE = 2; + + /** + * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing + * {@link RcsThread} queries + */ + public static final int THREAD_QUERY_CONTINUATION_TOKEN_TYPE = 3; + + /** + * @hide - not meant for public use + */ + public static final String QUERY_CONTINUATION_TOKEN = "query_continuation_token"; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({EVENT_QUERY_CONTINUATION_TOKEN_TYPE, MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE, + PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE, THREAD_QUERY_CONTINUATION_TOKEN_TYPE}) + public @interface ContinuationTokenType {} + + // The type of query this token should allow to continue + private @ContinuationTokenType int mQueryType; + // The raw query string for the initial query + private final String mRawQuery; + // The number of results that is returned with each query + private final int mLimit; + // The offset value that this query should start the query from + private int mOffset; + + /** + * @hide + */ + public RcsQueryContinuationToken(@ContinuationTokenType int queryType, String rawQuery, + int limit, int offset) { + mQueryType = queryType; + mRawQuery = rawQuery; + mLimit = limit; + mOffset = offset; + } + + /** + * Returns the original raw query used on {@link com.android.providers.telephony.RcsProvider} + * @hide + */ + public String getRawQuery() { + return mRawQuery; + } + + /** + * Returns which index this continuation query should start from + * @hide + */ + public int getOffset() { + return mOffset; + } + + /** + * Increments the offset by the amount of result rows returned with the continuation query for + * the next query. + * @hide + */ + public void incrementOffset() { + mOffset += mLimit; + } + + /** + * Returns the type of query that this {@link RcsQueryContinuationToken} is intended to be used + * to continue. + */ + public @ContinuationTokenType int getQueryType() { + return mQueryType; + } + + private RcsQueryContinuationToken(Parcel in) { + mQueryType = in.readInt(); + mRawQuery = in.readString(); + mLimit = in.readInt(); + mOffset = in.readInt(); + } + + public static final Creator<RcsQueryContinuationToken> CREATOR = + new Creator<RcsQueryContinuationToken>() { + @Override + public RcsQueryContinuationToken createFromParcel(Parcel in) { + return new RcsQueryContinuationToken(in); + } + + @Override + public RcsQueryContinuationToken[] newArray(int size) { + return new RcsQueryContinuationToken[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mQueryType); + dest.writeString(mRawQuery); + dest.writeInt(mLimit); + dest.writeInt(mOffset); + } +} diff --git a/telephony/java/android/telephony/ims/RcsTextPart.aidl b/telephony/java/android/telephony/ims/RcsTextPart.aidl deleted file mode 100644 index 4f9fe1fe26fe..000000000000 --- a/telephony/java/android/telephony/ims/RcsTextPart.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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.telephony.ims; - -parcelable RcsTextPart; diff --git a/telephony/java/android/telephony/ims/RcsTextPart.java b/telephony/java/android/telephony/ims/RcsTextPart.java deleted file mode 100644 index 2a72df17f32a..000000000000 --- a/telephony/java/android/telephony/ims/RcsTextPart.java +++ /dev/null @@ -1,48 +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.telephony.ims; - -import android.os.Parcel; - -/** - * A part of a composite {@link RcsMessage} that holds a string - * @hide - TODO(sahinc) make this public - */ -public class RcsTextPart extends RcsPart { - public static final Creator<RcsTextPart> CREATOR = new Creator<RcsTextPart>() { - @Override - public RcsTextPart createFromParcel(Parcel in) { - return new RcsTextPart(in); - } - - @Override - public RcsTextPart[] newArray(int size) { - return new RcsTextPart[size]; - } - }; - - protected RcsTextPart(Parcel in) { - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - } -} diff --git a/telephony/java/android/telephony/ims/RcsThread.aidl b/telephony/java/android/telephony/ims/RcsThread.aidl deleted file mode 100644 index d9cf6dbc0ff0..000000000000 --- a/telephony/java/android/telephony/ims/RcsThread.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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.telephony; - -parcelable RcsThread;
\ No newline at end of file diff --git a/telephony/java/android/telephony/ims/RcsThread.java b/telephony/java/android/telephony/ims/RcsThread.java index c0a0d946d204..e015dd3e9c0a 100644 --- a/telephony/java/android/telephony/ims/RcsThread.java +++ b/telephony/java/android/telephony/ims/RcsThread.java @@ -16,60 +16,118 @@ package android.telephony.ims; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Log; +import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_1_TO_1; +import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_GROUP; + +import android.annotation.NonNull; +import android.annotation.WorkerThread; + +import com.android.internal.annotations.VisibleForTesting; /** * RcsThread represents a single RCS conversation thread. It holds messages that were sent and * received and events that occurred on that thread. - * @hide - TODO(sahinc) make this public */ -public abstract class RcsThread implements Parcelable { - // Since this is an abstract class that gets parcelled, the sub-classes need to write these - // magic values into the parcel so that we know which type to unparcel into. - protected static final int RCS_1_TO_1_TYPE = 998; - protected static final int RCS_GROUP_TYPE = 999; - +public abstract class RcsThread { + /** + * The rcs_participant_thread_id that represents this thread in the database + * @hide + */ protected int mThreadId; + /** + * @hide + */ protected RcsThread(int threadId) { mThreadId = threadId; } - protected RcsThread(Parcel in) { - mThreadId = in.readInt(); + /** + * @return Returns the summary of the latest message in this {@link RcsThread} packaged in an + * {@link RcsMessageSnippet} object + */ + @WorkerThread + @NonNull + public RcsMessageSnippet getSnippet() throws RcsMessageStoreException { + return RcsControllerCall.call(iRcs -> iRcs.getMessageSnippet(mThreadId)); + } + + /** + * Adds a new {@link RcsIncomingMessage} to this RcsThread and persists it in storage. + * + * @throws RcsMessageStoreException if the message could not be persisted into storage. + */ + @WorkerThread + @NonNull + public RcsIncomingMessage addIncomingMessage( + @NonNull RcsIncomingMessageCreationParams rcsIncomingMessageCreationParams) + throws RcsMessageStoreException { + return new RcsIncomingMessage(RcsControllerCall.call(iRcs -> iRcs.addIncomingMessage( + mThreadId, rcsIncomingMessageCreationParams))); } - public static final Creator<RcsThread> CREATOR = new Creator<RcsThread>() { - @Override - public RcsThread createFromParcel(Parcel in) { - int type = in.readInt(); + /** + * Adds a new {@link RcsOutgoingMessage} to this RcsThread and persists it in storage. + * + * @throws RcsMessageStoreException if the message could not be persisted into storage. + */ + @WorkerThread + @NonNull + public RcsOutgoingMessage addOutgoingMessage( + @NonNull RcsOutgoingMessageCreationParams rcsOutgoingMessageCreationParams) + throws RcsMessageStoreException { + int messageId = RcsControllerCall.call(iRcs -> iRcs.addOutgoingMessage( + mThreadId, rcsOutgoingMessageCreationParams)); - switch (type) { - case RCS_1_TO_1_TYPE: - return new Rcs1To1Thread(in); - case RCS_GROUP_TYPE: - return new RcsGroupThread(in); - default: - Log.e(RcsMessageStore.TAG, "Cannot unparcel RcsThread, wrong type: " + type); - } - return null; - } + return new RcsOutgoingMessage(messageId); + } + + /** + * Deletes an {@link RcsMessage} from this RcsThread and updates the storage. + * + * @param rcsMessage The message to delete from the thread + * @throws RcsMessageStoreException if the message could not be deleted + */ + @WorkerThread + public void deleteMessage(@NonNull RcsMessage rcsMessage) throws RcsMessageStoreException { + RcsControllerCall.callWithNoReturn( + iRcs -> iRcs.deleteMessage(rcsMessage.getId(), rcsMessage.isIncoming(), mThreadId, + isGroup())); + } + + /** + * Convenience function for loading all the {@link RcsMessage}s in this {@link RcsThread}. For + * a more detailed and paginated query, please use + * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} + * + * @return Loads the {@link RcsMessage}s in this thread and returns them in an immutable list. + * @throws RcsMessageStoreException if the messages could not be read from the storage + */ + @WorkerThread + @NonNull + public RcsMessageQueryResult getMessages() throws RcsMessageStoreException { + RcsMessageQueryParams queryParameters = + new RcsMessageQueryParams.Builder().setThread(this).build(); + return RcsControllerCall.call(iRcs -> iRcs.getMessages(queryParameters)); + } - @Override - public RcsThread[] newArray(int size) { - return new RcsThread[0]; - } - }; + /** + * @return Returns whether this is a group thread or not + */ + public abstract boolean isGroup(); - @Override - public int describeContents() { - return 0; + /** + * @hide + */ + @VisibleForTesting + public int getThreadId() { + return mThreadId; } - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mThreadId); + /** + * @hide + */ + public int getThreadType() { + return isGroup() ? THREAD_TYPE_GROUP : THREAD_TYPE_1_TO_1; } } diff --git a/telephony/java/android/telephony/ims/RcsThreadEvent.aidl b/telephony/java/android/telephony/ims/RcsThreadEvent.aidl deleted file mode 100644 index 4a40d8906bbb..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadEvent.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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.telephony.ims; - -parcelable RcsThreadEvent; diff --git a/telephony/java/android/telephony/ims/RcsThreadEvent.java b/telephony/java/android/telephony/ims/RcsThreadEvent.java deleted file mode 100644 index e10baab9d8c5..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadEvent.java +++ /dev/null @@ -1,25 +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.telephony.ims; - -import android.os.Parcelable; - -/** - * An event that happened on an {@link RcsThread}. - * @hide - TODO(sahinc) make this public - */ -public abstract class RcsThreadEvent implements Parcelable { -} diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl deleted file mode 100644 index 82d985df4c6c..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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.telephony.ims; - -parcelable RcsThreadIconChangedEvent; diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.java b/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.java deleted file mode 100644 index b308fef46435..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.java +++ /dev/null @@ -1,49 +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.telephony.ims; - -import android.os.Parcel; - -/** - * An event that indicates an {@link RcsGroupThread}'s icon was changed. - * @hide - TODO(sahinc) make this public - */ -public class RcsThreadIconChangedEvent extends RcsThreadEvent { - public static final Creator<RcsThreadIconChangedEvent> CREATOR = - new Creator<RcsThreadIconChangedEvent>() { - @Override - public RcsThreadIconChangedEvent createFromParcel(Parcel in) { - return new RcsThreadIconChangedEvent(in); - } - - @Override - public RcsThreadIconChangedEvent[] newArray(int size) { - return new RcsThreadIconChangedEvent[size]; - } - }; - - protected RcsThreadIconChangedEvent(Parcel in) { - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - } -} diff --git a/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.aidl deleted file mode 100644 index 54a311d02958..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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.telephony.ims; - -parcelable RcsThreadNameChangedEvent; diff --git a/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.java b/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.java deleted file mode 100644 index 6f5cfdf3b4c4..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.java +++ /dev/null @@ -1,49 +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.telephony.ims; - -import android.os.Parcel; - -/** - * An event that indicates an {@link RcsGroupThread}'s name was changed. - * @hide - TODO(sahinc) make this public - */ -public class RcsThreadNameChangedEvent extends RcsThreadEvent { - public static final Creator<RcsThreadNameChangedEvent> CREATOR = - new Creator<RcsThreadNameChangedEvent>() { - @Override - public RcsThreadNameChangedEvent createFromParcel(Parcel in) { - return new RcsThreadNameChangedEvent(in); - } - - @Override - public RcsThreadNameChangedEvent[] newArray(int size) { - return new RcsThreadNameChangedEvent[size]; - } - }; - - protected RcsThreadNameChangedEvent(Parcel in) { - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - } -} diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.aidl b/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.aidl deleted file mode 100644 index 047a42466ee7..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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.telephony.ims; - -parcelable RcsThreadParticipantJoinedEvent; diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.java b/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.java deleted file mode 100644 index 5c4073c430e7..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.java +++ /dev/null @@ -1,49 +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.telephony.ims; - -import android.os.Parcel; - -/** - * An event that indicates an RCS participant has joined an {@link RcsGroupThread}. - * @hide - TODO(sahinc) make this public - */ -public class RcsThreadParticipantJoinedEvent extends RcsThreadEvent { - public static final Creator<RcsThreadParticipantJoinedEvent> CREATOR = - new Creator<RcsThreadParticipantJoinedEvent>() { - @Override - public RcsThreadParticipantJoinedEvent createFromParcel(Parcel in) { - return new RcsThreadParticipantJoinedEvent(in); - } - - @Override - public RcsThreadParticipantJoinedEvent[] newArray(int size) { - return new RcsThreadParticipantJoinedEvent[size]; - } - }; - - protected RcsThreadParticipantJoinedEvent(Parcel in) { - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - } -} diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.aidl b/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.aidl deleted file mode 100644 index 52f9bbd3cd93..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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.telephony.ims; - -parcelable RcsThreadParticipantLeftEvent; diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.java b/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.java deleted file mode 100644 index 4bf86b90ebb7..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.java +++ /dev/null @@ -1,49 +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.telephony.ims; - -import android.os.Parcel; - -/** - * An event that indicates an RCS participant has left an {@link RcsGroupThread}. - * @hide - TODO(sahinc) make this public - */ -public class RcsThreadParticipantLeftEvent extends RcsThreadEvent { - public static final Creator<RcsThreadParticipantLeftEvent> CREATOR = - new Creator<RcsThreadParticipantLeftEvent>() { - @Override - public RcsThreadParticipantLeftEvent createFromParcel(Parcel in) { - return new RcsThreadParticipantLeftEvent(in); - } - - @Override - public RcsThreadParticipantLeftEvent[] newArray(int size) { - return new RcsThreadParticipantLeftEvent[size]; - } - }; - - protected RcsThreadParticipantLeftEvent(Parcel in) { - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - } -} diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl deleted file mode 100644 index 7bcebfa08fcb..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* -** -** Copyright 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.telephony.ims; - -parcelable RcsThreadQueryContinuationToken; diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java b/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java deleted file mode 100644 index 931e93dc8bf1..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java +++ /dev/null @@ -1,52 +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.telephony.ims; - -import android.os.Parcel; -import android.os.Parcelable; - -/** - * A continuation token to provide for {@link RcsMessageStore#getRcsThreads}. Use this token to - * break large queries into manageable chunks - * @hide - TODO make this public - */ -public class RcsThreadQueryContinuationToken implements Parcelable { - protected RcsThreadQueryContinuationToken(Parcel in) { - } - - public static final Creator<RcsThreadQueryContinuationToken> CREATOR = - new Creator<RcsThreadQueryContinuationToken>() { - @Override - public RcsThreadQueryContinuationToken createFromParcel(Parcel in) { - return new RcsThreadQueryContinuationToken(in); - } - - @Override - public RcsThreadQueryContinuationToken[] newArray(int size) { - return new RcsThreadQueryContinuationToken[size]; - } - }; - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - } -} diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl deleted file mode 100644 index feb2d4dec094..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* -** -** Copyright 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.telephony.ims; - -parcelable RcsThreadQueryParameters; diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java deleted file mode 100644 index f2c4ab1884ca..000000000000 --- a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java +++ /dev/null @@ -1,225 +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.telephony.ims; - -import android.annotation.CheckResult; -import android.os.Parcel; -import android.os.Parcelable; - -import java.security.InvalidParameterException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; - -/** - * The parameters to pass into {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParameters)} in - * order to select a subset of {@link RcsThread}s present in the message store. - * @hide TODO - make the Builder and builder() public. The rest should stay internal only. - */ -public class RcsThreadQueryParameters implements Parcelable { - private final boolean mIsGroup; - private final Set<RcsParticipant> mRcsParticipants; - private final int mLimit; - private final boolean mIsAscending; - - RcsThreadQueryParameters(boolean isGroup, Set<RcsParticipant> participants, int limit, - boolean isAscending) { - mIsGroup = isGroup; - mRcsParticipants = participants; - mLimit = limit; - mIsAscending = isAscending; - } - - /** - * Returns a new builder to build a query with. - * TODO - make public - */ - public static Builder builder() { - return new Builder(); - } - - /** - * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get - * the list of participants. - * @hide - */ - public Set<RcsParticipant> getRcsParticipants() { - return mRcsParticipants; - } - - /** - * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get - * whether group threads should be queried - * @hide - */ - public boolean isGroupThread() { - return mIsGroup; - } - - /** - * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get - * the number of tuples the result query should be limited to. - */ - public int getLimit() { - return mLimit; - } - - /** - * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to - * determine the sort order. - */ - public boolean isAscending() { - return mIsAscending; - } - - /** - * A helper class to build the {@link RcsThreadQueryParameters}. - */ - public static class Builder { - private boolean mIsGroupThread; - private Set<RcsParticipant> mParticipants; - private int mLimit = 100; - private boolean mIsAscending; - - /** - * Package private constructor for {@link RcsThreadQueryParameters.Builder}. To obtain this, - * {@link RcsThreadQueryParameters#builder()} needs to be called. - */ - Builder() { - mParticipants = new HashSet<>(); - } - - /** - * Limits the query to only return group threads. - * @param isGroupThread Whether to limit the query result to group threads. - * @return The same instance of the builder to chain parameters. - */ - @CheckResult - public Builder isGroupThread(boolean isGroupThread) { - mIsGroupThread = isGroupThread; - return this; - } - - /** - * Limits the query to only return threads that contain the given participant. - * @param participant The participant that must be included in all of the returned threads. - * @return The same instance of the builder to chain parameters. - */ - @CheckResult - public Builder withParticipant(RcsParticipant participant) { - mParticipants.add(participant); - return this; - } - - /** - * Limits the query to only return threads that contain the given list of participants. - * @param participants An iterable list of participants that must be included in all of the - * returned threads. - * @return The same instance of the builder to chain parameters. - */ - @CheckResult - public Builder withParticipants(Iterable<RcsParticipant> participants) { - for (RcsParticipant participant : participants) { - mParticipants.add(participant); - } - return this; - } - - /** - * Desired number of threads to be returned from the query. Passing in 0 will return all - * existing threads at once. The limit defaults to 100. - * @param limit The number to limit the query result to. - * @return The same instance of the builder to chain parameters. - * @throws InvalidParameterException If the given limit is negative. - */ - @CheckResult - public Builder limitResultsTo(int limit) throws InvalidParameterException { - if (limit < 0) { - throw new InvalidParameterException("The query limit must be non-negative"); - } - - mLimit = limit; - return this; - } - - /** - * Sorts the results returned from the query via thread IDs. - * - * TODO - add sorting support for other fields - * - * @param isAscending whether to sort in ascending order or not - * @return The same instance of the builder to chain parameters. - */ - @CheckResult - public Builder sort(boolean isAscending) { - mIsAscending = isAscending; - return this; - } - - /** - * Builds the {@link RcsThreadQueryParameters} to use in - * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParameters)} - * - * @return An instance of {@link RcsThreadQueryParameters} to use with the thread query. - */ - public RcsThreadQueryParameters build() { - return new RcsThreadQueryParameters( - mIsGroupThread, mParticipants, mLimit, mIsAscending); - } - } - - /** - * Parcelable boilerplate below. - */ - protected RcsThreadQueryParameters(Parcel in) { - mIsGroup = in.readBoolean(); - - ArrayList<RcsParticipant> participantArrayList = new ArrayList<>(); - in.readTypedList(participantArrayList, RcsParticipant.CREATOR); - mRcsParticipants = new HashSet<>(participantArrayList); - - mLimit = in.readInt(); - mIsAscending = in.readBoolean(); - } - - public static final Creator<RcsThreadQueryParameters> CREATOR = - new Creator<RcsThreadQueryParameters>() { - @Override - public RcsThreadQueryParameters createFromParcel(Parcel in) { - return new RcsThreadQueryParameters(in); - } - - @Override - public RcsThreadQueryParameters[] newArray(int size) { - return new RcsThreadQueryParameters[size]; - } - }; - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeBoolean(mIsGroup); - dest.writeTypedList(new ArrayList<>(mRcsParticipants)); - dest.writeInt(mLimit); - dest.writeBoolean(mIsAscending); - } - -} diff --git a/telephony/java/android/telephony/ims/RcsFileTransferPart.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryParams.aidl index eaf312877deb..3f351dc5efee 100644 --- a/telephony/java/android/telephony/ims/RcsFileTransferPart.aidl +++ b/telephony/java/android/telephony/ims/RcsThreadQueryParams.aidl @@ -17,4 +17,4 @@ package android.telephony.ims; -parcelable RcsFileTransferPart; +parcelable RcsThreadQueryParams; diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParams.java b/telephony/java/android/telephony/ims/RcsThreadQueryParams.java new file mode 100644 index 000000000000..05a5a3917691 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsThreadQueryParams.java @@ -0,0 +1,303 @@ +/* + * 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.telephony.ims; + +import android.annotation.CheckResult; +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.security.InvalidParameterException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * The parameters to pass into {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} in + * order to select a subset of {@link RcsThread}s present in the message store. + */ +public final class RcsThreadQueryParams implements Parcelable { + /** + * Bitmask flag to be used with {@link Builder#setThreadType(int)} to make + * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} return + * {@link RcsGroupThread}s. + */ + public static final int THREAD_TYPE_GROUP = 0x0001; + + /** + * Bitmask flag to be used with {@link Builder#setThreadType(int)} to make + * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} return + * {@link Rcs1To1Thread}s. + */ + public static final int THREAD_TYPE_1_TO_1 = 0x0002; + + // The type of threads to be filtered with the query + private final int mThreadType; + // The list of participants that are expected in the resulting threads + private final List<Integer> mRcsParticipantIds; + // The number of RcsThread's that should be returned with this query + private final int mLimit; + // The property which the result of the query should be sorted against + private final @SortingProperty int mSortingProperty; + // Whether the sorting should be done in ascending + private final boolean mIsAscending; + + /** + * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should + * be sorted in the order of {@link RcsThread} creation time for faster results. + */ + public static final int SORT_BY_CREATION_ORDER = 0; + + /** + * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should + * be sorted according to the timestamp of {@link RcsThread#getSnippet()} + */ + public static final int SORT_BY_TIMESTAMP = 1; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_TIMESTAMP}) + public @interface SortingProperty { + } + + /** + * @hide + */ + public static final String THREAD_QUERY_PARAMETERS_KEY = "thread_query_parameters"; + + RcsThreadQueryParams(int threadType, Set<RcsParticipant> participants, + int limit, int sortingProperty, boolean isAscending) { + mThreadType = threadType; + mRcsParticipantIds = convertParticipantSetToIdList(participants); + mLimit = limit; + mSortingProperty = sortingProperty; + mIsAscending = isAscending; + } + + private static List<Integer> convertParticipantSetToIdList(Set<RcsParticipant> participants) { + List<Integer> ids = new ArrayList<>(participants.size()); + for (RcsParticipant participant : participants) { + ids.add(participant.getId()); + } + return ids; + } + + /** + * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get + * the list of participant IDs. + * + * As we don't expose any integer ID's to API users, this should stay hidden + * + * @hide - not meant for public use + */ + public List<Integer> getRcsParticipantsIds() { + return Collections.unmodifiableList(mRcsParticipantIds); + } + + /** + * @return Returns the bitmask flag for types of {@link RcsThread}s that this query should + * return. + */ + public int getThreadType() { + return mThreadType; + } + + /** + * @return Returns the number of {@link RcsThread}s to be returned from the query. A value + * of 0 means there is no set limit. + */ + public int getLimit() { + return mLimit; + } + + /** + * @return Returns the property that will be used to sort the result against. + * @see SortingProperty + */ + public @SortingProperty int getSortingProperty() { + return mSortingProperty; + } + + /** + * @return Returns {@code true} if the result set will be sorted in ascending order, + * {@code false} if it will be sorted in descending order. + */ + public boolean getSortDirection() { + return mIsAscending; + } + + /** + * A helper class to build the {@link RcsThreadQueryParams}. + */ + public static class Builder { + private int mThreadType; + private Set<RcsParticipant> mParticipants; + private int mLimit = 100; + private @SortingProperty int mSortingProperty; + private boolean mIsAscending; + + /** + * Constructs a {@link RcsThreadQueryParams.Builder} to help build an + * {@link RcsThreadQueryParams} + */ + public Builder() { + mParticipants = new HashSet<>(); + } + + /** + * Limits the query to only return group threads. + * + * @param threadType Whether to limit the query result to group threads. + * @return The same instance of the builder to chain parameters. + * @see RcsThreadQueryParams#THREAD_TYPE_GROUP + * @see RcsThreadQueryParams#THREAD_TYPE_1_TO_1 + */ + @CheckResult + public Builder setThreadType(int threadType) { + mThreadType = threadType; + return this; + } + + /** + * Limits the query to only return threads that contain the given participant. If this + * property was not set, participants will not be taken into account while querying for + * threads. + * + * @param participant The participant that must be included in all of the returned threads. + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setParticipant(@NonNull RcsParticipant participant) { + mParticipants.add(participant); + return this; + } + + /** + * Limits the query to only return threads that contain the given list of participants. If + * this property was not set, participants will not be taken into account while querying + * for threads. + * + * @param participants An iterable list of participants that must be included in all of the + * returned threads. + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setParticipants(@NonNull List<RcsParticipant> participants) { + mParticipants.addAll(participants); + return this; + } + + /** + * Desired number of threads to be returned from the query. Passing in 0 will return all + * existing threads at once. The limit defaults to 100. + * + * @param limit The number to limit the query result to. + * @return The same instance of the builder to chain parameters. + * @throws InvalidParameterException If the given limit is negative. + */ + @CheckResult + public Builder setResultLimit(@IntRange(from = 0) int limit) + throws InvalidParameterException { + if (limit < 0) { + throw new InvalidParameterException("The query limit must be non-negative"); + } + + mLimit = limit; + return this; + } + + /** + * Sets the property where the results should be sorted against. Defaults to + * {@link SortingProperty#SORT_BY_CREATION_ORDER} + * + * @param sortingProperty whether to sort in ascending order or not + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setSortProperty(@SortingProperty int sortingProperty) { + mSortingProperty = sortingProperty; + return this; + } + + /** + * Sets whether the results should be sorted ascending or descending + * + * @param isAscending whether the results should be sorted ascending + * @return The same instance of the builder to chain parameters. + */ + @CheckResult + public Builder setSortDirection(boolean isAscending) { + mIsAscending = isAscending; + return this; + } + + /** + * Builds the {@link RcsThreadQueryParams} to use in + * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} + * + * @return An instance of {@link RcsThreadQueryParams} to use with the thread query. + */ + public RcsThreadQueryParams build() { + return new RcsThreadQueryParams(mThreadType, mParticipants, mLimit, + mSortingProperty, mIsAscending); + } + } + + /** + * Parcelable boilerplate below. + */ + private RcsThreadQueryParams(Parcel in) { + mThreadType = in.readInt(); + mRcsParticipantIds = new ArrayList<>(); + in.readList(mRcsParticipantIds, Integer.class.getClassLoader()); + mLimit = in.readInt(); + mSortingProperty = in.readInt(); + mIsAscending = in.readByte() == 1; + } + + public static final Creator<RcsThreadQueryParams> CREATOR = + new Creator<RcsThreadQueryParams>() { + @Override + public RcsThreadQueryParams createFromParcel(Parcel in) { + return new RcsThreadQueryParams(in); + } + + @Override + public RcsThreadQueryParams[] newArray(int size) { + return new RcsThreadQueryParams[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mThreadType); + dest.writeList(mRcsParticipantIds); + dest.writeInt(mLimit); + dest.writeInt(mSortingProperty); + dest.writeByte((byte) (mIsAscending ? 1 : 0)); + } +} diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl index 4b06529d1294..b1d5cf4c7211 100644 --- a/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl +++ b/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl @@ -1,19 +1,19 @@ /* -** -** Copyright 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. -*/ + * + * 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.telephony.ims; diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java index 47715f8410d6..1cac61d1aa64 100644 --- a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java +++ b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 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. @@ -16,22 +16,28 @@ package android.telephony.ims; +import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_1_TO_1; + +import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; +import com.android.ims.RcsTypeIdPair; + +import java.util.ArrayList; import java.util.List; /** - * The result of a {@link RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken, - * RcsThreadQueryParameters)} + * The result of a {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} * call. This class allows getting the token for querying the next batch of threads in order to * prevent handling large amounts of data at once. - * - * @hide */ -public class RcsThreadQueryResult implements Parcelable { - private RcsThreadQueryContinuationToken mContinuationToken; - private List<RcsThread> mRcsThreads; +public final class RcsThreadQueryResult implements Parcelable { + // A token for the caller to continue their query for the next batch of results + private RcsQueryContinuationToken mContinuationToken; + // The list of thread IDs returned with this query + private List<RcsTypeIdPair> mRcsThreadIds; /** * Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController} @@ -40,31 +46,47 @@ public class RcsThreadQueryResult implements Parcelable { * @hide */ public RcsThreadQueryResult( - RcsThreadQueryContinuationToken continuationToken, List<RcsThread> rcsThreads) { + RcsQueryContinuationToken continuationToken, + List<RcsTypeIdPair> rcsThreadIds) { mContinuationToken = continuationToken; - mRcsThreads = rcsThreads; + mRcsThreadIds = rcsThreadIds; } /** * Returns a token to call - * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken)} + * {@link RcsMessageStore#getRcsThreads(RcsQueryContinuationToken)} * to get the next batch of {@link RcsThread}s. */ - public RcsThreadQueryContinuationToken nextChunkToken() { + @Nullable + public RcsQueryContinuationToken getContinuationToken() { return mContinuationToken; } /** * Returns all the RcsThreads in the current query result. Call {@link - * RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken)} to get the next batch of + * RcsMessageStore#getRcsThreads(RcsQueryContinuationToken)} to get the next batch of * {@link RcsThread}s. */ + @NonNull public List<RcsThread> getThreads() { - return mRcsThreads; + List<RcsThread> rcsThreads = new ArrayList<>(); + + for (RcsTypeIdPair typeIdPair : mRcsThreadIds) { + if (typeIdPair.getType() == THREAD_TYPE_1_TO_1) { + rcsThreads.add(new Rcs1To1Thread(typeIdPair.getId())); + } else { + rcsThreads.add(new RcsGroupThread(typeIdPair.getId())); + } + } + + return rcsThreads; } - protected RcsThreadQueryResult(Parcel in) { - // TODO - implement + private RcsThreadQueryResult(Parcel in) { + mContinuationToken = in.readParcelable( + RcsQueryContinuationToken.class.getClassLoader()); + mRcsThreadIds = new ArrayList<>(); + in.readList(mRcsThreadIds, Integer.class.getClassLoader()); } public static final Creator<RcsThreadQueryResult> CREATOR = @@ -87,6 +109,7 @@ public class RcsThreadQueryResult implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - // TODO - implement + dest.writeParcelable(mContinuationToken, flags); + dest.writeList(mRcsThreadIds); } } diff --git a/telephony/java/android/telephony/ims/aidl/IRcs.aidl b/telephony/java/android/telephony/ims/aidl/IRcs.aidl index 0c958ba719f3..2478f8cff6b7 100644 --- a/telephony/java/android/telephony/ims/aidl/IRcs.aidl +++ b/telephony/java/android/telephony/ims/aidl/IRcs.aidl @@ -16,10 +16,19 @@ package android.telephony.ims.aidl; -import android.telephony.ims.RcsParticipant; -import android.telephony.ims.Rcs1To1Thread; -import android.telephony.ims.RcsThreadQueryContinuationToken; -import android.telephony.ims.RcsThreadQueryParameters; +import android.net.Uri; +import android.telephony.ims.RcsEventQueryParams; +import android.telephony.ims.RcsEventQueryResult; +import android.telephony.ims.RcsFileTransferCreationParams; +import android.telephony.ims.RcsIncomingMessageCreationParams; +import android.telephony.ims.RcsMessageSnippet; +import android.telephony.ims.RcsMessageQueryParams; +import android.telephony.ims.RcsMessageQueryResult; +import android.telephony.ims.RcsOutgoingMessageCreationParams; +import android.telephony.ims.RcsParticipantQueryParams; +import android.telephony.ims.RcsParticipantQueryResult; +import android.telephony.ims.RcsQueryContinuationToken; +import android.telephony.ims.RcsThreadQueryParams; import android.telephony.ims.RcsThreadQueryResult; /** @@ -27,23 +36,231 @@ import android.telephony.ims.RcsThreadQueryResult; * {@hide} */ interface IRcs { + ///////////////////////// // RcsMessageStore APIs - RcsThreadQueryResult getRcsThreads(in RcsThreadQueryParameters queryParameters); + ///////////////////////// + RcsThreadQueryResult getRcsThreads(in RcsThreadQueryParams queryParams); RcsThreadQueryResult getRcsThreadsWithToken( - in RcsThreadQueryContinuationToken continuationToken); + in RcsQueryContinuationToken continuationToken); - void deleteThread(int threadId); + RcsParticipantQueryResult getParticipants(in RcsParticipantQueryParams queryParams); - Rcs1To1Thread createRcs1To1Thread(in RcsParticipant participant); + RcsParticipantQueryResult getParticipantsWithToken( + in RcsQueryContinuationToken continuationToken); + RcsMessageQueryResult getMessages(in RcsMessageQueryParams queryParams); + + RcsMessageQueryResult getMessagesWithToken( + in RcsQueryContinuationToken continuationToken); + + RcsEventQueryResult getEvents(in RcsEventQueryParams queryParams); + + RcsEventQueryResult getEventsWithToken( + in RcsQueryContinuationToken continuationToken); + + // returns true if the thread was successfully deleted + boolean deleteThread(int threadId, int threadType); + + // Creates an Rcs1To1Thread and returns its row ID + int createRcs1To1Thread(int participantId); + + // Creates an RcsGroupThread and returns its row ID + int createGroupThread(in int[] participantIds, String groupName, in Uri groupIcon); + + ///////////////////////// // RcsThread APIs - int getMessageCount(int rcsThreadId); + ///////////////////////// + + // Creates a new RcsIncomingMessage on the given thread and returns its row ID + int addIncomingMessage(int rcsThreadId, + in RcsIncomingMessageCreationParams rcsIncomingMessageCreationParams); + + // Creates a new RcsOutgoingMessage on the given thread and returns its row ID + int addOutgoingMessage(int rcsThreadId, + in RcsOutgoingMessageCreationParams rcsOutgoingMessageCreationParams); + + // TODO: modify RcsProvider URI's to allow deleting a message without specifying its thread + void deleteMessage(int rcsMessageId, boolean isIncoming, int rcsThreadId, boolean isGroup); + + RcsMessageSnippet getMessageSnippet(int rcsThreadId); + + ///////////////////////// + // Rcs1To1Thread APIs + ///////////////////////// + void set1To1ThreadFallbackThreadId(int rcsThreadId, long fallbackId); + + long get1To1ThreadFallbackThreadId(int rcsThreadId); + + int get1To1ThreadOtherParticipantId(int rcsThreadId); + + ///////////////////////// + // RcsGroupThread APIs + ///////////////////////// + void setGroupThreadName(int rcsThreadId, String groupName); + + String getGroupThreadName(int rcsThreadId); + + void setGroupThreadIcon(int rcsThreadId, in Uri groupIcon); + + Uri getGroupThreadIcon(int rcsThreadId); + + void setGroupThreadOwner(int rcsThreadId, int participantId); + + int getGroupThreadOwner(int rcsThreadId); + + void setGroupThreadConferenceUri(int rcsThreadId, in Uri conferenceUri); + + Uri getGroupThreadConferenceUri(int rcsThreadId); + void addParticipantToGroupThread(int rcsThreadId, int participantId); + + void removeParticipantFromGroupThread(int rcsThreadId, int participantId); + + ///////////////////////// // RcsParticipant APIs - RcsParticipant createRcsParticipant(String canonicalAddress); + ///////////////////////// + + // Creates a new RcsParticipant and returns its rowId + int createRcsParticipant(String canonicalAddress, String alias); + + String getRcsParticipantCanonicalAddress(int participantId); + + String getRcsParticipantAlias(int participantId); + + void setRcsParticipantAlias(int id, String alias); + + String getRcsParticipantContactId(int participantId); + + void setRcsParticipantContactId(int participantId, String contactId); + + ///////////////////////// + // RcsMessage APIs + ///////////////////////// + void setMessageSubId(int messageId, boolean isIncoming, int subId); + + int getMessageSubId(int messageId, boolean isIncoming); + + void setMessageStatus(int messageId, boolean isIncoming, int status); + + int getMessageStatus(int messageId, boolean isIncoming); + + void setMessageOriginationTimestamp(int messageId, boolean isIncoming, long originationTimestamp); + + long getMessageOriginationTimestamp(int messageId, boolean isIncoming); + + void setGlobalMessageIdForMessage(int messageId, boolean isIncoming, String globalId); + + String getGlobalMessageIdForMessage(int messageId, boolean isIncoming); + + void setMessageArrivalTimestamp(int messageId, boolean isIncoming, long arrivalTimestamp); + + long getMessageArrivalTimestamp(int messageId, boolean isIncoming); + + void setMessageSeenTimestamp(int messageId, boolean isIncoming, long seenTimestamp); + + long getMessageSeenTimestamp(int messageId, boolean isIncoming); + + void setTextForMessage(int messageId, boolean isIncoming, String text); + + String getTextForMessage(int messageId, boolean isIncoming); + + void setLatitudeForMessage(int messageId, boolean isIncoming, double latitude); + + double getLatitudeForMessage(int messageId, boolean isIncoming); + + void setLongitudeForMessage(int messageId, boolean isIncoming, double longitude); + + double getLongitudeForMessage(int messageId, boolean isIncoming); + + // Returns the ID's of the file transfers attached to the given message + int[] getFileTransfersAttachedToMessage(int messageId, boolean isIncoming); + + int getSenderParticipant(int messageId); + + ///////////////////////// + // RcsOutgoingMessageDelivery APIs + ///////////////////////// + + // Returns the participant ID's that this message is intended to be delivered to + int[] getMessageRecipients(int messageId); + + long getOutgoingDeliveryDeliveredTimestamp(int messageId, int participantId); + + void setOutgoingDeliveryDeliveredTimestamp(int messageId, int participantId, long deliveredTimestamp); + + long getOutgoingDeliverySeenTimestamp(int messageId, int participantId); + + void setOutgoingDeliverySeenTimestamp(int messageId, int participantId, long seenTimestamp); + + int getOutgoingDeliveryStatus(int messageId, int participantId); + + void setOutgoingDeliveryStatus(int messageId, int participantId, int status); + + ///////////////////////// + // RcsFileTransferPart APIs + ///////////////////////// + + // Performs the initial write to storage and returns the row ID. + int storeFileTransfer(int messageId, boolean isIncoming, + in RcsFileTransferCreationParams fileTransferCreationParams); + + void deleteFileTransfer(int partId); + + void setFileTransferSessionId(int partId, String sessionId); + + String getFileTransferSessionId(int partId); + + void setFileTransferContentUri(int partId, in Uri contentUri); + + Uri getFileTransferContentUri(int partId); + + void setFileTransferContentType(int partId, String contentType); + + String getFileTransferContentType(int partId); + + void setFileTransferFileSize(int partId, long fileSize); + + long getFileTransferFileSize(int partId); + + void setFileTransferTransferOffset(int partId, long transferOffset); + + long getFileTransferTransferOffset(int partId); + + void setFileTransferStatus(int partId, int transferStatus); + + int getFileTransferStatus(int partId); + + void setFileTransferWidth(int partId, int width); + + int getFileTransferWidth(int partId); + + void setFileTransferHeight(int partId, int height); + + int getFileTransferHeight(int partId); + + void setFileTransferLength(int partId, long length); + + long getFileTransferLength(int partId); + + void setFileTransferPreviewUri(int partId, in Uri uri); + + Uri getFileTransferPreviewUri(int partId); + + void setFileTransferPreviewType(int partId, String type); + + String getFileTransferPreviewType(int partId); + + ///////////////////////// + // RcsEvent APIs + ///////////////////////// + int createGroupThreadNameChangedEvent(long timestamp, int threadId, int originationParticipantId, String newName); + + int createGroupThreadIconChangedEvent(long timestamp, int threadId, int originationParticipantId, in Uri newIcon); + + int createGroupThreadParticipantJoinedEvent(long timestamp, int threadId, int originationParticipantId, int participantId); - void updateRcsParticipantCanonicalAddress(int id, String canonicalAddress); + int createGroupThreadParticipantLeftEvent(long timestamp, int threadId, int originationParticipantId, int participantId); - void updateRcsParticipantAlias(int id, String alias); + int createParticipantAliasChangedEvent(long timestamp, int participantId, String newAlias); }
\ No newline at end of file diff --git a/telephony/java/com/android/ims/RcsTypeIdPair.java b/telephony/java/com/android/ims/RcsTypeIdPair.java new file mode 100644 index 000000000000..a5177354002e --- /dev/null +++ b/telephony/java/com/android/ims/RcsTypeIdPair.java @@ -0,0 +1,79 @@ +/* + * 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.ims; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A utility class to pass RCS IDs and types in RPC calls + * + * @hide + */ +public class RcsTypeIdPair implements Parcelable { + private int mType; + private int mId; + + public RcsTypeIdPair(int type, int id) { + mType = type; + mId = id; + } + + public int getType() { + return mType; + } + + public void setType(int type) { + mType = type; + } + + public int getId() { + return mId; + } + + public void setId(int id) { + mId = id; + } + + public RcsTypeIdPair(Parcel in) { + mType = in.readInt(); + mId = in.readInt(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mType); + dest.writeInt(mId); + } + + public static final Creator<RcsTypeIdPair> CREATOR = + new Creator<RcsTypeIdPair>() { + @Override + public RcsTypeIdPair createFromParcel(Parcel in) { + return new RcsTypeIdPair(in); + } + + @Override + public RcsTypeIdPair[] newArray(int size) { + return new RcsTypeIdPair[size]; + } + }; +} diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl index 04ec3d1a3df3..a49d2d976d16 100755 --- a/telephony/java/com/android/internal/telephony/ISub.aidl +++ b/telephony/java/com/android/internal/telephony/ISub.aidl @@ -276,6 +276,11 @@ interface ISub { String getSubscriptionProperty(int subId, String propKey, String callingPackage); + boolean setSubscriptionEnabled(boolean enable, int subId); + + boolean isSubscriptionEnabled(int subId); + + int getEnabledSubscriptionId(int slotIndex); /** * Get the SIM state for the slot index * @return SIM state as the ordinal of IccCardConstants.State diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index cf3bb615bdd3..e6a55585506c 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -1830,7 +1830,7 @@ interface ITelephony { * @hide */ boolean isMultisimCarrierRestricted(); - + /** * Switch configs to enable multi-sim or switch back to single-sim * @hide @@ -1841,8 +1841,15 @@ interface ITelephony { * @hide */ int getNumOfActiveSims(); + /** * Get if reboot is required upon altering modems configurations + * @hide */ boolean isRebootRequiredForModemConfigChange(); + + /** + * Get the mapping from logical slots to physical slots. + */ + int[] getSlotsMapping(); } diff --git a/telephony/java/com/android/internal/telephony/cdma/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/BearerData.java index a4cd56b9e3e2..694cc69c2b3f 100644 --- a/telephony/java/com/android/internal/telephony/cdma/BearerData.java +++ b/telephony/java/com/android/internal/telephony/cdma/BearerData.java @@ -596,6 +596,45 @@ public final class BearerData { System.arraycopy(payload, 0, uData.payload, udhBytes, payload.length); } + private static void encode7bitAsciiEms(UserData uData, byte[] udhData, boolean force) + throws CodingException + { + try { + Rlog.d(LOG_TAG, "encode7bitAsciiEms"); + int udhBytes = udhData.length + 1; // Add length octet. + int udhSeptets = ((udhBytes * 8) + 6) / 7; + int paddingBits = (udhSeptets * 7) - (udhBytes * 8); + String msg = uData.payloadStr; + byte[] payload ; + int msgLen = msg.length(); + BitwiseOutputStream outStream = new BitwiseOutputStream(msgLen + + (paddingBits > 0 ? 1 : 0)); + outStream.write(paddingBits, 0); + for (int i = 0; i < msgLen; i++) { + int charCode = UserData.charToAscii.get(msg.charAt(i), -1); + if (charCode == -1) { + if (force) { + outStream.write(7, UserData.UNENCODABLE_7_BIT_CHAR); + } else { + throw new CodingException("cannot ASCII encode (" + msg.charAt(i) + ")"); + } + } else { + outStream.write(7, charCode); + } + } + payload = outStream.toByteArray(); + uData.msgEncoding = UserData.ENCODING_7BIT_ASCII; + uData.msgEncodingSet = true; + uData.numFields = udhSeptets + uData.payloadStr.length(); + uData.payload = new byte[udhBytes + payload.length]; + uData.payload[0] = (byte)udhData.length; + System.arraycopy(udhData, 0, uData.payload, 1, udhData.length); + System.arraycopy(payload, 0, uData.payload, udhBytes, payload.length); + } catch (BitwiseOutputStream.AccessException ex) { + throw new CodingException("7bit ASCII encode failed: " + ex); + } + } + private static void encodeEmsUserDataPayload(UserData uData) throws CodingException { @@ -605,6 +644,8 @@ public final class BearerData { encode7bitEms(uData, headerData, true); } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) { encode16bitEms(uData, headerData); + } else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) { + encode7bitAsciiEms(uData, headerData, true); } else { throw new CodingException("unsupported EMS user data encoding (" + uData.msgEncoding + ")"); @@ -1056,15 +1097,18 @@ public final class BearerData { throws CodingException { try { - offset *= 8; + int offsetBits = offset * 8; + int offsetSeptets = (offsetBits + 6) / 7; + numFields -= offsetSeptets; + StringBuffer strBuf = new StringBuffer(numFields); BitwiseInputStream inStream = new BitwiseInputStream(data); - int wantedBits = (offset * 8) + (numFields * 7); + int wantedBits = (offsetSeptets * 7) + (numFields * 7); if (inStream.available() < wantedBits) { throw new CodingException("insufficient data (wanted " + wantedBits + " bits, but only have " + inStream.available() + ")"); } - inStream.skip(offset); + inStream.skip(offsetSeptets * 7); for (int i = 0; i < numFields; i++) { int charCode = inStream.read(7); if ((charCode >= UserData.ASCII_MAP_BASE_INDEX) && diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java index 964a31304db5..1da5eac27002 100644 --- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java @@ -21,7 +21,6 @@ import android.os.SystemProperties; import android.telephony.PhoneNumberUtils; import android.telephony.SmsCbLocation; import android.telephony.SmsCbMessage; -import android.telephony.TelephonyManager; import android.telephony.cdma.CdmaSmsCbProgramData; import android.telephony.Rlog; import android.util.Log; @@ -746,8 +745,10 @@ public class SmsMessage extends SmsMessageBase { /** * Parses a broadcast SMS, possibly containing a CMAS alert. + * + * @param plmn the PLMN for a broadcast SMS */ - public SmsCbMessage parseBroadcastSms() { + public SmsCbMessage parseBroadcastSms(String plmn) { BearerData bData = BearerData.decode(mEnvelope.bearerData, mEnvelope.serviceCategory); if (bData == null) { Rlog.w(LOG_TAG, "BearerData.decode() returned null"); @@ -758,7 +759,6 @@ public class SmsMessage extends SmsMessageBase { Rlog.d(LOG_TAG, "MT raw BearerData = " + HexDump.toHexString(mEnvelope.bearerData)); } - String plmn = TelephonyManager.getDefault().getNetworkOperator(); SmsCbLocation location = new SmsCbLocation(plmn); return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP2, @@ -858,14 +858,15 @@ public class SmsMessage extends SmsMessageBase { bearerData.userData = userData; byte[] encodedBearerData = BearerData.encode(bearerData); + if (encodedBearerData == null) return null; if (Rlog.isLoggable(LOGGABLE_TAG, Log.VERBOSE)) { Rlog.d(LOG_TAG, "MO (encoded) BearerData = " + bearerData); Rlog.d(LOG_TAG, "MO raw BearerData = '" + HexDump.toHexString(encodedBearerData) + "'"); } - if (encodedBearerData == null) return null; - int teleservice = bearerData.hasUserDataHeader ? - SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT; + int teleservice = (bearerData.hasUserDataHeader + && userData.msgEncoding != UserData.ENCODING_7BIT_ASCII) + ? SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT; SmsEnvelope envelope = new SmsEnvelope(); envelope.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT; diff --git a/test-base/api/TEST_MAPPING b/test-base/api/TEST_MAPPING new file mode 100644 index 000000000000..3535954bc019 --- /dev/null +++ b/test-base/api/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "CtsAndroidTestBase27ApiSignatureTestCases" + } + ] +} diff --git a/test-mock/api/TEST_MAPPING b/test-mock/api/TEST_MAPPING new file mode 100644 index 000000000000..d1bd9afaf287 --- /dev/null +++ b/test-mock/api/TEST_MAPPING @@ -0,0 +1,10 @@ +{ + "presubmit": [ + { + "name": "CtsAndroidTestMockCurrentApiSignatureTestCases" + }, + { + "name": "CtsCurrentApiSignatureTestCases" + } + ] +} diff --git a/test-runner/Android.mk b/test-runner/Android.mk deleted file mode 100644 index 18bde8517351..000000000000 --- a/test-runner/Android.mk +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright (C) 2008 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. -# - -LOCAL_PATH:= $(call my-dir) - -# additionally, build unit tests in a separate .apk -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/test-runner/api/TEST_MAPPING b/test-runner/api/TEST_MAPPING new file mode 100644 index 000000000000..76ade3c1e647 --- /dev/null +++ b/test-runner/api/TEST_MAPPING @@ -0,0 +1,10 @@ +{ + "presubmit": [ + { + "name": "CtsAndroidTestRunnerCurrentApiSignatureTestCases" + }, + { + "name": "CtsCurrentApiSignatureTestCases" + } + ] +} diff --git a/test-runner/tests/Android.bp b/test-runner/tests/Android.bp new file mode 100644 index 000000000000..03c73986118d --- /dev/null +++ b/test-runner/tests/Android.bp @@ -0,0 +1,40 @@ +// Copyright 2010, 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. + +android_test { + name: "FrameworkTestRunnerTests", + + // We only want this apk build for tests. + // + // Run the tests using the following commands: + // adb install -r ${ANDROID_PRODUCT_OUT}/data/app/FrameworkTestRunnerTests/FrameworkTestRunnerTests.apk + // adb shell am instrument \ + // -e notAnnotation android.test.suitebuilder.examples.error.RunAsPartOfSeparateTest \ + // -w com.android.frameworks.testrunner.tests/android.test.InstrumentationTestRunner \ + // + + libs: [ + "android.test.runner", + "android.test.base", + "android.test.mock", + ], + static_libs: ["junit"], + + // Include all test java files. + srcs: ["src/**/*.java"], + + // Because of android.test.mock. + platform_apis: true, + +} diff --git a/test-runner/tests/Android.mk b/test-runner/tests/Android.mk deleted file mode 100644 index f97d1c986b1c..000000000000 --- a/test-runner/tests/Android.mk +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright 2010, 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. - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -# We only want this apk build for tests. -# -# Run the tests using the following commands: -# adb install -r ${ANDROID_PRODUCT_OUT}/data/app/FrameworkTestRunnerTests/FrameworkTestRunnerTests.apk -# adb shell am instrument \ - -e notAnnotation android.test.suitebuilder.examples.error.RunAsPartOfSeparateTest \ - -w com.android.frameworks.testrunner.tests/android.test.InstrumentationTestRunner -# -LOCAL_MODULE_TAGS := tests - -LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock -LOCAL_STATIC_JAVA_LIBRARIES := junit - -# Include all test java files. -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PACKAGE_NAME := FrameworkTestRunnerTests -# Because of android.test.mock. -LOCAL_PRIVATE_PLATFORM_APIS := true - -include $(BUILD_PACKAGE) - diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java new file mode 100644 index 000000000000..915a260f5c79 --- /dev/null +++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.tests.ims; + +import static com.google.common.truth.Truth.assertThat; + +import android.net.Uri; +import android.os.Parcel; +import android.support.test.runner.AndroidJUnit4; +import android.telephony.ims.RcsGroupThread; +import android.telephony.ims.RcsGroupThreadIconChangedEvent; +import android.telephony.ims.RcsParticipant; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class RcsGroupThreadIconChangedEventTest { + + @Test + public void testCanUnparcel() { + RcsGroupThread rcsGroupThread = new RcsGroupThread(1); + RcsParticipant rcsParticipant = new RcsParticipant(2); + Uri newIconUri = Uri.parse("content://new_icon"); + + RcsGroupThreadIconChangedEvent iconChangedEvent = + new RcsGroupThreadIconChangedEvent(1234567890, rcsGroupThread, rcsParticipant, + newIconUri); + + Parcel parcel = Parcel.obtain(); + iconChangedEvent.writeToParcel(parcel, iconChangedEvent.describeContents()); + + parcel.setDataPosition(0); + + iconChangedEvent = RcsGroupThreadIconChangedEvent.CREATOR.createFromParcel(parcel); + + assertThat(iconChangedEvent.getNewIcon()).isEqualTo(newIconUri); + assertThat(iconChangedEvent.getRcsGroupThread().getThreadId()).isEqualTo(1); + assertThat(iconChangedEvent.getOriginatingParticipant().getId()).isEqualTo(2); + assertThat(iconChangedEvent.getTimestamp()).isEqualTo(1234567890); + } +} diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java new file mode 100644 index 000000000000..1384c016daa8 --- /dev/null +++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.tests.ims; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Parcel; +import android.support.test.runner.AndroidJUnit4; +import android.telephony.ims.RcsGroupThread; +import android.telephony.ims.RcsGroupThreadNameChangedEvent; +import android.telephony.ims.RcsParticipant; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class RcsGroupThreadNameChangedEventTest { + @Test + public void testCanUnparcel() { + String newName = "new name"; + + RcsGroupThread rcsGroupThread = new RcsGroupThread(1); + RcsParticipant rcsParticipant = new RcsParticipant(2); + + RcsGroupThreadNameChangedEvent nameChangedEvent = + new RcsGroupThreadNameChangedEvent(1234567890, rcsGroupThread, rcsParticipant, + newName); + + Parcel parcel = Parcel.obtain(); + nameChangedEvent.writeToParcel(parcel, nameChangedEvent.describeContents()); + + parcel.setDataPosition(0); + + nameChangedEvent = RcsGroupThreadNameChangedEvent.CREATOR.createFromParcel( + parcel); + + assertThat(nameChangedEvent.getNewName()).isEqualTo(newName); + assertThat(nameChangedEvent.getRcsGroupThread().getThreadId()).isEqualTo(1); + assertThat(nameChangedEvent.getOriginatingParticipant().getId()).isEqualTo(2); + assertThat(nameChangedEvent.getTimestamp()).isEqualTo(1234567890); + } +} diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java new file mode 100644 index 000000000000..d0af7db90627 --- /dev/null +++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java @@ -0,0 +1,54 @@ +/* + * 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.tests.ims; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Parcel; +import android.support.test.runner.AndroidJUnit4; +import android.telephony.ims.RcsGroupThread; +import android.telephony.ims.RcsGroupThreadParticipantJoinedEvent; +import android.telephony.ims.RcsParticipant; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class RcsGroupThreadParticipantJoinedEventTest { + + @Test + public void testCanUnparcel() { + RcsGroupThread rcsGroupThread = new RcsGroupThread(1); + RcsParticipant rcsParticipant = new RcsParticipant(2); + + RcsGroupThreadParticipantJoinedEvent participantJoinedEvent = + new RcsGroupThreadParticipantJoinedEvent(1234567890, rcsGroupThread, rcsParticipant, + rcsParticipant); + + Parcel parcel = Parcel.obtain(); + participantJoinedEvent.writeToParcel(parcel, participantJoinedEvent.describeContents()); + + parcel.setDataPosition(0); + + participantJoinedEvent = RcsGroupThreadParticipantJoinedEvent.CREATOR.createFromParcel( + parcel); + + assertThat(participantJoinedEvent.getJoinedParticipant().getId()).isEqualTo(2); + assertThat(participantJoinedEvent.getRcsGroupThread().getThreadId()).isEqualTo(1); + assertThat(participantJoinedEvent.getOriginatingParticipant().getId()).isEqualTo(2); + assertThat(participantJoinedEvent.getTimestamp()).isEqualTo(1234567890); + } +} diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java new file mode 100644 index 000000000000..7ba5fa653258 --- /dev/null +++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java @@ -0,0 +1,53 @@ +/* + * 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.tests.ims; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Parcel; +import android.support.test.runner.AndroidJUnit4; +import android.telephony.ims.RcsGroupThread; +import android.telephony.ims.RcsGroupThreadParticipantLeftEvent; +import android.telephony.ims.RcsParticipant; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class RcsGroupThreadParticipantLeftEventTest { + @Test + public void testCanUnparcel() { + RcsGroupThread rcsGroupThread = new RcsGroupThread(1); + RcsParticipant rcsParticipant = new RcsParticipant(2); + + RcsGroupThreadParticipantLeftEvent participantLeftEvent = + new RcsGroupThreadParticipantLeftEvent(1234567890, rcsGroupThread, rcsParticipant, + rcsParticipant); + + Parcel parcel = Parcel.obtain(); + participantLeftEvent.writeToParcel(parcel, participantLeftEvent.describeContents()); + + parcel.setDataPosition(0); + + // create from parcel + parcel.setDataPosition(0); + participantLeftEvent = RcsGroupThreadParticipantLeftEvent.CREATOR.createFromParcel( + parcel); + assertThat(participantLeftEvent.getRcsGroupThread().getThreadId()).isEqualTo(1); + assertThat(participantLeftEvent.getLeavingParticipantId().getId()).isEqualTo(2); + assertThat(participantLeftEvent.getTimestamp()).isEqualTo(1234567890); + } +} diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java deleted file mode 100644 index 44277edcdb8c..000000000000 --- a/tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java +++ /dev/null @@ -1,32 +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 com.android.tests.ims; - -import android.support.test.runner.AndroidJUnit4; -import android.telephony.ims.RcsMessageStore; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -public class RcsMessageStoreTest { - //TODO(sahinc): Add meaningful tests once we have more of the implementation in place - @Test - public void testDeleteThreadDoesntCrash() { - RcsMessageStore mRcsMessageStore = new RcsMessageStore(); - mRcsMessageStore.deleteThread(0); - } -} diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java new file mode 100644 index 000000000000..3e2bbbf8256c --- /dev/null +++ b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.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.tests.ims; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Parcel; +import android.support.test.runner.AndroidJUnit4; +import android.telephony.ims.RcsParticipant; +import android.telephony.ims.RcsParticipantAliasChangedEvent; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class RcsParticipantAliasChangedEventTest { + private static final String OLD_ALIAS = "old alias"; + private static final String NEW_ALIAS = "new alias"; + private RcsParticipant mParticipant; + + @Before + public void setUp() { + mParticipant = new RcsParticipant(3); + } + + @Test + public void testCanUnparcel() { + RcsParticipantAliasChangedEvent aliasChangedEvent = + new RcsParticipantAliasChangedEvent(1234567890, mParticipant, NEW_ALIAS); + + Parcel parcel = Parcel.obtain(); + aliasChangedEvent.writeToParcel(parcel, aliasChangedEvent.describeContents()); + + parcel.setDataPosition(0); + + aliasChangedEvent = RcsParticipantAliasChangedEvent.CREATOR.createFromParcel( + parcel); + + assertThat(aliasChangedEvent.getParticipantId().getId()).isEqualTo(3); + assertThat(aliasChangedEvent.getNewAlias()).isEqualTo(NEW_ALIAS); + assertThat(aliasChangedEvent.getTimestamp()).isEqualTo(1234567890); + } +} diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParamsTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParamsTest.java new file mode 100644 index 000000000000..6361a393187e --- /dev/null +++ b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParamsTest.java @@ -0,0 +1,57 @@ +/* + * 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 com.android.tests.ims; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Parcel; +import android.support.test.runner.AndroidJUnit4; +import android.telephony.ims.RcsParticipantQueryParams; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class RcsParticipantQueryParamsTest { + + @Test + public void testCanUnparcel() { + RcsParticipantQueryParams rcsParticipantQueryParams = + new RcsParticipantQueryParams.Builder() + .setAliasLike("%alias_") + .setCanonicalAddressLike("_canonical%") + .setSortProperty(RcsParticipantQueryParams.SORT_BY_CANONICAL_ADDRESS) + .setSortDirection(true) + .setResultLimit(432) + .build(); + + + Parcel parcel = Parcel.obtain(); + rcsParticipantQueryParams.writeToParcel(parcel, + rcsParticipantQueryParams.describeContents()); + + parcel.setDataPosition(0); + rcsParticipantQueryParams = RcsParticipantQueryParams.CREATOR.createFromParcel( + parcel); + + assertThat(rcsParticipantQueryParams.getAliasLike()).isEqualTo("%alias_"); + assertThat(rcsParticipantQueryParams.getCanonicalAddressLike()).contains("_canonical%"); + assertThat(rcsParticipantQueryParams.getLimit()).isEqualTo(432); + assertThat(rcsParticipantQueryParams.getSortingProperty()).isEqualTo( + RcsParticipantQueryParams.SORT_BY_CANONICAL_ADDRESS); + assertThat(rcsParticipantQueryParams.getSortDirection()).isTrue(); + } +} diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java deleted file mode 100644 index c402dbffc84b..000000000000 --- a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java +++ /dev/null @@ -1,46 +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 com.android.tests.ims; - -import static com.google.common.truth.Truth.assertThat; - -import android.os.Bundle; -import android.support.test.runner.AndroidJUnit4; -import android.telephony.ims.RcsParticipant; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -public class RcsParticipantTest { - private static final int ID = 123; - private static final String ALIAS = "alias"; - private static final String CANONICAL_ADDRESS = "+1234567890"; - - @Test - public void testCanUnparcel() { - RcsParticipant rcsParticipant = new RcsParticipant(ID, CANONICAL_ADDRESS); - rcsParticipant.setAlias(ALIAS); - - Bundle bundle = new Bundle(); - bundle.putParcelable("Some key", rcsParticipant); - rcsParticipant = bundle.getParcelable("Some key"); - - assertThat(rcsParticipant.getId()).isEqualTo(ID); - assertThat(rcsParticipant.getAlias()).isEqualTo(ALIAS); - assertThat(rcsParticipant.getCanonicalAddress()).isEqualTo(CANONICAL_ADDRESS); - } -} diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java deleted file mode 100644 index a890a389bdfc..000000000000 --- a/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java +++ /dev/null @@ -1,53 +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 com.android.tests.ims; - -import static com.google.common.truth.Truth.assertThat; - -import android.os.Bundle; -import android.support.test.runner.AndroidJUnit4; -import android.telephony.ims.RcsParticipant; -import android.telephony.ims.RcsThreadQueryParameters; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; - -@RunWith(AndroidJUnit4.class) -public class RcsThreadQueryParametersTest { - private RcsThreadQueryParameters mRcsThreadQueryParameters; - @Mock RcsParticipant mMockParticipant; - - @Test - public void testUnparceling() { - String key = "some key"; - mRcsThreadQueryParameters = RcsThreadQueryParameters.builder() - .isGroupThread(true) - .withParticipant(mMockParticipant) - .limitResultsTo(50) - .sort(true) - .build(); - - Bundle bundle = new Bundle(); - bundle.putParcelable(key, mRcsThreadQueryParameters); - mRcsThreadQueryParameters = bundle.getParcelable(key); - - assertThat(mRcsThreadQueryParameters.isGroupThread()).isTrue(); - assertThat(mRcsThreadQueryParameters.getRcsParticipants()).contains(mMockParticipant); - assertThat(mRcsThreadQueryParameters.getLimit()).isEqualTo(50); - assertThat(mRcsThreadQueryParameters.isAscending()).isTrue(); - } -} diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParamsTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParamsTest.java new file mode 100644 index 000000000000..beb4f8ad28e2 --- /dev/null +++ b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParamsTest.java @@ -0,0 +1,58 @@ +/* + * 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 com.android.tests.ims; + +import static android.telephony.ims.RcsThreadQueryParams.SORT_BY_TIMESTAMP; +import static android.telephony.ims.RcsThreadQueryParams.THREAD_TYPE_GROUP; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Parcel; +import android.support.test.runner.AndroidJUnit4; +import android.telephony.ims.RcsParticipant; +import android.telephony.ims.RcsThreadQueryParams; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class RcsThreadQueryParamsTest { + + @Test + public void testCanUnparcel() { + RcsParticipant rcsParticipant = new RcsParticipant(1); + RcsThreadQueryParams rcsThreadQueryParams = new RcsThreadQueryParams.Builder() + .setThreadType(THREAD_TYPE_GROUP) + .setParticipant(rcsParticipant) + .setResultLimit(50) + .setSortProperty(SORT_BY_TIMESTAMP) + .setSortDirection(true) + .build(); + + Parcel parcel = Parcel.obtain(); + rcsThreadQueryParams.writeToParcel(parcel, rcsThreadQueryParams.describeContents()); + + parcel.setDataPosition(0); + rcsThreadQueryParams = RcsThreadQueryParams.CREATOR.createFromParcel(parcel); + + assertThat(rcsThreadQueryParams.getThreadType()).isEqualTo(THREAD_TYPE_GROUP); + assertThat(rcsThreadQueryParams.getRcsParticipantsIds()) + .contains(rcsParticipant.getId()); + assertThat(rcsThreadQueryParams.getLimit()).isEqualTo(50); + assertThat(rcsThreadQueryParams.getSortingProperty()).isEqualTo(SORT_BY_TIMESTAMP); + assertThat(rcsThreadQueryParams.getSortDirection()).isTrue(); + } +} diff --git a/tests/net/java/android/net/LinkPropertiesTest.java b/tests/net/java/android/net/LinkPropertiesTest.java index 299fbefc78e4..bdde0961909d 100644 --- a/tests/net/java/android/net/LinkPropertiesTest.java +++ b/tests/net/java/android/net/LinkPropertiesTest.java @@ -22,18 +22,15 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; import android.net.LinkProperties.CompareResult; import android.net.LinkProperties.ProvisioningChange; -import android.net.RouteInfo; -import android.os.Parcel; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.system.OsConstants; import android.util.ArraySet; +import com.android.internal.util.TestUtils; + import org.junit.Test; import org.junit.runner.RunWith; @@ -849,18 +846,6 @@ public class LinkPropertiesTest { assertEquals(new ArraySet<>(expectRemoved), (new ArraySet<>(result.removed))); } - private void assertParcelingIsLossless(LinkProperties source) { - Parcel p = Parcel.obtain(); - source.writeToParcel(p, /* flags */ 0); - p.setDataPosition(0); - final byte[] marshalled = p.marshall(); - p = Parcel.obtain(); - p.unmarshall(marshalled, 0, marshalled.length); - p.setDataPosition(0); - LinkProperties dest = LinkProperties.CREATOR.createFromParcel(p); - assertEquals(source, dest); - } - @Test public void testLinkPropertiesParcelable() throws Exception { LinkProperties source = new LinkProperties(); @@ -882,12 +867,12 @@ public class LinkPropertiesTest { source.setNat64Prefix(new IpPrefix("2001:db8:1:2:64:64::/96")); - assertParcelingIsLossless(source); + TestUtils.assertParcelingIsLossless(source, LinkProperties.CREATOR); } @Test public void testParcelUninitialized() throws Exception { LinkProperties empty = new LinkProperties(); - assertParcelingIsLossless(empty); + TestUtils.assertParcelingIsLossless(empty, LinkProperties.CREATOR); } } diff --git a/tests/net/java/android/net/TcpKeepalivePacketDataTest.java b/tests/net/java/android/net/TcpKeepalivePacketDataTest.java new file mode 100644 index 000000000000..1f2dd275bb7b --- /dev/null +++ b/tests/net/java/android/net/TcpKeepalivePacketDataTest.java @@ -0,0 +1,126 @@ +/* + * 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.net; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import android.net.SocketKeepalive.InvalidPacketException; +import android.net.TcpKeepalivePacketData.TcpSocketInfo; + +import com.android.internal.util.TestUtils; + +import libcore.net.InetAddressUtils; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.net.InetAddress; +import java.nio.ByteBuffer; + +@RunWith(JUnit4.class) +public final class TcpKeepalivePacketDataTest { + + @Before + public void setUp() {} + + @Test + public void testV4TcpKeepalivePacket() { + final InetAddress srcAddr = InetAddressUtils.parseNumericAddress("192.168.0.1"); + final InetAddress dstAddr = InetAddressUtils.parseNumericAddress("192.168.0.10"); + final int srcPort = 1234; + final int dstPort = 4321; + final int seq = 0x11111111; + final int ack = 0x22222222; + final int wnd = 8000; + final int wndScale = 2; + TcpKeepalivePacketData resultData = null; + TcpSocketInfo testInfo = new TcpSocketInfo( + srcAddr, srcPort, dstAddr, dstPort, seq, ack, wnd, wndScale); + try { + resultData = TcpKeepalivePacketData.tcpKeepalivePacket(testInfo); + } catch (InvalidPacketException e) { + fail("InvalidPacketException: " + e); + } + + assertEquals(testInfo.srcAddress, resultData.srcAddress); + assertEquals(testInfo.dstAddress, resultData.dstAddress); + assertEquals(testInfo.srcPort, resultData.srcPort); + assertEquals(testInfo.dstPort, resultData.dstPort); + assertEquals(testInfo.seq, resultData.tcpSeq); + assertEquals(testInfo.ack, resultData.tcpAck); + assertEquals(testInfo.rcvWndScale, resultData.tcpWndScale); + + TestUtils.assertParcelingIsLossless(resultData, TcpKeepalivePacketData.CREATOR); + + final byte[] packet = resultData.getPacket(); + // IP version and TOS. + ByteBuffer buf = ByteBuffer.wrap(packet); + assertEquals(buf.getShort(), 0x4500); + // Source IP address. + byte[] ip = new byte[4]; + buf = ByteBuffer.wrap(packet, 12, 4); + buf.get(ip); + assertArrayEquals(ip, srcAddr.getAddress()); + // Destination IP address. + buf = ByteBuffer.wrap(packet, 16, 4); + buf.get(ip); + assertArrayEquals(ip, dstAddr.getAddress()); + + buf = ByteBuffer.wrap(packet, 20, 12); + // Source port. + assertEquals(buf.getShort(), srcPort); + // Destination port. + assertEquals(buf.getShort(), dstPort); + // Sequence number. + assertEquals(buf.getInt(), seq); + // Ack. + assertEquals(buf.getInt(), ack); + // Window size. + buf = ByteBuffer.wrap(packet, 34, 2); + assertEquals(buf.getShort(), wnd >> wndScale); + } + + //TODO: add ipv6 test when ipv6 supported + + @Test + public void testParcel() throws Exception { + final InetAddress srcAddr = InetAddresses.parseNumericAddress("192.168.0.1"); + final InetAddress dstAddr = InetAddresses.parseNumericAddress("192.168.0.10"); + final int srcPort = 1234; + final int dstPort = 4321; + final int sequence = 0x11111111; + final int ack = 0x22222222; + final int wnd = 48_000; + final int wndScale = 2; + TcpKeepalivePacketData testData = null; + TcpKeepalivePacketDataParcelable resultData = null; + TcpSocketInfo testInfo = new TcpSocketInfo( + srcAddr, srcPort, dstAddr, dstPort, sequence, ack, wnd, wndScale); + testData = TcpKeepalivePacketData.tcpKeepalivePacket(testInfo); + resultData = testData.toStableParcelable(); + assertArrayEquals(resultData.srcAddress, srcAddr.getAddress()); + assertArrayEquals(resultData.dstAddress, dstAddr.getAddress()); + assertEquals(resultData.srcPort, srcPort); + assertEquals(resultData.dstPort, dstPort); + assertEquals(resultData.seq, sequence); + assertEquals(resultData.ack, ack); + } +} diff --git a/tests/net/java/com/android/internal/util/TestUtils.java b/tests/net/java/com/android/internal/util/TestUtils.java index 6db01d343756..7e5a1d3ad4d3 100644 --- a/tests/net/java/com/android/internal/util/TestUtils.java +++ b/tests/net/java/com/android/internal/util/TestUtils.java @@ -16,12 +16,15 @@ package com.android.internal.util; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import android.os.ConditionVariable; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; +import android.os.Parcel; +import android.os.Parcelable; public final class TestUtils { private TestUtils() { } @@ -50,4 +53,18 @@ public final class TestUtils { fail(handler.toString() + " did not become idle after " + timeoutMs + " ms"); } } + + // TODO : fetch the creator through reflection or something instead of passing it + public static <T extends Parcelable, C extends Parcelable.Creator<T>> + void assertParcelingIsLossless(T source, C creator) { + Parcel p = Parcel.obtain(); + source.writeToParcel(p, /* flags */ 0); + p.setDataPosition(0); + final byte[] marshalled = p.marshall(); + p = Parcel.obtain(); + p.unmarshall(marshalled, 0, marshalled.length); + p.setDataPosition(0); + T dest = creator.createFromParcel(p); + assertEquals(source, dest); + } } diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 1548a76cc443..963b6851efea 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -554,7 +554,7 @@ public class ConnectivityServiceTest { if (mNmValidationRedirectUrl != null) { mNmCallbacks.showProvisioningNotification( - "test_provisioning_notif_action"); + "test_provisioning_notif_action", "com.android.test.package"); mNmProvNotificationRequested = true; } } catch (RemoteException e) { diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java index b6356076db60..a4a735d1a89d 100644 --- a/tests/net/java/com/android/server/connectivity/TetheringTest.java +++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java @@ -35,6 +35,7 @@ import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY; import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER; +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -274,6 +275,11 @@ public class TetheringTest { isTetheringSupportedCalls++; return true; } + + @Override + public int getDefaultDataSubscriptionId() { + return INVALID_SUBSCRIPTION_ID; + } } private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6, diff --git a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java index ec286759354a..193f3806dbf6 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java @@ -21,6 +21,7 @@ import static android.net.ConnectivityManager.TETHERING_WIFI; import static android.net.ConnectivityManager.TETHER_ERROR_ENTITLEMENT_UNKONWN; import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED; +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -140,7 +141,8 @@ public final class EntitlementManagerTest { mMockContext = new MockContext(mContext); mSM = new TestStateMachine(); mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, mSystemProperties); - mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog)); + mEnMgr.updateConfiguration( + new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID)); } @After @@ -168,7 +170,8 @@ public final class EntitlementManagerTest { @Test public void canRequireProvisioning() { setupForRequiredProvisioning(); - mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog)); + mEnMgr.updateConfiguration( + new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID)); assertTrue(mEnMgr.isTetherProvisioningRequired()); } @@ -177,7 +180,8 @@ public final class EntitlementManagerTest { setupForRequiredProvisioning(); when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) .thenReturn(null); - mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog)); + mEnMgr.updateConfiguration( + new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID)); // Couldn't get the CarrierConfigManager, but still had a declared provisioning app. // Therefore provisioning still be required. assertTrue(mEnMgr.isTetherProvisioningRequired()); @@ -187,7 +191,8 @@ public final class EntitlementManagerTest { public void toleratesCarrierConfigMissing() { setupForRequiredProvisioning(); when(mCarrierConfigManager.getConfig()).thenReturn(null); - mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog)); + mEnMgr.updateConfiguration( + new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID)); // We still have a provisioning app configured, so still require provisioning. assertTrue(mEnMgr.isTetherProvisioningRequired()); } @@ -197,11 +202,13 @@ public final class EntitlementManagerTest { setupForRequiredProvisioning(); when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) .thenReturn(null); - mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog)); + mEnMgr.updateConfiguration( + new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID)); assertFalse(mEnMgr.isTetherProvisioningRequired()); when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) .thenReturn(new String[] {"malformedApp"}); - mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog)); + mEnMgr.updateConfiguration( + new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID)); assertFalse(mEnMgr.isTetherProvisioningRequired()); } @@ -223,7 +230,8 @@ public final class EntitlementManagerTest { assertFalse(mEnMgr.everRunUiEntitlement); setupForRequiredProvisioning(); - mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog)); + mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog, + INVALID_SUBSCRIPTION_ID)); // 2. No cache value and don't need to run entitlement check. mEnMgr.everRunUiEntitlement = false; receiver = new ResultReceiver(null) { diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java index 521778484d91..01b904d8f088 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java @@ -22,10 +22,12 @@ import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER; +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static com.android.server.connectivity.tethering.TetheringConfiguration.DUN_NOT_REQUIRED; import static com.android.server.connectivity.tethering.TetheringConfiguration.DUN_REQUIRED; import static com.android.server.connectivity.tethering.TetheringConfiguration.DUN_UNSPECIFIED; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -44,26 +46,39 @@ import android.test.mock.MockContentResolver; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.FakeSettingsProvider; -import java.util.Iterator; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Iterator; @RunWith(AndroidJUnit4.class) @SmallTest public class TetheringConfigurationTest { private final SharedLog mLog = new SharedLog("TetheringConfigurationTest"); + + private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; @Mock private Context mContext; @Mock private TelephonyManager mTelephonyManager; @Mock private Resources mResources; + @Mock private Resources mResourcesForSubId; private MockContentResolver mContentResolver; private Context mMockContext; private boolean mHasTelephonyManager; + private class MockTetheringConfiguration extends TetheringConfiguration { + MockTetheringConfiguration(Context ctx, SharedLog log, int id) { + super(ctx, log, id); + } + + @Override + protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) { + return mResourcesForSubId; + } + } + private class MockContext extends BroadcastInterceptingContext { MockContext(Context base) { super(base); @@ -99,6 +114,9 @@ public class TetheringConfigurationTest { .thenReturn(new String[0]); when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types)) .thenReturn(new int[0]); + when(mResources.getStringArray( + com.android.internal.R.array.config_mobile_hotspot_provision_app)) + .thenReturn(new String[0]); mContentResolver = new MockContentResolver(); mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); mMockContext = new MockContext(mContext); @@ -111,7 +129,8 @@ public class TetheringConfigurationTest { mHasTelephonyManager = true; when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_REQUIRED); - final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog); + final TetheringConfiguration cfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); assertTrue(cfg.isDunRequired); assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN)); assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE)); @@ -127,7 +146,8 @@ public class TetheringConfigurationTest { mHasTelephonyManager = true; when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_NOT_REQUIRED); - final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog); + final TetheringConfiguration cfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); assertFalse(cfg.isDunRequired); assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN)); assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE)); @@ -143,7 +163,8 @@ public class TetheringConfigurationTest { mHasTelephonyManager = false; when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_UNSPECIFIED); - final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog); + final TetheringConfiguration cfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); assertTrue(cfg.isDunRequired); assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN)); // Just to prove we haven't clobbered Wi-Fi: @@ -160,7 +181,8 @@ public class TetheringConfigurationTest { mHasTelephonyManager = false; when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_UNSPECIFIED); - final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog); + final TetheringConfiguration cfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator(); assertTrue(upstreamIterator.hasNext()); assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue()); @@ -181,7 +203,8 @@ public class TetheringConfigurationTest { mHasTelephonyManager = false; when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_UNSPECIFIED); - final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog); + final TetheringConfiguration cfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator(); assertTrue(upstreamIterator.hasNext()); assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue()); @@ -199,7 +222,8 @@ public class TetheringConfigurationTest { mHasTelephonyManager = false; when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_UNSPECIFIED); - final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog); + final TetheringConfiguration cfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator(); assertTrue(upstreamIterator.hasNext()); assertEquals(TYPE_WIFI, upstreamIterator.next().intValue()); @@ -214,7 +238,8 @@ public class TetheringConfigurationTest { public void testNewDhcpServerDisabled() { Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1); - final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog); + final TetheringConfiguration cfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); assertTrue(cfg.enableLegacyDhcpServer); } @@ -222,7 +247,41 @@ public class TetheringConfigurationTest { public void testNewDhcpServerEnabled() { Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0); - final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog); + final TetheringConfiguration cfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); assertFalse(cfg.enableLegacyDhcpServer); } + + @Test + public void testGetResourcesBySubId() { + setUpResourceForSubId(); + final TetheringConfiguration cfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + assertTrue(cfg.provisioningApp.length == 0); + final int anyValidSubId = 1; + final MockTetheringConfiguration mockCfg = + new MockTetheringConfiguration(mMockContext, mLog, anyValidSubId); + assertEquals(mockCfg.provisioningApp[0], PROVISIONING_APP_NAME[0]); + assertEquals(mockCfg.provisioningApp[1], PROVISIONING_APP_NAME[1]); + } + + private void setUpResourceForSubId() { + when(mResourcesForSubId.getStringArray( + com.android.internal.R.array.config_tether_dhcp_range)).thenReturn(new String[0]); + when(mResourcesForSubId.getStringArray( + com.android.internal.R.array.config_tether_usb_regexs)).thenReturn(new String[0]); + when(mResourcesForSubId.getStringArray( + com.android.internal.R.array.config_tether_wifi_regexs)) + .thenReturn(new String[]{ "test_wlan\\d" }); + when(mResourcesForSubId.getStringArray( + com.android.internal.R.array.config_tether_bluetooth_regexs)) + .thenReturn(new String[0]); + when(mResourcesForSubId.getIntArray( + com.android.internal.R.array.config_tether_upstream_types)) + .thenReturn(new int[0]); + when(mResourcesForSubId.getStringArray( + com.android.internal.R.array.config_mobile_hotspot_provision_app)) + .thenReturn(PROVISIONING_APP_NAME); + } + } diff --git a/tools/preload/Android.bp b/tools/preload/Android.bp new file mode 100644 index 000000000000..809ee474969a --- /dev/null +++ b/tools/preload/Android.bp @@ -0,0 +1,17 @@ +java_library_host { + name: "preload", + srcs: [ + "Compile.java", + "LoadedClass.java", + "MemoryUsage.java", + "Operation.java", + "Policy.java", + "PrintCsv.java", + "PrintHtmlDiff.java", + "PrintPsTree.java", + "Proc.java", + "Record.java", + "Root.java", + "WritePreloadedClassFile.java", + ], +} diff --git a/tools/preload/Android.mk b/tools/preload/Android.mk deleted file mode 100644 index 14a4547cccbf..000000000000 --- a/tools/preload/Android.mk +++ /dev/null @@ -1,23 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ - Compile.java \ - LoadedClass.java \ - MemoryUsage.java \ - Operation.java \ - Policy.java \ - PrintCsv.java \ - PrintHtmlDiff.java \ - PrintPsTree.java \ - Proc.java \ - Record.java \ - Root.java \ - WritePreloadedClassFile.java - -LOCAL_MODULE:= preload - -include $(BUILD_HOST_JAVA_LIBRARY) - -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/tools/preload/loadclass/Android.bp b/tools/preload/loadclass/Android.bp new file mode 100644 index 000000000000..6f12015dae2b --- /dev/null +++ b/tools/preload/loadclass/Android.bp @@ -0,0 +1,4 @@ +java_test { + name: "loadclass", + srcs: ["**/*.java"], +} diff --git a/tools/preload/loadclass/Android.mk b/tools/preload/loadclass/Android.mk deleted file mode 100644 index 65828be617df..000000000000 --- a/tools/preload/loadclass/Android.mk +++ /dev/null @@ -1,9 +0,0 @@ -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_MODULE_TAGS := tests - -LOCAL_MODULE := loadclass - -include $(BUILD_JAVA_LIBRARY) |