diff options
209 files changed, 4403 insertions, 1915 deletions
diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000000..03af56d64062 --- /dev/null +++ b/.clang-format @@ -0,0 +1,13 @@ +BasedOnStyle: Google + +AccessModifierOffset: -4 +AlignOperands: false +AllowShortFunctionsOnASingleLine: Inline +AlwaysBreakBeforeMultilineStrings: false +ColumnLimit: 100 +CommentPragmas: NOLINT:.* +ConstructorInitializerIndentWidth: 6 +ContinuationIndentWidth: 8 +IndentWidth: 4 +PenaltyBreakBeforeFirstCallParameter: 100000 +SpacesBeforeTrailingComments: 1 diff --git a/Android.bp b/Android.bp index aeaaca3d7705..a93a155089ce 100644 --- a/Android.bp +++ b/Android.bp @@ -371,7 +371,7 @@ filegroup { java_library { name: "framework-updatable-stubs-module_libs_api", static_libs: [ - "framework-sdkextensions-stubs-module_libs_api", + "framework-sdkextensions.stubs.module_lib", "framework-tethering.stubs.module_lib", "updatable_media_stubs", ], diff --git a/Android.mk b/Android.mk index d8532489a786..46529eb64657 100644 --- a/Android.mk +++ b/Android.mk @@ -32,10 +32,6 @@ ifneq ($(ANDROID_BUILD_EMBEDDED),true) # ============================================================ include $(CLEAR_VARS) -# This is used by ide.mk as the list of source files that are -# always included. -INTERNAL_SDK_SOURCE_DIRS := $(addprefix $(LOCAL_PATH)/,$(dirs_to_document)) - # sdk.atree needs to copy the whole dir: $(OUT_DOCS)/offline-sdk to the final zip. # So keep offline-sdk-timestamp target here, and unzip offline-sdk-docs.zip to # $(OUT_DOCS)/offline-sdk. diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 68311176a783..9abb308534df 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -1,3 +1,15 @@ +[Builtin Hooks] +clang_format = true + +[Builtin Hooks Options] +# Only turn on clang-format check for the following subfolders. +clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp + cmds/hid/ + cmds/input/ + core/jni/ + libs/input/ + services/core/jni/ + [Hook Scripts] checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} diff --git a/StubLibraries.bp b/StubLibraries.bp index bb13eaacfa90..26478d3bad26 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -150,6 +150,9 @@ priv_apps = " " + module_libs = " " + " --show-annotation android.annotation.SystemApi\\(" + "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" + + "\\)" + + " --show-for-stub-purposes-annotation android.annotation.SystemApi\\(" + + "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" + "\\) " droidstubs { @@ -222,16 +225,10 @@ droidstubs { } ///////////////////////////////////////////////////////////////////// -// Following droidstubs modules are for extra APIs for modules. -// The framework currently have two more API surfaces for modules: -// @SystemApi(client=MODULE_APPS) and @SystemApi(client=MODULE_LIBRARIES) +// Following droidstubs modules are for extra APIs for modules, +// namely @SystemApi(client=MODULE_LIBRARIES) APIs. ///////////////////////////////////////////////////////////////////// -// TODO(b/146727827) remove the *-api module when we can teach metalava -// about the relationship among the API surfaces. Currently, these modules are only to generate -// the API signature files and ensure that the APIs evolve in a backwards compatible manner. -// They however are NOT used for building the API stub. - droidstubs { name: "module-lib-api", defaults: ["metalava-full-api-stubs-default"], @@ -268,7 +265,7 @@ droidstubs { name: "module-lib-api-stubs-docs-non-updatable", defaults: ["metalava-non-updatable-api-stubs-default"], arg_files: ["core/res/AndroidManifest.xml"], - args: metalava_framework_docs_args + module_libs, + args: metalava_framework_docs_args + priv_apps + module_libs, check_api: { current: { api_file: "non-updatable-api/module-lib-current.txt", @@ -277,17 +274,6 @@ droidstubs { }, } -// The following droidstub module generates source files for the API stub library for -// modules. Note that it not only includes its own APIs but also other APIs that have -// narrower scope (all @SystemApis, not just the ones with 'client=MODULE_LIBRARIES'). - -droidstubs { - name: "module-lib-api-stubs-docs", - defaults: ["metalava-non-updatable-api-stubs-default"], - arg_files: ["core/res/AndroidManifest.xml"], - args: metalava_framework_docs_args + priv_apps + module_libs, -} - ///////////////////////////////////////////////////////////////////// // android_*_stubs_current modules are the stubs libraries compiled // from *-api-stubs-docs @@ -333,7 +319,7 @@ java_library_static { java_library_static { name: "android_module_lib_stubs_current", - srcs: [ ":module-lib-api-stubs-docs" ], + srcs: [ ":module-lib-api-stubs-docs-non-updatable" ], defaults: ["android_defaults_stubs_current"], libs: ["sdk_system_29_android"], } diff --git a/apex/Android.bp b/apex/Android.bp index 380b4c6cc0be..f34ecbd79108 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -53,6 +53,9 @@ priv_apps = " " + module_libs = " " + " --show-annotation android.annotation.SystemApi\\(" + "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" + + "\\)" + + " --show-for-stub-purposes-annotation android.annotation.SystemApi\\(" + + "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" + "\\) " mainline_service_stubs_args = diff --git a/api/current.txt b/api/current.txt index fea1f2f4cac4..8dd7af81315b 100644 --- a/api/current.txt +++ b/api/current.txt @@ -41088,6 +41088,7 @@ package android.security { field public static final String EXTRA_KEY_ALIAS = "android.security.extra.KEY_ALIAS"; field public static final String EXTRA_NAME = "name"; field public static final String EXTRA_PKCS12 = "PKCS12"; + field public static final String KEY_ALIAS_SELECTION_DENIED = "android:alias-selection-denied"; } public interface KeyChainAliasCallback { @@ -45143,7 +45144,7 @@ package android.telephony { public abstract class CellLocation { ctor public CellLocation(); method public static android.telephony.CellLocation getEmpty(); - method public static void requestLocationUpdate(); + method @Deprecated public static void requestLocationUpdate(); } public abstract class CellSignalStrength { diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt index 355fa18ca249..283af068a077 100644 --- a/api/module-lib-current.txt +++ b/api/module-lib-current.txt @@ -11,24 +11,6 @@ package android.annotation { package android.net { - public final class TetheredClient implements android.os.Parcelable { - ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int); - method public int describeContents(); - method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses(); - method @NonNull public android.net.MacAddress getMacAddress(); - method public int getTetheringType(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR; - } - - public static final class TetheredClient.AddressInfo implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public android.net.LinkAddress getAddress(); - method @Nullable public String getHostname(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR; - } - public final class TetheringConstants { field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType"; field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback"; @@ -48,69 +30,15 @@ package android.net { method @NonNull public String[] getTetheringErroredIfaces(); method public boolean isTetheringSupported(); method public boolean isTetheringSupported(@NonNull String); - method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback); - method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener); method public void requestLatestTetheringEntitlementResult(int, @NonNull android.os.ResultReceiver, boolean); method @Deprecated public int setUsbTethering(boolean); - method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); - method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); - method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering(); - method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int); + method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); method @Deprecated public int tether(@NonNull String); - method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback); method @Deprecated public int untether(@NonNull String); - field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED"; - field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY"; - field public static final String EXTRA_ACTIVE_TETHER = "tetherArray"; - field public static final String EXTRA_AVAILABLE_TETHER = "availableArray"; - field public static final String EXTRA_ERRORED_TETHER = "erroredArray"; - field public static final int TETHERING_BLUETOOTH = 2; // 0x2 - field public static final int TETHERING_ETHERNET = 5; // 0x5 - field public static final int TETHERING_INVALID = -1; // 0xffffffff - field public static final int TETHERING_NCM = 4; // 0x4 - field public static final int TETHERING_USB = 1; // 0x1 - field public static final int TETHERING_WIFI = 0; // 0x0 - field public static final int TETHERING_WIFI_P2P = 3; // 0x3 - field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc - field public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; // 0x9 - field public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; // 0x8 - field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd - field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa - field public static final int TETHER_ERROR_INTERNAL_ERROR = 5; // 0x5 - field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf - field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe - field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 - field public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; // 0xb - field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2 - field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6 - field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4 - field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1 - field public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; // 0x10 - field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3 - field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7 - field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2 - field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1 - field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0 - } - - public static interface TetheringManager.OnTetheringEntitlementResultListener { - method public void onTetheringEntitlementResult(int); - } - - public static interface TetheringManager.StartTetheringCallback { - method public default void onTetheringFailed(int); - method public default void onTetheringStarted(); } public static interface TetheringManager.TetheringEventCallback { - method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>); - method public default void onError(@NonNull String, int); - method public default void onOffloadStatusChanged(int); method public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps); - method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>); - method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>); - method public default void onTetheringSupported(boolean); - method public default void onUpstreamChanged(@Nullable android.net.Network); } public static class TetheringManager.TetheringInterfaceRegexps { @@ -119,21 +47,5 @@ package android.net { method @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs(); } - public static class TetheringManager.TetheringRequest { - method @Nullable public android.net.LinkAddress getClientStaticIpv4Address(); - method @Nullable public android.net.LinkAddress getLocalIpv4Address(); - method public boolean getShouldShowEntitlementUi(); - method public int getTetheringType(); - method public boolean isExemptFromEntitlementCheck(); - } - - public static class TetheringManager.TetheringRequest.Builder { - ctor public TetheringManager.TetheringRequest.Builder(int); - method @NonNull public android.net.TetheringManager.TetheringRequest build(); - method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); - method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean); - method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress); - } - } diff --git a/cmds/statsd/src/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp index 019a9f7e5f9a..a21327b4aae0 100644 --- a/cmds/statsd/src/anomaly/AlarmTracker.cpp +++ b/cmds/statsd/src/anomaly/AlarmTracker.cpp @@ -61,11 +61,11 @@ void AlarmTracker::addSubscription(const Subscription& subscription) { } int64_t AlarmTracker::findNextAlarmSec(int64_t currentTimeSec) { - if (currentTimeSec <= mAlarmSec) { + if (currentTimeSec < mAlarmSec) { return mAlarmSec; } int64_t periodsForward = - ((currentTimeSec - mAlarmSec) * MS_PER_SEC - 1) / mAlarmConfig.period_millis() + 1; + ((currentTimeSec - mAlarmSec) * MS_PER_SEC) / mAlarmConfig.period_millis() + 1; return mAlarmSec + periodsForward * mAlarmConfig.period_millis() / MS_PER_SEC; } diff --git a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp index 5e6de1cc6b91..e664023a1647 100644 --- a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp +++ b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp @@ -40,23 +40,47 @@ TEST(AlarmTrackerTest, TestTriggerTimestamp) { alarm.set_offset_millis(15 * MS_PER_SEC); alarm.set_period_millis(60 * 60 * MS_PER_SEC); // 1hr int64_t startMillis = 100000000 * MS_PER_SEC; + int64_t nextAlarmTime = startMillis / MS_PER_SEC + 15; AlarmTracker tracker(startMillis, startMillis, alarm, kConfigKey, subscriberAlarmMonitor); - EXPECT_EQ(tracker.mAlarmSec, (int64_t)(startMillis / MS_PER_SEC + 15)); + EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime); uint64_t currentTimeSec = startMillis / MS_PER_SEC + 10; std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec)); EXPECT_TRUE(firedAlarmSet.empty()); tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet); - EXPECT_EQ(tracker.mAlarmSec, (int64_t)(startMillis / MS_PER_SEC + 15)); + EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime); + EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime); currentTimeSec = startMillis / MS_PER_SEC + 7000; + nextAlarmTime = startMillis / MS_PER_SEC + 15 + 2 * 60 * 60; firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec)); EXPECT_EQ(firedAlarmSet.size(), 1u); tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet); EXPECT_TRUE(firedAlarmSet.empty()); - EXPECT_EQ(tracker.mAlarmSec, (int64_t)(startMillis / MS_PER_SEC + 15 + 2 * 60 * 60)); + EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime); + EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime); + + // Alarm fires exactly on time. + currentTimeSec = startMillis / MS_PER_SEC + 15 + 2 * 60 * 60; + nextAlarmTime = startMillis / MS_PER_SEC + 15 + 3 * 60 * 60; + firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec)); + ASSERT_EQ(firedAlarmSet.size(), 1u); + tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet); + EXPECT_TRUE(firedAlarmSet.empty()); + EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime); + EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime); + + // Alarm fires exactly 1 period late. + currentTimeSec = startMillis / MS_PER_SEC + 15 + 4 * 60 * 60; + nextAlarmTime = startMillis / MS_PER_SEC + 15 + 5 * 60 * 60; + firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec)); + ASSERT_EQ(firedAlarmSet.size(), 1u); + tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet); + EXPECT_TRUE(firedAlarmSet.empty()); + EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime); + EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime); } } // namespace statsd diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java index 97074050448e..35f4add46d77 100644 --- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java +++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java @@ -64,6 +64,8 @@ public final class Telecom extends BaseCommand { private static final String COMMAND_UNREGISTER_PHONE_ACCOUNT = "unregister-phone-account"; private static final String COMMAND_SET_DEFAULT_DIALER = "set-default-dialer"; private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer"; + private static final String COMMAND_STOP_BLOCK_SUPPRESSION = "stop-block-suppression"; + /** * Change the system dialer package name if a package name was specified, * Example: adb shell telecom set-system-dialer <PACKAGE> @@ -111,6 +113,8 @@ public final class Telecom extends BaseCommand { + "usage: telecom set-sim-count <COUNT>\n" + "usage: telecom get-sim-config\n" + "usage: telecom get-max-phones\n" + + "usage: telecom stop-block-suppression: Stop suppressing the blocked number" + + " provider after a call to emergency services.\n" + "usage: telecom set-emer-phone-account-filter <PACKAGE>\n" + "\n" + "telecom set-phone-account-enabled: Enables the given phone account, if it has" @@ -203,6 +207,9 @@ public final class Telecom extends BaseCommand { case COMMAND_UNREGISTER_PHONE_ACCOUNT: runUnregisterPhoneAccount(); break; + case COMMAND_STOP_BLOCK_SUPPRESSION: + runStopBlockSuppression(); + break; case COMMAND_SET_DEFAULT_DIALER: runSetDefaultDialer(); break; @@ -320,8 +327,13 @@ public final class Telecom extends BaseCommand { System.out.println("Success - " + handle + " unregistered."); } + private void runStopBlockSuppression() throws RemoteException { + mTelecomService.stopBlockSuppression(); + } + private void runSetDefaultDialer() throws RemoteException { - final String packageName = nextArgRequired(); + String packageName = nextArg(); + if ("default".equals(packageName)) packageName = null; mTelecomService.setTestDefaultDialer(packageName); System.out.println("Success - " + packageName + " set as override default dialer."); } diff --git a/config/preloaded-classes b/config/preloaded-classes index b05d02c9eb15..e0a3f637645d 100644 --- a/config/preloaded-classes +++ b/config/preloaded-classes @@ -5583,8 +5583,6 @@ dalvik.system.CloseGuard$DefaultReporter dalvik.system.CloseGuard$Reporter dalvik.system.CloseGuard$Tracker dalvik.system.CloseGuard -dalvik.system.DalvikLogHandler -dalvik.system.DalvikLogging dalvik.system.DelegateLastClassLoader dalvik.system.DexClassLoader dalvik.system.DexFile$1 diff --git a/core/java/android/annotation/SystemApi.java b/core/java/android/annotation/SystemApi.java index 4ac00983af13..a468439c8e74 100644 --- a/core/java/android/annotation/SystemApi.java +++ b/core/java/android/annotation/SystemApi.java @@ -23,7 +23,6 @@ import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PACKAGE; import static java.lang.annotation.ElementType.TYPE; -import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -41,7 +40,6 @@ import java.lang.annotation.Target; */ @Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE, PACKAGE}) @Retention(RetentionPolicy.RUNTIME) -@Repeatable(SystemApi.Container.class) // TODO(b/146727827): make this non-repeatable public @interface SystemApi { enum Client { /** diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 4f1d7f2c761c..262051d0af1e 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -215,8 +215,8 @@ import java.util.function.Consumer; * <a name="Fragments"></a> * <h3>Fragments</h3> * - * <p>The {@link android.support.v4.app.FragmentActivity} subclass - * can make use of the {@link android.support.v4.app.Fragment} class to better + * <p>The {@link androidx.fragment.app.FragmentActivity} subclass + * can make use of the {@link androidx.fragment.app.Fragment} class to better * modularize their code, build more sophisticated user interfaces for larger * screens, and help scale their application between small and large screens.</p> * @@ -1003,7 +1003,7 @@ public class Activity extends ContextThemeWrapper /** * Return the LoaderManager for this activity, creating it if needed. * - * @deprecated Use {@link android.support.v4.app.FragmentActivity#getSupportLoaderManager()} + * @deprecated Use {@link androidx.fragment.app.FragmentActivity#getSupportLoaderManager()} */ @Deprecated public LoaderManager getLoaderManager() { @@ -3022,7 +3022,7 @@ public class Activity extends ContextThemeWrapper * Return the FragmentManager for interacting with fragments associated * with this activity. * - * @deprecated Use {@link android.support.v4.app.FragmentActivity#getSupportFragmentManager()} + * @deprecated Use {@link androidx.fragment.app.FragmentActivity#getSupportFragmentManager()} */ @Deprecated public FragmentManager getFragmentManager() { @@ -3035,7 +3035,7 @@ public class Activity extends ContextThemeWrapper * method and before {@link Fragment#onCreate Fragment.onCreate()}. * * @deprecated Use {@link - * android.support.v4.app.FragmentActivity#onAttachFragment(android.support.v4.app.Fragment)} + * androidx.fragment.app.FragmentActivity#onAttachFragment(androidx.fragment.app.Fragment)} */ @Deprecated public void onAttachFragment(Fragment fragment) { @@ -5847,8 +5847,8 @@ public class Activity extends ContextThemeWrapper * @see Fragment#startActivity * @see Fragment#startActivityForResult * - * @deprecated Use {@link android.support.v4.app.FragmentActivity#startActivityFromFragment( - * android.support.v4.app.Fragment,Intent,int)} + * @deprecated Use {@link androidx.fragment.app.FragmentActivity#startActivityFromFragment( + * androidx.fragment.app.Fragment,Intent,int)} */ @Deprecated public void startActivityFromFragment(@NonNull Fragment fragment, @@ -5876,8 +5876,8 @@ public class Activity extends ContextThemeWrapper * @see Fragment#startActivity * @see Fragment#startActivityForResult * - * @deprecated Use {@link android.support.v4.app.FragmentActivity#startActivityFromFragment( - * android.support.v4.app.Fragment,Intent,int,Bundle)} + * @deprecated Use {@link androidx.fragment.app.FragmentActivity#startActivityFromFragment( + * androidx.fragment.app.Fragment,Intent,int,Bundle)} */ @Deprecated public void startActivityFromFragment(@NonNull Fragment fragment, diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java index 647f63025cf7..febb293921cd 100644 --- a/core/java/android/app/AlarmManager.java +++ b/core/java/android/app/AlarmManager.java @@ -35,7 +35,7 @@ import android.util.ArrayMap; import android.util.Log; import android.util.proto.ProtoOutputStream; -import libcore.timezone.ZoneInfoDb; +import com.android.i18n.timezone.ZoneInfoDb; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index c6a0de458df0..da3bc681ed34 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -102,7 +102,7 @@ import java.lang.reflect.InvocationTargetException; * While the Fragment API was introduced in * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, a version of the API * at is also available for use on older platforms through - * {@link android.support.v4.app.FragmentActivity}. See the blog post + * {@link androidx.fragment.app.FragmentActivity}. See the blog post * <a href="http://android-developers.blogspot.com/2011/03/fragments-for-all.html"> * Fragments For All</a> for more details. * @@ -258,8 +258,8 @@ import java.lang.reflect.InvocationTargetException; * pressing back will pop it to return the user to whatever previous state * the activity UI was in. * - * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a> - * {@link android.support.v4.app.Fragment} for consistent behavior across all devices + * @deprecated Use the <a href="{@docRoot}jetpack">Jetpack Fragment Library</a> + * {@link androidx.fragment.app.Fragment} for consistent behavior across all devices * and access to <a href="{@docRoot}topic/libraries/architecture/lifecycle.html">Lifecycle</a>. */ @Deprecated @@ -432,7 +432,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * through {@link FragmentManager#saveFragmentInstanceState(Fragment) * FragmentManager.saveFragmentInstanceState}. * - * @deprecated Use {@link android.support.v4.app.Fragment.SavedState} + * @deprecated Use {@link androidx.fragment.app.Fragment.SavedState} */ @Deprecated public static class SavedState implements Parcelable { @@ -479,7 +479,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * Thrown by {@link Fragment#instantiate(Context, String, Bundle)} when * there is an instantiation failure. * - * @deprecated Use {@link android.support.v4.app.Fragment.InstantiationException} + * @deprecated Use {@link androidx.fragment.app.Fragment.InstantiationException} */ @Deprecated static public class InstantiationException extends AndroidRuntimeException { @@ -1055,7 +1055,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene /** * Return the LoaderManager for this fragment, creating it if needed. * - * @deprecated Use {@link android.support.v4.app.Fragment#getLoaderManager()} + * @deprecated Use {@link androidx.fragment.app.Fragment#getLoaderManager()} */ @Deprecated public LoaderManager getLoaderManager() { diff --git a/core/java/android/app/admin/DelegatedAdminReceiver.java b/core/java/android/app/admin/DelegatedAdminReceiver.java index f66de8d238ed..25b8eab452bf 100644 --- a/core/java/android/app/admin/DelegatedAdminReceiver.java +++ b/core/java/android/app/admin/DelegatedAdminReceiver.java @@ -63,6 +63,10 @@ public class DelegatedAdminReceiver extends BroadcastReceiver { * Allows this receiver to select the alias for a private key and certificate pair for * authentication. If this method returns null, the default {@link android.app.Activity} will * be shown that lets the user pick a private key and certificate pair. + * If this method returns {@link KeyChain#KEY_ALIAS_SELECTION_DENIED}, + * the default {@link android.app.Activity} will not be shown and the user will not be allowed + * to pick anything. And the app, that called {@link KeyChain#choosePrivateKeyAlias}, will + * receive {@code null} back. * * <p> This callback is only applicable if the delegated app has * {@link DevicePolicyManager#DELEGATION_CERT_SELECTION} capability. Additionally, it must diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java index 4771fd8a00b6..e3a49f39bdc7 100644 --- a/core/java/android/app/admin/DeviceAdminReceiver.java +++ b/core/java/android/app/admin/DeviceAdminReceiver.java @@ -791,6 +791,10 @@ public class DeviceAdminReceiver extends BroadcastReceiver { * Allows this receiver to select the alias for a private key and certificate pair for * authentication. If this method returns null, the default {@link android.app.Activity} will be * shown that lets the user pick a private key and certificate pair. + * If this method returns {@link KeyChain#KEY_ALIAS_SELECTION_DENIED}, + * the default {@link android.app.Activity} will not be shown and the user will not be allowed + * to pick anything. And the app, that called {@link KeyChain#choosePrivateKeyAlias}, will + * receive {@code null} back. * * @param context The running context as per {@link #onReceive}. * @param intent The received intent as per {@link #onReceive}. diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index f962ea0966bd..47bd207fee65 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1784,6 +1784,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { + if (DBG) Log.d(TAG, "removeActiveDevice, profiles: " + profiles); return mService.removeActiveDevice(profiles); } } catch (RemoteException e) { @@ -1828,6 +1829,9 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { + if (DBG) { + Log.d(TAG, "setActiveDevice, device: " + device + ", profiles: " + profiles); + } return mService.setActiveDevice(device, profiles); } } catch (RemoteException e) { diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java index 85e0e08b19c8..28363250ebda 100644 --- a/core/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java @@ -19,7 +19,6 @@ package android.bluetooth; import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; -import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; @@ -587,7 +586,6 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if connectionPolicy is set, false on error * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { @@ -637,7 +635,6 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return connection policy of the device * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java index 19240dc0bbc7..df11d3adac01 100644 --- a/core/java/android/bluetooth/BluetoothMapClient.java +++ b/core/java/android/bluetooth/BluetoothMapClient.java @@ -19,7 +19,6 @@ package android.bluetooth; import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; -import android.annotation.SystemApi; import android.app.PendingIntent; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -53,6 +52,18 @@ public final class BluetoothMapClient implements BluetoothProfile { public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY = "android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY"; + /** + * Action to notify read status changed + */ + public static final String ACTION_MESSAGE_READ_STATUS_CHANGED = + "android.bluetooth.mapmce.profile.action.MESSAGE_READ_STATUS_CHANGED"; + + /** + * Action to notify deleted status changed + */ + public static final String ACTION_MESSAGE_DELETED_STATUS_CHANGED = + "android.bluetooth.mapmce.profile.action.MESSAGE_DELETED_STATUS_CHANGED"; + /* Extras used in ACTION_MESSAGE_RECEIVED intent. * NOTE: HANDLE is only valid for a single session with the device. */ public static final String EXTRA_MESSAGE_HANDLE = @@ -66,6 +77,25 @@ public final class BluetoothMapClient implements BluetoothProfile { public static final String EXTRA_SENDER_CONTACT_NAME = "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_NAME"; + /** + * Used as a boolean extra in ACTION_MESSAGE_DELETED_STATUS_CHANGED + * Contains the MAP message deleted status + * Possible values are: + * true: deleted + * false: undeleted + */ + public static final String EXTRA_MESSAGE_DELETED_STATUS = + "android.bluetooth.mapmce.profile.extra.MESSAGE_DELETED_STATUS"; + + /** + * Extra used in ACTION_MESSAGE_READ_STATUS_CHANGED or ACTION_MESSAGE_DELETED_STATUS_CHANGED + * Possible values are: + * 0: failure + * 1: success + */ + public static final String EXTRA_RESULT_CODE = + "android.bluetooth.device.extra.RESULT_CODE"; + /** There was an error trying to obtain the state */ public static final int STATE_ERROR = -1; @@ -76,6 +106,12 @@ public final class BluetoothMapClient implements BluetoothProfile { private static final int UPLOADING_FEATURE_BITMASK = 0x08; + /** Parameters in setMessageStatus */ + public static final int UNREAD = 0; + public static final int READ = 1; + public static final int UNDELETED = 2; + public static final int DELETED = 3; + private BluetoothAdapter mAdapter; private final BluetoothProfileConnector<IBluetoothMapClient> mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.MAP_CLIENT, @@ -276,7 +312,6 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return true if connectionPolicy is set, false on error * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { @@ -325,7 +360,6 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return connection policy of the device * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) Log.d(TAG, "getConnectionPolicy(" + device + ")"); @@ -408,6 +442,38 @@ public final class BluetoothMapClient implements BluetoothProfile { return false; } + /** + * Set message status of message on MSE + * <p> + * When read status changed, the result will be published via + * {@link #ACTION_MESSAGE_READ_STATUS_CHANGED} + * When deleted status changed, the result will be published via + * {@link #ACTION_MESSAGE_DELETED_STATUS_CHANGED} + * + * @param device Bluetooth device + * @param handle message handle + * @param status <code>UNREAD</code> for "unread", <code>READ</code> for + * "read", <code>UNDELETED</code> for "undeleted", <code>DELETED</code> for + * "deleted", otherwise return error + * @return <code>true</code> if request has been sent, <code>false</code> on error + * + */ + @RequiresPermission(Manifest.permission.READ_SMS) + public boolean setMessageStatus(BluetoothDevice device, String handle, int status) { + if (DBG) Log.d(TAG, "setMessageStatus(" + device + ", " + handle + ", " + status + ")"); + final IBluetoothMapClient service = getService(); + if (service != null && isEnabled() && isValidDevice(device) && handle != null && + (status == READ || status == UNREAD || status == UNDELETED || status == DELETED)) { + try { + return service.setMessageStatus(device, handle, status); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; + } + } + return false; + } + private boolean isEnabled() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true; diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java index d3452ffb4586..f356da18fc73 100644 --- a/core/java/android/bluetooth/BluetoothPbapClient.java +++ b/core/java/android/bluetooth/BluetoothPbapClient.java @@ -19,7 +19,6 @@ package android.bluetooth; import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; -import android.annotation.SystemApi; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -276,7 +275,6 @@ public final class BluetoothPbapClient implements BluetoothProfile { * @return true if connectionPolicy is set, false on error * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { @@ -329,7 +327,6 @@ public final class BluetoothPbapClient implements BluetoothProfile { * @return connection policy of the device * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) { diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java index 6e0348158f48..48e8c1ada255 100644 --- a/core/java/android/bluetooth/BluetoothSap.java +++ b/core/java/android/bluetooth/BluetoothSap.java @@ -18,7 +18,6 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresPermission; -import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; @@ -328,7 +327,6 @@ public final class BluetoothSap implements BluetoothProfile { * @return true if connectionPolicy is set, false on error * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { @@ -377,7 +375,6 @@ public final class BluetoothSap implements BluetoothProfile { * @return connection policy of the device * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 0e610169ffa6..62669e019558 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -46,7 +46,6 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; -import android.annotation.TestApi; import android.apex.ApexInfo; import android.app.ActivityTaskManager; import android.app.ActivityThread; @@ -2682,7 +2681,6 @@ public class PackageParser { * not compatible with this platform * @hide Exposed for unit testing only. */ - @TestApi public static int computeTargetSdkVersion(@IntRange(from = 0) int targetVers, @Nullable String targetCode, @NonNull String[] platformSdkCodenames, @NonNull String[] outError) { @@ -2747,7 +2745,6 @@ public class PackageParser { * compatible with this platform * @hide Exposed for unit testing only. */ - @TestApi public static int computeMinSdkVersion(@IntRange(from = 1) int minVers, @Nullable String minCode, @IntRange(from = 1) int platformSdkVersion, @NonNull String[] platformSdkCodenames, @NonNull String[] outError) { @@ -4845,7 +4842,6 @@ public class PackageParser { * AndroidManifest.xml. * @hide Exposed for unit testing only. */ - @TestApi public static int getActivityConfigChanges(int configChanges, int recreateOnConfigChanges) { return configChanges | ((~recreateOnConfigChanges) & RECREATE_ON_CONFIG_CHANGES_MASK); } diff --git a/core/java/android/hardware/GeomagneticField.java b/core/java/android/hardware/GeomagneticField.java index 0d7b695d7f1d..cbfe4fa58e40 100644 --- a/core/java/android/hardware/GeomagneticField.java +++ b/core/java/android/hardware/GeomagneticField.java @@ -16,7 +16,8 @@ package android.hardware; -import java.util.GregorianCalendar; +import java.util.Calendar; +import java.util.TimeZone; /** * Estimates magnetic field at a given point on @@ -26,7 +27,7 @@ import java.util.GregorianCalendar; * <p>This uses the World Magnetic Model produced by the United States National * Geospatial-Intelligence Agency. More details about the model can be found at * <a href="http://www.ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml">http://www.ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml</a>. - * This class currently uses WMM-2015 which is valid until 2020, but should + * This class currently uses WMM-2020 which is valid until 2025, but should * produce acceptable results for several years after that. Future versions of * Android may use a newer version of the model. */ @@ -48,69 +49,72 @@ public class GeomagneticField { static private final float EARTH_REFERENCE_RADIUS_KM = 6371.2f; // These coefficients and the formulae used below are from: - // NOAA Technical Report: The US/UK World Magnetic Model for 2015-2020 - static private final float[][] G_COEFF = new float[][] { - { 0.0f }, - { -29438.5f, -1501.1f }, - { -2445.3f, 3012.5f, 1676.6f }, - { 1351.1f, -2352.3f, 1225.6f, 581.9f }, - { 907.2f, 813.7f, 120.3f, -335.0f, 70.3f }, - { -232.6f, 360.1f, 192.4f, -141.0f, -157.4f, 4.3f }, - { 69.5f, 67.4f, 72.8f, -129.8f, -29.0f, 13.2f, -70.9f }, - { 81.6f, -76.1f, -6.8f, 51.9f, 15.0f, 9.3f, -2.8f, 6.7f }, - { 24.0f, 8.6f, -16.9f, -3.2f, -20.6f, 13.3f, 11.7f, -16.0f, -2.0f }, - { 5.4f, 8.8f, 3.1f, -3.1f, 0.6f, -13.3f, -0.1f, 8.7f, -9.1f, -10.5f }, - { -1.9f, -6.5f, 0.2f, 0.6f, -0.6f, 1.7f, -0.7f, 2.1f, 2.3f, -1.8f, -3.6f }, - { 3.1f, -1.5f, -2.3f, 2.1f, -0.9f, 0.6f, -0.7f, 0.2f, 1.7f, -0.2f, 0.4f, 3.5f }, - { -2.0f, -0.3f, 0.4f, 1.3f, -0.9f, 0.9f, 0.1f, 0.5f, -0.4f, -0.4f, 0.2f, -0.9f, 0.0f } }; - - static private final float[][] H_COEFF = new float[][] { - { 0.0f }, - { 0.0f, 4796.2f }, - { 0.0f, -2845.6f, -642.0f }, - { 0.0f, -115.3f, 245.0f, -538.3f }, - { 0.0f, 283.4f, -188.6f, 180.9f, -329.5f }, - { 0.0f, 47.4f, 196.9f, -119.4f, 16.1f, 100.1f }, - { 0.0f, -20.7f, 33.2f, 58.8f, -66.5f, 7.3f, 62.5f }, - { 0.0f, -54.1f, -19.4f, 5.6f, 24.4f, 3.3f, -27.5f, -2.3f }, - { 0.0f, 10.2f, -18.1f, 13.2f, -14.6f, 16.2f, 5.7f, -9.1f, 2.2f }, - { 0.0f, -21.6f, 10.8f, 11.7f, -6.8f, -6.9f, 7.8f, 1.0f, -3.9f, 8.5f }, - { 0.0f, 3.3f, -0.3f, 4.6f, 4.4f, -7.9f, -0.6f, -4.1f, -2.8f, -1.1f, -8.7f }, - { 0.0f, -0.1f, 2.1f, -0.7f, -1.1f, 0.7f, -0.2f, -2.1f, -1.5f, -2.5f, -2.0f, -2.3f }, - { 0.0f, -1.0f, 0.5f, 1.8f, -2.2f, 0.3f, 0.7f, -0.1f, 0.3f, 0.2f, -0.9f, -0.2f, 0.7f } }; - - static private final float[][] DELTA_G = new float[][] { - { 0.0f }, - { 10.7f, 17.9f }, - { -8.6f, -3.3f, 2.4f }, - { 3.1f, -6.2f, -0.4f, -10.4f }, - { -0.4f, 0.8f, -9.2f, 4.0f, -4.2f }, - { -0.2f, 0.1f, -1.4f, 0.0f, 1.3f, 3.8f }, - { -0.5f, -0.2f, -0.6f, 2.4f, -1.1f, 0.3f, 1.5f }, - { 0.2f, -0.2f, -0.4f, 1.3f, 0.2f, -0.4f, -0.9f, 0.3f }, - { 0.0f, 0.1f, -0.5f, 0.5f, -0.2f, 0.4f, 0.2f, -0.4f, 0.3f }, - { 0.0f, -0.1f, -0.1f, 0.4f, -0.5f, -0.2f, 0.1f, 0.0f, -0.2f, -0.1f }, - { 0.0f, 0.0f, -0.1f, 0.3f, -0.1f, -0.1f, -0.1f, 0.0f, -0.2f, -0.1f, -0.2f }, - { 0.0f, 0.0f, -0.1f, 0.1f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.1f, -0.1f }, - { 0.1f, 0.0f, 0.0f, 0.1f, -0.1f, 0.0f, 0.1f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f } }; - - static private final float[][] DELTA_H = new float[][] { - { 0.0f }, - { 0.0f, -26.8f }, - { 0.0f, -27.1f, -13.3f }, - { 0.0f, 8.4f, -0.4f, 2.3f }, - { 0.0f, -0.6f, 5.3f, 3.0f, -5.3f }, - { 0.0f, 0.4f, 1.6f, -1.1f, 3.3f, 0.1f }, - { 0.0f, 0.0f, -2.2f, -0.7f, 0.1f, 1.0f, 1.3f }, - { 0.0f, 0.7f, 0.5f, -0.2f, -0.1f, -0.7f, 0.1f, 0.1f }, - { 0.0f, -0.3f, 0.3f, 0.3f, 0.6f, -0.1f, -0.2f, 0.3f, 0.0f }, - { 0.0f, -0.2f, -0.1f, -0.2f, 0.1f, 0.1f, 0.0f, -0.2f, 0.4f, 0.3f }, - { 0.0f, 0.1f, -0.1f, 0.0f, 0.0f, -0.2f, 0.1f, -0.1f, -0.2f, 0.1f, -0.1f }, - { 0.0f, 0.0f, 0.1f, 0.0f, 0.1f, 0.0f, 0.0f, 0.1f, 0.0f, -0.1f, 0.0f, -0.1f }, - { 0.0f, 0.0f, 0.0f, -0.1f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f } }; - - static private final long BASE_TIME = - new GregorianCalendar(2015, 1, 1).getTimeInMillis(); + // NOAA Technical Report: The US/UK World Magnetic Model for 2020-2025 + static private final float[][] G_COEFF = new float[][]{ + {0.0f}, + {-29404.5f, -1450.7f}, + {-2500.0f, 2982.0f, 1676.8f}, + {1363.9f, -2381.0f, 1236.2f, 525.7f}, + {903.1f, 809.4f, 86.2f, -309.4f, 47.9f}, + {-234.4f, 363.1f, 187.8f, -140.7f, -151.2f, 13.7f}, + {65.9f, 65.6f, 73.0f, -121.5f, -36.2f, 13.5f, -64.7f}, + {80.6f, -76.8f, -8.3f, 56.5f, 15.8f, 6.4f, -7.2f, 9.8f}, + {23.6f, 9.8f, -17.5f, -0.4f, -21.1f, 15.3f, 13.7f, -16.5f, -0.3f}, + {5.0f, 8.2f, 2.9f, -1.4f, -1.1f, -13.3f, 1.1f, 8.9f, -9.3f, -11.9f}, + {-1.9f, -6.2f, -0.1f, 1.7f, -0.9f, 0.6f, -0.9f, 1.9f, 1.4f, -2.4f, -3.9f}, + {3.0f, -1.4f, -2.5f, 2.4f, -0.9f, 0.3f, -0.7f, -0.1f, 1.4f, -0.6f, 0.2f, 3.1f}, + {-2.0f, -0.1f, 0.5f, 1.3f, -1.2f, 0.7f, 0.3f, 0.5f, -0.2f, -0.5f, 0.1f, -1.1f, -0.3f}}; + + static private final float[][] H_COEFF = new float[][]{ + {0.0f}, + {0.0f, 4652.9f}, + {0.0f, -2991.6f, -734.8f}, + {0.0f, -82.2f, 241.8f, -542.9f}, + {0.0f, 282.0f, -158.4f, 199.8f, -350.1f}, + {0.0f, 47.7f, 208.4f, -121.3f, 32.2f, 99.1f}, + {0.0f, -19.1f, 25.0f, 52.7f, -64.4f, 9.0f, 68.1f}, + {0.0f, -51.4f, -16.8f, 2.3f, 23.5f, -2.2f, -27.2f, -1.9f}, + {0.0f, 8.4f, -15.3f, 12.8f, -11.8f, 14.9f, 3.6f, -6.9f, 2.8f}, + {0.0f, -23.3f, 11.1f, 9.8f, -5.1f, -6.2f, 7.8f, 0.4f, -1.5f, 9.7f}, + {0.0f, 3.4f, -0.2f, 3.5f, 4.8f, -8.6f, -0.1f, -4.2f, -3.4f, -0.1f, -8.8f}, + {0.0f, 0.0f, 2.6f, -0.5f, -0.4f, 0.6f, -0.2f, -1.7f, -1.6f, -3.0f, -2.0f, -2.6f}, + {0.0f, -1.2f, 0.5f, 1.3f, -1.8f, 0.1f, 0.7f, -0.1f, 0.6f, 0.2f, -0.9f, 0.0f, 0.5f}}; + + static private final float[][] DELTA_G = new float[][]{ + {0.0f}, + {6.7f, 7.7f}, + {-11.5f, -7.1f, -2.2f}, + {2.8f, -6.2f, 3.4f, -12.2f}, + {-1.1f, -1.6f, -6.0f, 5.4f, -5.5f}, + {-0.3f, 0.6f, -0.7f, 0.1f, 1.2f, 1.0f}, + {-0.6f, -0.4f, 0.5f, 1.4f, -1.4f, 0.0f, 0.8f}, + {-0.1f, -0.3f, -0.1f, 0.7f, 0.2f, -0.5f, -0.8f, 1.0f}, + {-0.1f, 0.1f, -0.1f, 0.5f, -0.1f, 0.4f, 0.5f, 0.0f, 0.4f}, + {-0.1f, -0.2f, 0.0f, 0.4f, -0.3f, 0.0f, 0.3f, 0.0f, 0.0f, -0.4f}, + {0.0f, 0.0f, 0.0f, 0.2f, -0.1f, -0.2f, 0.0f, -0.1f, -0.2f, -0.1f, 0.0f}, + {0.0f, -0.1f, 0.0f, 0.0f, 0.0f, -0.1f, 0.0f, 0.0f, -0.1f, -0.1f, -0.1f, -0.1f}, + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.1f}}; + + static private final float[][] DELTA_H = new float[][]{ + {0.0f}, + {0.0f, -25.1f}, + {0.0f, -30.2f, -23.9f}, + {0.0f, 5.7f, -1.0f, 1.1f}, + {0.0f, 0.2f, 6.9f, 3.7f, -5.6f}, + {0.0f, 0.1f, 2.5f, -0.9f, 3.0f, 0.5f}, + {0.0f, 0.1f, -1.8f, -1.4f, 0.9f, 0.1f, 1.0f}, + {0.0f, 0.5f, 0.6f, -0.7f, -0.2f, -1.2f, 0.2f, 0.3f}, + {0.0f, -0.3f, 0.7f, -0.2f, 0.5f, -0.3f, -0.5f, 0.4f, 0.1f}, + {0.0f, -0.3f, 0.2f, -0.4f, 0.4f, 0.1f, 0.0f, -0.2f, 0.5f, 0.2f}, + {0.0f, 0.0f, 0.1f, -0.3f, 0.1f, -0.2f, 0.1f, 0.0f, -0.1f, 0.2f, 0.0f}, + {0.0f, 0.0f, 0.1f, 0.0f, 0.2f, 0.0f, 0.0f, 0.1f, 0.0f, -0.1f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, -0.1f, 0.1f, 0.0f, 0.0f, 0.0f, 0.1f, 0.0f, 0.0f, 0.0f, -0.1f}}; + + static private final long BASE_TIME = new Calendar.Builder() + .setTimeZone(TimeZone.getTimeZone("UTC")) + .setDate(2020, Calendar.JANUARY, 1) + .build() + .getTimeInMillis(); // The ratio between the Gauss-normalized associated Legendre functions and // the Schmid quasi-normalized ones. Compute these once staticly since they @@ -190,7 +194,7 @@ public class GeomagneticField { // We now compute the magnetic field strength given the geocentric // location. The magnetic field is the derivative of the potential // function defined by the model. See NOAA Technical Report: The US/UK - // World Magnetic Model for 2015-2020 for the derivation. + // World Magnetic Model for 2020-2025 for the derivation. float gcX = 0.0f; // Geocentric northwards component. float gcY = 0.0f; // Geocentric eastwards component. float gcZ = 0.0f; // Geocentric downwards component. @@ -203,7 +207,7 @@ public class GeomagneticField { // Negative derivative with respect to latitude, divided by // radius. This looks like the negation of the version in the - // NOAA Techincal report because that report used + // NOAA Technical report because that report used // P_n^m(sin(theta)) and we use P_n^m(cos(90 - theta)), so the // derivative with respect to theta is negated. gcX += relativeRadiusPower[n+2] diff --git a/core/java/android/net/DnsPacket.java b/core/java/android/net/DnsPacket.java deleted file mode 100644 index 83e57e0a047b..000000000000 --- a/core/java/android/net/DnsPacket.java +++ /dev/null @@ -1,235 +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.net; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.text.TextUtils; - -import com.android.internal.util.BitUtils; - -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.text.DecimalFormat; -import java.text.FieldPosition; -import java.util.ArrayList; -import java.util.List; - -/** - * Defines basic data for DNS protocol based on RFC 1035. - * Subclasses create the specific format used in DNS packet. - * - * @hide - */ -public abstract class DnsPacket { - public class DnsHeader { - private static final String TAG = "DnsHeader"; - public final int id; - public final int flags; - public final int rcode; - private final int[] mRecordCount; - - /** - * Create a new DnsHeader from a positioned ByteBuffer. - * - * The ByteBuffer must be in network byte order (which is the default). - * Reads the passed ByteBuffer from its current position and decodes a DNS header. - * When this constructor returns, the reading position of the ByteBuffer has been - * advanced to the end of the DNS header record. - * This is meant to chain with other methods reading a DNS response in sequence. - */ - DnsHeader(@NonNull ByteBuffer buf) throws BufferUnderflowException { - id = BitUtils.uint16(buf.getShort()); - flags = BitUtils.uint16(buf.getShort()); - rcode = flags & 0xF; - mRecordCount = new int[NUM_SECTIONS]; - for (int i = 0; i < NUM_SECTIONS; ++i) { - mRecordCount[i] = BitUtils.uint16(buf.getShort()); - } - } - - /** - * Get record count by type. - */ - public int getRecordCount(int type) { - return mRecordCount[type]; - } - } - - /** - * Superclass for DNS questions and DNS resource records. - * - * DNS questions (No TTL/RDATA) - * DNS resource records (With TTL/RDATA) - */ - public class DnsRecord { - private static final int MAXNAMESIZE = 255; - private static final int MAXLABELSIZE = 63; - private static final int MAXLABELCOUNT = 128; - private static final int NAME_NORMAL = 0; - private static final int NAME_COMPRESSION = 0xC0; - private final DecimalFormat byteFormat = new DecimalFormat(); - private final FieldPosition pos = new FieldPosition(0); - - private static final String TAG = "DnsRecord"; - - public final String dName; - public final int nsType; - public final int nsClass; - public final long ttl; - private final byte[] mRdata; - - /** - * Create a new DnsRecord from a positioned ByteBuffer. - * - * Reads the passed ByteBuffer from its current position and decodes a DNS record. - * When this constructor returns, the reading position of the ByteBuffer has been - * advanced to the end of the DNS header record. - * This is meant to chain with other methods reading a DNS response in sequence. - * - * @param ByteBuffer input of record, must be in network byte order - * (which is the default). - */ - DnsRecord(int recordType, @NonNull ByteBuffer buf) - throws BufferUnderflowException, ParseException { - dName = parseName(buf, 0 /* Parse depth */); - if (dName.length() > MAXNAMESIZE) { - throw new ParseException( - "Parse name fail, name size is too long: " + dName.length()); - } - nsType = BitUtils.uint16(buf.getShort()); - nsClass = BitUtils.uint16(buf.getShort()); - - if (recordType != QDSECTION) { - ttl = BitUtils.uint32(buf.getInt()); - final int length = BitUtils.uint16(buf.getShort()); - mRdata = new byte[length]; - buf.get(mRdata); - } else { - ttl = 0; - mRdata = null; - } - } - - /** - * Get a copy of rdata. - */ - @Nullable - public byte[] getRR() { - return (mRdata == null) ? null : mRdata.clone(); - } - - /** - * Convert label from {@code byte[]} to {@code String} - * - * Follows the same conversion rules of the native code (ns_name.c in libc) - */ - private String labelToString(@NonNull byte[] label) { - final StringBuffer sb = new StringBuffer(); - for (int i = 0; i < label.length; ++i) { - int b = BitUtils.uint8(label[i]); - // Control characters and non-ASCII characters. - if (b <= 0x20 || b >= 0x7f) { - // Append the byte as an escaped decimal number, e.g., "\19" for 0x13. - sb.append('\\'); - byteFormat.format(b, sb, pos); - } else if (b == '"' || b == '.' || b == ';' || b == '\\' - || b == '(' || b == ')' || b == '@' || b == '$') { - // Append the byte as an escaped character, e.g., "\:" for 0x3a. - sb.append('\\'); - sb.append((char) b); - } else { - // Append the byte as a character, e.g., "a" for 0x61. - sb.append((char) b); - } - } - return sb.toString(); - } - - private String parseName(@NonNull ByteBuffer buf, int depth) throws - BufferUnderflowException, ParseException { - if (depth > MAXLABELCOUNT) { - throw new ParseException("Failed to parse name, too many labels"); - } - final int len = BitUtils.uint8(buf.get()); - final int mask = len & NAME_COMPRESSION; - if (0 == len) { - return ""; - } else if (mask != NAME_NORMAL && mask != NAME_COMPRESSION) { - throw new ParseException("Parse name fail, bad label type"); - } else if (mask == NAME_COMPRESSION) { - // Name compression based on RFC 1035 - 4.1.4 Message compression - final int offset = ((len & ~NAME_COMPRESSION) << 8) + BitUtils.uint8(buf.get()); - final int oldPos = buf.position(); - if (offset >= oldPos - 2) { - throw new ParseException("Parse compression name fail, invalid compression"); - } - buf.position(offset); - final String pointed = parseName(buf, depth + 1); - buf.position(oldPos); - return pointed; - } else { - final byte[] label = new byte[len]; - buf.get(label); - final String head = labelToString(label); - if (head.length() > MAXLABELSIZE) { - throw new ParseException("Parse name fail, invalid label length"); - } - final String tail = parseName(buf, depth + 1); - return TextUtils.isEmpty(tail) ? head : head + "." + tail; - } - } - } - - public static final int QDSECTION = 0; - public static final int ANSECTION = 1; - public static final int NSSECTION = 2; - public static final int ARSECTION = 3; - private static final int NUM_SECTIONS = ARSECTION + 1; - - private static final String TAG = DnsPacket.class.getSimpleName(); - - protected final DnsHeader mHeader; - protected final List<DnsRecord>[] mRecords; - - protected DnsPacket(@NonNull byte[] data) throws ParseException { - if (null == data) throw new ParseException("Parse header failed, null input data"); - final ByteBuffer buffer; - try { - buffer = ByteBuffer.wrap(data); - mHeader = new DnsHeader(buffer); - } catch (BufferUnderflowException e) { - throw new ParseException("Parse Header fail, bad input data", e); - } - - mRecords = new ArrayList[NUM_SECTIONS]; - - for (int i = 0; i < NUM_SECTIONS; ++i) { - final int count = mHeader.getRecordCount(i); - if (count > 0) { - mRecords[i] = new ArrayList(count); - } - for (int j = 0; j < count; ++j) { - try { - mRecords[i].add(new DnsRecord(i, buffer)); - } catch (BufferUnderflowException e) { - throw new ParseException("Parse record fail", e); - } - } - } - } -} diff --git a/core/java/android/net/DnsResolver.java b/core/java/android/net/DnsResolver.java index 0b1a84534e38..3f7660f5709a 100644 --- a/core/java/android/net/DnsResolver.java +++ b/core/java/android/net/DnsResolver.java @@ -38,6 +38,8 @@ import android.os.MessageQueue; import android.system.ErrnoException; import android.util.Log; +import com.android.net.module.util.DnsPacket; + import java.io.FileDescriptor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -97,7 +99,7 @@ public final class DnsResolver { @interface DnsError {} /** * Indicates that there was an error parsing the response the query. - * The cause of this error is available via getCause() and is a ParseException. + * The cause of this error is available via getCause() and is a {@link ParseException}. */ public static final int ERROR_PARSE = 0; /** @@ -290,8 +292,15 @@ public final class DnsResolver { } try { mAllAnswers.addAll(new DnsAddressAnswer(answer).getAddresses()); - } catch (ParseException e) { - mDnsException = new DnsException(ERROR_PARSE, e); + } catch (DnsPacket.ParseException e) { + // Convert the com.android.net.module.util.DnsPacket.ParseException to an + // android.net.ParseException. This is the type that was used in Q and is implied + // by the public documentation of ERROR_PARSE. + // + // DnsPacket cannot throw android.net.ParseException directly because it's @hide. + ParseException pe = new ParseException(e.reason, e.getCause()); + pe.setStackTrace(e.getStackTrace()); + mDnsException = new DnsException(ERROR_PARSE, pe); } maybeReportAnswer(); } diff --git a/core/java/android/net/ITetheredInterfaceCallback.aidl b/core/java/android/net/ITetheredInterfaceCallback.aidl index e3d075988c8a..14aa0237f24a 100644 --- a/core/java/android/net/ITetheredInterfaceCallback.aidl +++ b/core/java/android/net/ITetheredInterfaceCallback.aidl @@ -17,7 +17,7 @@ package android.net; /** @hide */ -interface ITetheredInterfaceCallback { +oneway interface ITetheredInterfaceCallback { void onAvailable(in String iface); void onUnavailable(); }
\ No newline at end of file diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java index 0e10c42e61db..0eb3c1e8ad01 100644 --- a/core/java/android/net/MacAddress.java +++ b/core/java/android/net/MacAddress.java @@ -38,7 +38,9 @@ import java.util.Arrays; * Representation of a MAC address. * * This class only supports 48 bits long addresses and does not support 64 bits long addresses. - * Instances of this class are immutable. + * Instances of this class are immutable. This class provides implementations of hashCode() + * and equals() that make it suitable for use as keys in standard implementations of + * {@link java.util.Map}. */ public final class MacAddress implements Parcelable { @@ -122,12 +124,22 @@ public final class MacAddress implements Parcelable { } /** + * Convert this MacAddress to a byte array. + * + * The returned array is in network order. For example, if this MacAddress is 1:2:3:4:5:6, + * the returned array is [1, 2, 3, 4, 5, 6]. + * * @return a byte array representation of this MacAddress. */ public @NonNull byte[] toByteArray() { return byteAddrFromLongAddr(mAddr); } + /** + * Returns a human-readable representation of this MacAddress. + * The exact format is implementation-dependent and should not be assumed to have any + * particular format. + */ @Override public @NonNull String toString() { return stringAddrFromLongAddr(mAddr); diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java index cb9463a59d34..4b806e714ffe 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -84,6 +84,15 @@ public class NetworkTemplate implements Parcelable { * @hide */ public static final int NETWORK_TYPE_ALL = -1; + /** + * Virtual RAT type to represent 5G NSA (Non Stand Alone) mode, where the primary cell is + * still LTE and network allocates a secondary 5G cell so telephony reports RAT = LTE along + * with NR state as connected. This should not be overlapped with any of the + * {@code TelephonyManager.NETWORK_TYPE_*} constants. + * + * @hide + */ + public static final int NETWORK_TYPE_5G_NSA = -2; private static boolean isKnownMatchRule(final int rule) { switch (rule) { @@ -472,6 +481,9 @@ public class NetworkTemplate implements Parcelable { return TelephonyManager.NETWORK_TYPE_LTE; case TelephonyManager.NETWORK_TYPE_NR: return TelephonyManager.NETWORK_TYPE_NR; + // Virtual RAT type for 5G NSA mode, see {@link NetworkTemplate#NETWORK_TYPE_5G_NSA}. + case NetworkTemplate.NETWORK_TYPE_5G_NSA: + return NetworkTemplate.NETWORK_TYPE_5G_NSA; default: return TelephonyManager.NETWORK_TYPE_UNKNOWN; } diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java index 779f7bc91e8f..0b92b95128d3 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/core/java/android/net/NetworkUtils.java @@ -155,6 +155,14 @@ public class NetworkUtils { public static native Network getDnsNetwork() throws ErrnoException; /** + * Allow/Disallow creating AF_INET/AF_INET6 sockets and DNS lookups for current process. + * + * @param allowNetworking whether to allow or disallow creating AF_INET/AF_INET6 sockets + * and DNS lookups. + */ + public static native void setAllowNetworkingForProcess(boolean allowNetworking); + + /** * Get the tcp repair window associated with the {@code fd}. * * @param fd the tcp socket's {@link FileDescriptor}. diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java index ffe9ae9521a8..a32b41f6be4b 100644 --- a/core/java/android/net/ProxyInfo.java +++ b/core/java/android/net/ProxyInfo.java @@ -127,18 +127,6 @@ public class ProxyInfo implements Parcelable { } /** - * Create a ProxyProperties that points at a PAC URL. - * @hide - */ - public ProxyInfo(String pacFileUrl) { - mHost = LOCAL_HOST; - mPort = LOCAL_PORT; - mExclusionList = LOCAL_EXCL_LIST; - mParsedExclusionList = parseExclusionList(mExclusionList); - mPacFileUrl = Uri.parse(pacFileUrl); - } - - /** * Only used in PacManager after Local Proxy is bound. * @hide */ diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 50379222f1a9..9418cbf27643 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -1051,8 +1051,6 @@ public final class Debug if (outStream != null) outStream.close(); } - - VMDebug.startEmulatorTracing(); } /** @@ -1066,8 +1064,6 @@ public final class Debug * region of code.</p> */ public static void stopNativeTracing() { - VMDebug.stopEmulatorTracing(); - // Open the sysfs file for writing and write "0" to it. PrintWriter outStream = null; try { @@ -1096,7 +1092,7 @@ public final class Debug * To temporarily enable tracing, use {@link #startNativeTracing()}. */ public static void enableEmulatorTraceOutput() { - VMDebug.startEmulatorTracing(); + Log.w(TAG, "Unimplemented"); } /** @@ -2028,25 +2024,6 @@ public final class Debug public static final native int getBinderDeathObjectCount(); /** - * Primes the register map cache. - * - * Only works for classes in the bootstrap class loader. Does not - * cause classes to be loaded if they're not already present. - * - * The classAndMethodDesc argument is a concatentation of the VM-internal - * class descriptor, method name, and method descriptor. Examples: - * Landroid/os/Looper;.loop:()V - * Landroid/app/ActivityThread;.main:([Ljava/lang/String;)V - * - * @param classAndMethodDesc the method to prepare - * - * @hide - */ - public static final boolean cacheRegisterMap(String classAndMethodDesc) { - return VMDebug.cacheRegisterMap(classAndMethodDesc); - } - - /** * Dumps the contents of VM reference tables (e.g. JNI locals and * globals) to the log file. * diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java index 7c42c36e7747..64ab1d711765 100644 --- a/core/java/android/os/HwBinder.java +++ b/core/java/android/os/HwBinder.java @@ -96,6 +96,15 @@ public abstract class HwBinder implements IHwBinder { throws RemoteException, NoSuchElementException; /** + * This allows getService to bypass the VINTF manifest for testing only. + * + * Disabled on user builds. + * @hide + */ + public static native final void setTrebleTestingOverride( + boolean testingOverride); + + /** * Configures how many threads the process-wide hwbinder threadpool * has to process incoming requests. * diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 994ed2148baa..337786734bb8 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -194,6 +194,13 @@ public class Process { */ public static final int FSVERITY_CERT_UID = 1075; + /** + * GID that corresponds to the INTERNET permission. + * Must match the value of AID_INET. + * @hide + */ + public static final int INET_GID = 3003; + /** {@hide} */ public static final int NOBODY_UID = 9999; diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index a95fe3c4043d..7946dda3170d 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -1331,8 +1331,7 @@ public final class Telephony { Object[] messages; try { messages = (Object[]) intent.getSerializableExtra("pdus"); - } - catch (ClassCastException e) { + } catch (ClassCastException e) { Rlog.e(TAG, "getMessagesFromIntent: " + e); return null; } @@ -1344,9 +1343,12 @@ public final class Telephony { String format = intent.getStringExtra("format"); int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, - SubscriptionManager.getDefaultSmsSubscriptionId()); - - Rlog.v(TAG, " getMessagesFromIntent sub_id : " + subId); + SubscriptionManager.INVALID_SUBSCRIPTION_ID); + if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + Rlog.v(TAG, "getMessagesFromIntent with valid subId : " + subId); + } else { + Rlog.v(TAG, "getMessagesFromIntent"); + } int pduCount = messages.length; SmsMessage[] msgs = new SmsMessage[pduCount]; diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index 5924421decce..3611d924b904 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -18,7 +18,6 @@ package android.telephony; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; -import android.annotation.TestApi; import android.content.Context; import android.os.Binder; import android.os.RemoteException; @@ -248,7 +247,6 @@ public class TelephonyRegistryManager { * @param incomingNumber incoming phone number. * @hide */ - @TestApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyCallStateChangedForAllSubscriptions(@CallState int state, @Nullable String incomingNumber) { diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java index 0863a813543c..4fe6752be4d5 100755 --- a/core/java/android/text/format/DateFormat.java +++ b/core/java/android/text/format/DateFormat.java @@ -19,12 +19,13 @@ package android.text.format; import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; +import android.icu.text.DateFormatSymbols; +import android.icu.text.DateTimePatternGenerator; import android.provider.Settings; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.SpannedString; -import libcore.icu.ICU; import libcore.icu.LocaleData; import java.text.SimpleDateFormat; @@ -251,7 +252,8 @@ public class DateFormat { * @return a string pattern suitable for use with {@link java.text.SimpleDateFormat}. */ public static String getBestDateTimePattern(Locale locale, String skeleton) { - return ICU.getBestDateTimePattern(skeleton, locale); + DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(locale); + return dtpg.getBestPattern(skeleton); } /** @@ -333,7 +335,52 @@ public class DateFormat { * order returned here. */ public static char[] getDateFormatOrder(Context context) { - return ICU.getDateFormatOrder(getDateFormatString(context)); + return getDateFormatOrder(getDateFormatString(context)); + } + + /** + * @hide Used by internal framework class {@link android.widget.DatePickerSpinnerDelegate}. + */ + public static char[] getDateFormatOrder(String pattern) { + char[] result = new char[3]; + int resultIndex = 0; + boolean sawDay = false; + boolean sawMonth = false; + boolean sawYear = false; + + for (int i = 0; i < pattern.length(); ++i) { + char ch = pattern.charAt(i); + if (ch == 'd' || ch == 'L' || ch == 'M' || ch == 'y') { + if (ch == 'd' && !sawDay) { + result[resultIndex++] = 'd'; + sawDay = true; + } else if ((ch == 'L' || ch == 'M') && !sawMonth) { + result[resultIndex++] = 'M'; + sawMonth = true; + } else if ((ch == 'y') && !sawYear) { + result[resultIndex++] = 'y'; + sawYear = true; + } + } else if (ch == 'G') { + // Ignore the era specifier, if present. + } else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { + throw new IllegalArgumentException("Bad pattern character '" + ch + "' in " + + pattern); + } else if (ch == '\'') { + if (i < pattern.length() - 1 && pattern.charAt(i + 1) == '\'') { + ++i; + } else { + i = pattern.indexOf('\'', i + 1); + if (i == -1) { + throw new IllegalArgumentException("Bad quoting in " + pattern); + } + ++i; + } + } else { + // Ignore spaces and punctuation. + } + } + return result; } private static String getDateFormatString(Context context) { @@ -429,6 +476,8 @@ public class DateFormat { int count; LocaleData localeData = LocaleData.get(Locale.getDefault()); + DateFormatSymbols dfs = getIcuDateFormatSymbols(Locale.getDefault()); + String[] amPm = dfs.getAmPmStrings(); int len = inFormat.length(); @@ -450,7 +499,7 @@ public class DateFormat { switch (c) { case 'A': case 'a': - replacement = localeData.amPm[inDate.get(Calendar.AM_PM) - Calendar.AM]; + replacement = amPm[inDate.get(Calendar.AM_PM) - Calendar.AM]; break; case 'd': replacement = zeroPad(inDate.get(Calendar.DATE), count); @@ -632,4 +681,16 @@ public class DateFormat { private static String zeroPad(int inValue, int inMinDigits) { return String.format(Locale.getDefault(), "%0" + inMinDigits + "d", inValue); } + + /** + * We use Gregorian calendar for date formats in android.text.format and various UI widget + * historically. It's a utility method to get an {@link DateFormatSymbols} instance. Note that + * {@link DateFormatSymbols} has cache, and external cache is not needed unless same instance is + * requested repeatedly in the performance critical code. + * + * @hide + */ + public static DateFormatSymbols getIcuDateFormatSymbols(Locale locale) { + return new DateFormatSymbols(android.icu.util.GregorianCalendar.class, locale); + } } diff --git a/core/java/android/text/format/DateIntervalFormat.java b/core/java/android/text/format/DateIntervalFormat.java new file mode 100644 index 000000000000..de9ec7ab9ea9 --- /dev/null +++ b/core/java/android/text/format/DateIntervalFormat.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2013 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.text.format; + +import static android.text.format.DateUtils.FORMAT_SHOW_TIME; +import static android.text.format.DateUtils.FORMAT_UTC; + +import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; + +import android.icu.util.Calendar; +import android.icu.util.ULocale; +import android.util.LruCache; + +import com.android.internal.annotations.VisibleForTesting; + +import java.text.FieldPosition; +import java.util.TimeZone; + +/** + * A wrapper of {@link android.icu.text.DateIntervalFormat} used by {@link DateUtilsBridge}. + * + * @hide + */ +@VisibleForTesting(visibility = PACKAGE) +public final class DateIntervalFormat { + + private static final LruCache<String, android.icu.text.DateIntervalFormat> CACHED_FORMATTERS = + new LruCache<>(8); + + private DateIntervalFormat() { + } + + /** + * Format a date range. + */ + @VisibleForTesting(visibility = PACKAGE) + public static String formatDateRange(long startMs, long endMs, int flags, String olsonId) { + if ((flags & FORMAT_UTC) != 0) { + olsonId = "UTC"; + } + // We create a java.util.TimeZone here to use libcore's data and libcore's olson ID / + // pseudo-tz logic. + TimeZone tz = (olsonId != null) ? TimeZone.getTimeZone(olsonId) : TimeZone.getDefault(); + android.icu.util.TimeZone icuTimeZone = DateUtilsBridge.icuTimeZone(tz); + ULocale icuLocale = ULocale.getDefault(); + return formatDateRange(icuLocale, icuTimeZone, startMs, endMs, flags); + } + + /** + * Format a date range. This is our slightly more sensible internal API. + * A truly sane replacement would take a skeleton instead of int flags. + */ + @VisibleForTesting(visibility = PACKAGE) + public static String formatDateRange(ULocale icuLocale, android.icu.util.TimeZone icuTimeZone, + long startMs, long endMs, int flags) { + Calendar startCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, startMs); + Calendar endCalendar; + if (startMs == endMs) { + endCalendar = startCalendar; + } else { + endCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, endMs); + } + + // Special handling when the range ends at midnight: + // - If we're not showing times, and the range is non-empty, we fudge the end date so we + // don't count the day that's about to start. + // - If we are showing times, and the range ends at exactly 00:00 of the day following + // its start (which can be thought of as 24:00 the same day), we fudge the end date so we + // don't show the dates --- unless the start is anything displayed as 00:00, in which case + // we include both dates to disambiguate. + // This is not the behavior of icu4j's DateIntervalFormat, but it's the required behavior + // of Android's DateUtils.formatDateRange. + if (isExactlyMidnight(endCalendar)) { + boolean showTime = (flags & FORMAT_SHOW_TIME) == FORMAT_SHOW_TIME; + boolean endsDayAfterStart = DateUtilsBridge.dayDistance(startCalendar, endCalendar) + == 1; + if ((!showTime && startMs != endMs) + || (endsDayAfterStart + && !DateUtilsBridge.isDisplayMidnightUsingSkeleton(startCalendar))) { + endCalendar.add(Calendar.DAY_OF_MONTH, -1); + } + } + + String skeleton = DateUtilsBridge.toSkeleton(startCalendar, endCalendar, flags); + synchronized (CACHED_FORMATTERS) { + android.icu.text.DateIntervalFormat formatter = + getFormatter(skeleton, icuLocale, icuTimeZone); + return formatter.format(startCalendar, endCalendar, new StringBuffer(), + new FieldPosition(0)).toString(); + } + } + + private static android.icu.text.DateIntervalFormat getFormatter(String skeleton, ULocale locale, + android.icu.util.TimeZone icuTimeZone) { + String key = skeleton + "\t" + locale + "\t" + icuTimeZone; + android.icu.text.DateIntervalFormat formatter = CACHED_FORMATTERS.get(key); + if (formatter != null) { + return formatter; + } + formatter = android.icu.text.DateIntervalFormat.getInstance(skeleton, locale); + formatter.setTimeZone(icuTimeZone); + CACHED_FORMATTERS.put(key, formatter); + return formatter; + } + + private static boolean isExactlyMidnight(Calendar c) { + return c.get(Calendar.HOUR_OF_DAY) == 0 + && c.get(Calendar.MINUTE) == 0 + && c.get(Calendar.SECOND) == 0 + && c.get(Calendar.MILLISECOND) == 0; + } +} diff --git a/core/java/android/text/format/DateTimeFormat.java b/core/java/android/text/format/DateTimeFormat.java new file mode 100644 index 000000000000..064d7172c44f --- /dev/null +++ b/core/java/android/text/format/DateTimeFormat.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2015 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.text.format; + +import android.icu.text.DateFormat; +import android.icu.text.DateTimePatternGenerator; +import android.icu.text.DisplayContext; +import android.icu.text.SimpleDateFormat; +import android.icu.util.Calendar; +import android.icu.util.ULocale; +import android.util.LruCache; + +/** + * A formatter that outputs a single date/time. + * + * @hide + */ +class DateTimeFormat { + private static final FormatterCache CACHED_FORMATTERS = new FormatterCache(); + + static class FormatterCache extends LruCache<String, DateFormat> { + FormatterCache() { + super(8); + } + } + + private DateTimeFormat() { + } + + public static String format(ULocale icuLocale, Calendar time, int flags, + DisplayContext displayContext) { + String skeleton = DateUtilsBridge.toSkeleton(time, flags); + String key = skeleton + "\t" + icuLocale + "\t" + time.getTimeZone(); + synchronized (CACHED_FORMATTERS) { + DateFormat formatter = CACHED_FORMATTERS.get(key); + if (formatter == null) { + DateTimePatternGenerator generator = DateTimePatternGenerator.getInstance( + icuLocale); + formatter = new SimpleDateFormat(generator.getBestPattern(skeleton), icuLocale); + CACHED_FORMATTERS.put(key, formatter); + } + formatter.setContext(displayContext); + return formatter.format(time); + } + } +} diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java index f236f19e973f..f313faec1756 100644 --- a/core/java/android/text/format/DateUtils.java +++ b/core/java/android/text/format/DateUtils.java @@ -27,9 +27,7 @@ import android.icu.util.MeasureUnit; import com.android.internal.R; -import libcore.icu.DateIntervalFormat; import libcore.icu.LocaleData; -import libcore.icu.RelativeDateTimeFormatter; import java.io.IOException; import java.util.Calendar; @@ -224,7 +222,8 @@ public class DateUtils */ @Deprecated public static String getAMPMString(int ampm) { - return LocaleData.get(Locale.getDefault()).amPm[ampm - Calendar.AM]; + String[] amPm = DateFormat.getIcuDateFormatSymbols(Locale.getDefault()).getAmPmStrings(); + return amPm[ampm - Calendar.AM]; } /** diff --git a/core/java/android/text/format/DateUtilsBridge.java b/core/java/android/text/format/DateUtilsBridge.java new file mode 100644 index 000000000000..92ec9cf6d736 --- /dev/null +++ b/core/java/android/text/format/DateUtilsBridge.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2015 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.text.format; + +import static android.text.format.DateUtils.FORMAT_12HOUR; +import static android.text.format.DateUtils.FORMAT_24HOUR; +import static android.text.format.DateUtils.FORMAT_ABBREV_ALL; +import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; +import static android.text.format.DateUtils.FORMAT_ABBREV_TIME; +import static android.text.format.DateUtils.FORMAT_ABBREV_WEEKDAY; +import static android.text.format.DateUtils.FORMAT_NO_MONTH_DAY; +import static android.text.format.DateUtils.FORMAT_NO_YEAR; +import static android.text.format.DateUtils.FORMAT_NUMERIC_DATE; +import static android.text.format.DateUtils.FORMAT_SHOW_DATE; +import static android.text.format.DateUtils.FORMAT_SHOW_TIME; +import static android.text.format.DateUtils.FORMAT_SHOW_WEEKDAY; +import static android.text.format.DateUtils.FORMAT_SHOW_YEAR; + +import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; + +import android.icu.util.Calendar; +import android.icu.util.GregorianCalendar; +import android.icu.util.TimeZone; +import android.icu.util.ULocale; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * Common methods and constants for the various ICU formatters used to support {@link + * android.text.format.DateUtils}. + * + * @hide + */ +@VisibleForTesting(visibility = PACKAGE) +public final class DateUtilsBridge { + + /** + * Creates an immutable ICU timezone backed by the specified libcore timezone data. At the time + * of writing the libcore implementation is faster but restricted to 1902 - 2038. Callers must + * not modify the {@code tz} after calling this method. + */ + public static TimeZone icuTimeZone(java.util.TimeZone tz) { + TimeZone icuTimeZone = TimeZone.getTimeZone(tz.getID()); + icuTimeZone.freeze(); // Optimization - allows the timezone to be copied cheaply. + return icuTimeZone; + } + + /** + * Create a GregorianCalendar based on the arguments + */ + public static Calendar createIcuCalendar(TimeZone icuTimeZone, ULocale icuLocale, + long timeInMillis) { + Calendar calendar = new GregorianCalendar(icuTimeZone, icuLocale); + calendar.setTimeInMillis(timeInMillis); + return calendar; + } + + public static String toSkeleton(Calendar calendar, int flags) { + return toSkeleton(calendar, calendar, flags); + } + + public static String toSkeleton(Calendar startCalendar, Calendar endCalendar, int flags) { + if ((flags & FORMAT_ABBREV_ALL) != 0) { + flags |= FORMAT_ABBREV_MONTH | FORMAT_ABBREV_TIME | FORMAT_ABBREV_WEEKDAY; + } + + String monthPart = "MMMM"; + if ((flags & FORMAT_NUMERIC_DATE) != 0) { + monthPart = "M"; + } else if ((flags & FORMAT_ABBREV_MONTH) != 0) { + monthPart = "MMM"; + } + + String weekPart = "EEEE"; + if ((flags & FORMAT_ABBREV_WEEKDAY) != 0) { + weekPart = "EEE"; + } + + String timePart = "j"; // "j" means choose 12 or 24 hour based on current locale. + if ((flags & FORMAT_24HOUR) != 0) { + timePart = "H"; + } else if ((flags & FORMAT_12HOUR) != 0) { + timePart = "h"; + } + + // If we've not been asked to abbreviate times, or we're using the 24-hour clock (where it + // never makes sense to leave out the minutes), include minutes. This gets us times like + // "4 PM" while avoiding times like "16" (for "16:00"). + if ((flags & FORMAT_ABBREV_TIME) == 0 || (flags & FORMAT_24HOUR) != 0) { + timePart += "m"; + } else { + // Otherwise, we're abbreviating a 12-hour time, and should only show the minutes + // if they're not both "00". + if (!(onTheHour(startCalendar) && onTheHour(endCalendar))) { + timePart = timePart + "m"; + } + } + + if (fallOnDifferentDates(startCalendar, endCalendar)) { + flags |= FORMAT_SHOW_DATE; + } + + if (fallInSameMonth(startCalendar, endCalendar) && (flags & FORMAT_NO_MONTH_DAY) != 0) { + flags &= (~FORMAT_SHOW_WEEKDAY); + flags &= (~FORMAT_SHOW_TIME); + } + + if ((flags & (FORMAT_SHOW_DATE | FORMAT_SHOW_TIME | FORMAT_SHOW_WEEKDAY)) == 0) { + flags |= FORMAT_SHOW_DATE; + } + + // If we've been asked to show the date, work out whether we think we should show the year. + if ((flags & FORMAT_SHOW_DATE) != 0) { + if ((flags & FORMAT_SHOW_YEAR) != 0) { + // The caller explicitly wants us to show the year. + } else if ((flags & FORMAT_NO_YEAR) != 0) { + // The caller explicitly doesn't want us to show the year, even if we otherwise + // would. + } else if (!fallInSameYear(startCalendar, endCalendar) || !isThisYear(startCalendar)) { + flags |= FORMAT_SHOW_YEAR; + } + } + + StringBuilder builder = new StringBuilder(); + if ((flags & (FORMAT_SHOW_DATE | FORMAT_NO_MONTH_DAY)) != 0) { + if ((flags & FORMAT_SHOW_YEAR) != 0) { + builder.append("y"); + } + builder.append(monthPart); + if ((flags & FORMAT_NO_MONTH_DAY) == 0) { + builder.append("d"); + } + } + if ((flags & FORMAT_SHOW_WEEKDAY) != 0) { + builder.append(weekPart); + } + if ((flags & FORMAT_SHOW_TIME) != 0) { + builder.append(timePart); + } + return builder.toString(); + } + + public static int dayDistance(Calendar c1, Calendar c2) { + return c2.get(Calendar.JULIAN_DAY) - c1.get(Calendar.JULIAN_DAY); + } + + /** + * Returns whether the argument will be displayed as if it were midnight, using any of the + * skeletons provided by {@link #toSkeleton}. + */ + public static boolean isDisplayMidnightUsingSkeleton(Calendar c) { + // All the skeletons returned by toSkeleton have minute precision (they may abbreviate + // 4:00 PM to 4 PM but will still show the following minute as 4:01 PM). + return c.get(Calendar.HOUR_OF_DAY) == 0 && c.get(Calendar.MINUTE) == 0; + } + + private static boolean onTheHour(Calendar c) { + return c.get(Calendar.MINUTE) == 0 && c.get(Calendar.SECOND) == 0; + } + + private static boolean fallOnDifferentDates(Calendar c1, Calendar c2) { + return c1.get(Calendar.YEAR) != c2.get(Calendar.YEAR) + || c1.get(Calendar.MONTH) != c2.get(Calendar.MONTH) + || c1.get(Calendar.DAY_OF_MONTH) != c2.get(Calendar.DAY_OF_MONTH); + } + + private static boolean fallInSameMonth(Calendar c1, Calendar c2) { + return c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH); + } + + private static boolean fallInSameYear(Calendar c1, Calendar c2) { + return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR); + } + + private static boolean isThisYear(Calendar c) { + Calendar now = (Calendar) c.clone(); + now.setTimeInMillis(System.currentTimeMillis()); + return c.get(Calendar.YEAR) == now.get(Calendar.YEAR); + } +} diff --git a/core/java/android/text/format/RelativeDateTimeFormatter.java b/core/java/android/text/format/RelativeDateTimeFormatter.java new file mode 100644 index 000000000000..9096469699c1 --- /dev/null +++ b/core/java/android/text/format/RelativeDateTimeFormatter.java @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2015 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.text.format; + +import static android.text.format.DateUtils.FORMAT_ABBREV_ALL; +import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; +import static android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE; +import static android.text.format.DateUtils.FORMAT_NO_YEAR; +import static android.text.format.DateUtils.FORMAT_NUMERIC_DATE; +import static android.text.format.DateUtils.FORMAT_SHOW_DATE; +import static android.text.format.DateUtils.FORMAT_SHOW_TIME; +import static android.text.format.DateUtils.FORMAT_SHOW_YEAR; + +import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; + +import android.icu.text.DisplayContext; +import android.icu.util.Calendar; +import android.icu.util.ULocale; +import android.util.LruCache; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.Locale; + +/** + * Exposes icu4j's RelativeDateTimeFormatter. + * + * @hide + */ +@VisibleForTesting(visibility = PACKAGE) +public final class RelativeDateTimeFormatter { + + public static final long SECOND_IN_MILLIS = 1000; + public static final long MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60; + public static final long HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60; + public static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24; + public static final long WEEK_IN_MILLIS = DAY_IN_MILLIS * 7; + // YEAR_IN_MILLIS considers 364 days as a year. However, since this + // constant comes from public API in DateUtils, it cannot be fixed here. + public static final long YEAR_IN_MILLIS = WEEK_IN_MILLIS * 52; + + private static final int DAY_IN_MS = 24 * 60 * 60 * 1000; + private static final int EPOCH_JULIAN_DAY = 2440588; + + private static final FormatterCache CACHED_FORMATTERS = new FormatterCache(); + + static class FormatterCache + extends LruCache<String, android.icu.text.RelativeDateTimeFormatter> { + FormatterCache() { + super(8); + } + } + + private RelativeDateTimeFormatter() { + } + + /** + * This is the internal API that implements the functionality of DateUtils + * .getRelativeTimeSpanString(long, + * long, long, int), which is to return a string describing 'time' as a time relative to 'now' + * such as '5 minutes ago', or 'In 2 days'. More examples can be found in DateUtils' doc. + * <p> + * In the implementation below, it selects the appropriate time unit based on the elapsed time + * between time' and 'now', e.g. minutes, days and etc. Callers may also specify the desired + * minimum resolution to show in the result. For example, '45 minutes ago' will become '0 hours + * ago' when minResolution is HOUR_IN_MILLIS. Once getting the quantity and unit to display, it + * calls icu4j's RelativeDateTimeFormatter to format the actual string according to the given + * locale. + * <p> + * Note that when minResolution is set to DAY_IN_MILLIS, it returns the result depending on the + * actual date difference. For example, it will return 'Yesterday' even if 'time' was less than + * 24 hours ago but falling onto a different calendar day. + * <p> + * It takes two additional parameters of Locale and TimeZone than the DateUtils' API. Caller + * must specify the locale and timezone. FORMAT_ABBREV_RELATIVE or FORMAT_ABBREV_ALL can be set + * in 'flags' to get the abbreviated forms when available. When 'time' equals to 'now', it + * always // returns a string like '0 seconds/minutes/... ago' according to minResolution. + */ + public static String getRelativeTimeSpanString(Locale locale, java.util.TimeZone tz, long time, + long now, long minResolution, int flags) { + // Android has been inconsistent about capitalization in the past. e.g. bug + // http://b/20247811. + // Now we capitalize everything consistently. + final DisplayContext displayContext = + DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE; + return getRelativeTimeSpanString(locale, tz, time, now, minResolution, flags, + displayContext); + } + + public static String getRelativeTimeSpanString(Locale locale, java.util.TimeZone tz, long time, + long now, long minResolution, int flags, DisplayContext displayContext) { + if (locale == null) { + throw new NullPointerException("locale == null"); + } + if (tz == null) { + throw new NullPointerException("tz == null"); + } + ULocale icuLocale = ULocale.forLocale(locale); + android.icu.util.TimeZone icuTimeZone = DateUtilsBridge.icuTimeZone(tz); + return getRelativeTimeSpanString(icuLocale, icuTimeZone, time, now, minResolution, flags, + displayContext); + } + + private static String getRelativeTimeSpanString(ULocale icuLocale, + android.icu.util.TimeZone icuTimeZone, long time, long now, long minResolution, + int flags, + DisplayContext displayContext) { + + long duration = Math.abs(now - time); + boolean past = (now >= time); + + android.icu.text.RelativeDateTimeFormatter.Style style; + if ((flags & (FORMAT_ABBREV_RELATIVE | FORMAT_ABBREV_ALL)) != 0) { + style = android.icu.text.RelativeDateTimeFormatter.Style.SHORT; + } else { + style = android.icu.text.RelativeDateTimeFormatter.Style.LONG; + } + + android.icu.text.RelativeDateTimeFormatter.Direction direction; + if (past) { + direction = android.icu.text.RelativeDateTimeFormatter.Direction.LAST; + } else { + direction = android.icu.text.RelativeDateTimeFormatter.Direction.NEXT; + } + + // 'relative' defaults to true as we are generating relative time span + // string. It will be set to false when we try to display strings without + // a quantity, such as 'Yesterday', etc. + boolean relative = true; + int count; + android.icu.text.RelativeDateTimeFormatter.RelativeUnit unit; + android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit aunit = null; + + if (duration < MINUTE_IN_MILLIS && minResolution < MINUTE_IN_MILLIS) { + count = (int) (duration / SECOND_IN_MILLIS); + unit = android.icu.text.RelativeDateTimeFormatter.RelativeUnit.SECONDS; + } else if (duration < HOUR_IN_MILLIS && minResolution < HOUR_IN_MILLIS) { + count = (int) (duration / MINUTE_IN_MILLIS); + unit = android.icu.text.RelativeDateTimeFormatter.RelativeUnit.MINUTES; + } else if (duration < DAY_IN_MILLIS && minResolution < DAY_IN_MILLIS) { + // Even if 'time' actually happened yesterday, we don't format it as + // "Yesterday" in this case. Unless the duration is longer than a day, + // or minResolution is specified as DAY_IN_MILLIS by user. + count = (int) (duration / HOUR_IN_MILLIS); + unit = android.icu.text.RelativeDateTimeFormatter.RelativeUnit.HOURS; + } else if (duration < WEEK_IN_MILLIS && minResolution < WEEK_IN_MILLIS) { + count = Math.abs(dayDistance(icuTimeZone, time, now)); + unit = android.icu.text.RelativeDateTimeFormatter.RelativeUnit.DAYS; + + if (count == 2) { + // Some locales have special terms for "2 days ago". Return them if + // available. Note that we cannot set up direction and unit here and + // make it fall through to use the call near the end of the function, + // because for locales that don't have special terms for "2 days ago", + // icu4j returns an empty string instead of falling back to strings + // like "2 days ago". + String str; + if (past) { + synchronized (CACHED_FORMATTERS) { + str = getFormatter(icuLocale, style, displayContext).format( + android.icu.text.RelativeDateTimeFormatter.Direction.LAST_2, + android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY); + } + } else { + synchronized (CACHED_FORMATTERS) { + str = getFormatter(icuLocale, style, displayContext).format( + android.icu.text.RelativeDateTimeFormatter.Direction.NEXT_2, + android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY); + } + } + if (str != null && !str.isEmpty()) { + return str; + } + // Fall back to show something like "2 days ago". + } else if (count == 1) { + // Show "Yesterday / Tomorrow" instead of "1 day ago / In 1 day". + aunit = android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY; + relative = false; + } else if (count == 0) { + // Show "Today" if time and now are on the same day. + aunit = android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY; + direction = android.icu.text.RelativeDateTimeFormatter.Direction.THIS; + relative = false; + } + } else if (minResolution == WEEK_IN_MILLIS) { + count = (int) (duration / WEEK_IN_MILLIS); + unit = android.icu.text.RelativeDateTimeFormatter.RelativeUnit.WEEKS; + } else { + Calendar timeCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, time); + // The duration is longer than a week and minResolution is not + // WEEK_IN_MILLIS. Return the absolute date instead of relative time. + + // Bug 19822016: + // If user doesn't supply the year display flag, we need to explicitly + // set that to show / hide the year based on time and now. Otherwise + // formatDateRange() would determine that based on the current system + // time and may give wrong results. + if ((flags & (FORMAT_NO_YEAR | FORMAT_SHOW_YEAR)) == 0) { + Calendar nowCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, + now); + + if (timeCalendar.get(Calendar.YEAR) != nowCalendar.get(Calendar.YEAR)) { + flags |= FORMAT_SHOW_YEAR; + } else { + flags |= FORMAT_NO_YEAR; + } + } + return DateTimeFormat.format(icuLocale, timeCalendar, flags, displayContext); + } + + synchronized (CACHED_FORMATTERS) { + android.icu.text.RelativeDateTimeFormatter formatter = + getFormatter(icuLocale, style, displayContext); + if (relative) { + return formatter.format(count, direction, unit); + } else { + return formatter.format(direction, aunit); + } + } + } + + /** + * This is the internal API that implements DateUtils.getRelativeDateTimeString(long, long, + * long, long, int), which is to return a string describing 'time' as a time relative to 'now', + * formatted like '[relative time/date], [time]'. More examples can be found in DateUtils' doc. + * <p> + * The function is similar to getRelativeTimeSpanString, but it always appends the absolute time + * to the relative time string to return '[relative time/date clause], [absolute time clause]'. + * It also takes an extra parameter transitionResolution to determine the format of the date + * clause. When the elapsed time is less than the transition resolution, it displays the + * relative time string. Otherwise, it gives the absolute numeric date string as the date + * clause. With the date and time clauses, it relies on icu4j's + * RelativeDateTimeFormatter::combineDateAndTime() + * to concatenate the two. + * <p> + * It takes two additional parameters of Locale and TimeZone than the DateUtils' API. Caller + * must specify the locale and timezone. FORMAT_ABBREV_RELATIVE or FORMAT_ABBREV_ALL can be set + * in 'flags' to get the abbreviated forms when they are available. + * <p> + * Bug 5252772: Since the absolute time will always be part of the result, minResolution will be + * set to at least DAY_IN_MILLIS to correctly indicate the date difference. For example, when + * it's 1:30 AM, it will return 'Yesterday, 11:30 PM' for getRelativeDateTimeString(null, null, + * now - 2 hours, now, HOUR_IN_MILLIS, DAY_IN_MILLIS, 0), instead of '2 hours ago, 11:30 PM' + * even with minResolution being HOUR_IN_MILLIS. + */ + public static String getRelativeDateTimeString(Locale locale, java.util.TimeZone tz, long time, + long now, long minResolution, long transitionResolution, int flags) { + + if (locale == null) { + throw new NullPointerException("locale == null"); + } + if (tz == null) { + throw new NullPointerException("tz == null"); + } + ULocale icuLocale = ULocale.forLocale(locale); + android.icu.util.TimeZone icuTimeZone = DateUtilsBridge.icuTimeZone(tz); + + long duration = Math.abs(now - time); + // It doesn't make much sense to have results like: "1 week ago, 10:50 AM". + if (transitionResolution > WEEK_IN_MILLIS) { + transitionResolution = WEEK_IN_MILLIS; + } + android.icu.text.RelativeDateTimeFormatter.Style style; + if ((flags & (FORMAT_ABBREV_RELATIVE | FORMAT_ABBREV_ALL)) != 0) { + style = android.icu.text.RelativeDateTimeFormatter.Style.SHORT; + } else { + style = android.icu.text.RelativeDateTimeFormatter.Style.LONG; + } + + Calendar timeCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, time); + Calendar nowCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, now); + + int days = Math.abs(DateUtilsBridge.dayDistance(timeCalendar, nowCalendar)); + + // Now get the date clause, either in relative format or the actual date. + String dateClause; + if (duration < transitionResolution) { + // This is to fix bug 5252772. If there is any date difference, we should + // promote the minResolution to DAY_IN_MILLIS so that it can display the + // date instead of "x hours/minutes ago, [time]". + if (days > 0 && minResolution < DAY_IN_MILLIS) { + minResolution = DAY_IN_MILLIS; + } + dateClause = getRelativeTimeSpanString(icuLocale, icuTimeZone, time, now, minResolution, + flags, DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE); + } else { + // We always use fixed flags to format the date clause. User-supplied + // flags are ignored. + if (timeCalendar.get(Calendar.YEAR) != nowCalendar.get(Calendar.YEAR)) { + // Different years + flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE; + } else { + // Default + flags = FORMAT_SHOW_DATE | FORMAT_NO_YEAR | FORMAT_ABBREV_MONTH; + } + + dateClause = DateTimeFormat.format(icuLocale, timeCalendar, flags, + DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE); + } + + String timeClause = DateTimeFormat.format(icuLocale, timeCalendar, FORMAT_SHOW_TIME, + DisplayContext.CAPITALIZATION_NONE); + + // icu4j also has other options available to control the capitalization. We are currently + // using + // the _NONE option only. + DisplayContext capitalizationContext = DisplayContext.CAPITALIZATION_NONE; + + // Combine the two clauses, such as '5 days ago, 10:50 AM'. + synchronized (CACHED_FORMATTERS) { + return getFormatter(icuLocale, style, capitalizationContext) + .combineDateAndTime(dateClause, timeClause); + } + } + + /** + * getFormatter() caches the RelativeDateTimeFormatter instances based on the combination of + * localeName, sytle and capitalizationContext. It should always be used along with the action + * of the formatter in a synchronized block, because otherwise the formatter returned by + * getFormatter() may have been evicted by the time of the call to formatter->action(). + */ + private static android.icu.text.RelativeDateTimeFormatter getFormatter( + ULocale locale, android.icu.text.RelativeDateTimeFormatter.Style style, + DisplayContext displayContext) { + String key = locale + "\t" + style + "\t" + displayContext; + android.icu.text.RelativeDateTimeFormatter formatter = CACHED_FORMATTERS.get(key); + if (formatter == null) { + formatter = android.icu.text.RelativeDateTimeFormatter.getInstance( + locale, null, style, displayContext); + CACHED_FORMATTERS.put(key, formatter); + } + return formatter; + } + + // Return the date difference for the two times in a given timezone. + private static int dayDistance(android.icu.util.TimeZone icuTimeZone, long startTime, + long endTime) { + return julianDay(icuTimeZone, endTime) - julianDay(icuTimeZone, startTime); + } + + private static int julianDay(android.icu.util.TimeZone icuTimeZone, long time) { + long utcMs = time + icuTimeZone.getOffset(time); + return (int) (utcMs / DAY_IN_MS) + EPOCH_JULIAN_DAY; + } +} diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java index 8e8409d15143..e7339380df69 100644 --- a/core/java/android/text/format/Time.java +++ b/core/java/android/text/format/Time.java @@ -18,8 +18,8 @@ package android.text.format; import android.util.TimeFormatException; -import libcore.timezone.ZoneInfoDb; -import libcore.util.ZoneInfo; +import com.android.i18n.timezone.ZoneInfoData; +import com.android.i18n.timezone.ZoneInfoDb; import java.util.Locale; import java.util.TimeZone; @@ -1059,15 +1059,15 @@ public class Time { * to the enclosing object, but others do not: thus separate state is retained. */ private static class TimeCalculator { - public final ZoneInfo.WallTime wallTime; + public final ZoneInfoData.WallTime wallTime; public String timezone; // Information about the current timezone. - private ZoneInfo zoneInfo; + private ZoneInfoData mZoneInfoData; public TimeCalculator(String timezoneId) { - this.zoneInfo = lookupZoneInfo(timezoneId); - this.wallTime = new ZoneInfo.WallTime(); + this.mZoneInfoData = lookupZoneInfoData(timezoneId); + this.wallTime = new ZoneInfoData.WallTime(); } public long toMillis(boolean ignoreDst) { @@ -1075,7 +1075,7 @@ public class Time { wallTime.setIsDst(-1); } - int r = wallTime.mktime(zoneInfo); + int r = wallTime.mktime(mZoneInfoData); if (r == -1) { return -1; } @@ -1087,7 +1087,7 @@ public class Time { int intSeconds = (int) (millis / 1000); updateZoneInfoFromTimeZone(); - wallTime.localtime(intSeconds, zoneInfo); + wallTime.localtime(intSeconds, mZoneInfoData); } public String format(String format) { @@ -1095,31 +1095,31 @@ public class Time { format = "%c"; } TimeFormatter formatter = new TimeFormatter(); - return formatter.format(format, wallTime, zoneInfo); + return formatter.format(format, wallTime, mZoneInfoData); } private void updateZoneInfoFromTimeZone() { - if (!zoneInfo.getID().equals(timezone)) { - this.zoneInfo = lookupZoneInfo(timezone); + if (!mZoneInfoData.getID().equals(timezone)) { + this.mZoneInfoData = lookupZoneInfoData(timezone); } } - private static ZoneInfo lookupZoneInfo(String timezoneId) { - ZoneInfo zoneInfo = ZoneInfoDb.getInstance().makeTimeZone(timezoneId); - if (zoneInfo == null) { - zoneInfo = ZoneInfoDb.getInstance().makeTimeZone("GMT"); + private static ZoneInfoData lookupZoneInfoData(String timezoneId) { + ZoneInfoData zoneInfoData = ZoneInfoDb.getInstance().makeZoneInfoData(timezoneId); + if (zoneInfoData == null) { + zoneInfoData = ZoneInfoDb.getInstance().makeZoneInfoData("GMT"); } - if (zoneInfo == null) { + if (zoneInfoData == null) { throw new AssertionError("GMT not found: \"" + timezoneId + "\""); } - return zoneInfo; + return zoneInfoData; } public void switchTimeZone(String timezone) { - int seconds = wallTime.mktime(zoneInfo); + int seconds = wallTime.mktime(mZoneInfoData); this.timezone = timezone; updateZoneInfoFromTimeZone(); - wallTime.localtime(seconds, zoneInfo); + wallTime.localtime(seconds, mZoneInfoData); } public String format2445(boolean hasTime) { diff --git a/core/java/android/text/format/TimeFormatter.java b/core/java/android/text/format/TimeFormatter.java index f7fd89d7d819..9393f36d1b6f 100644 --- a/core/java/android/text/format/TimeFormatter.java +++ b/core/java/android/text/format/TimeFormatter.java @@ -21,9 +21,11 @@ package android.text.format; import android.content.res.Resources; +import android.icu.text.DateFormatSymbols; + +import com.android.i18n.timezone.ZoneInfoData; import libcore.icu.LocaleData; -import libcore.util.ZoneInfo; import java.nio.CharBuffer; import java.time.Instant; @@ -54,11 +56,13 @@ class TimeFormatter { * The Locale for which the cached LocaleData and formats have been loaded. */ private static Locale sLocale; + private static DateFormatSymbols sDateFormatSymbols; private static LocaleData sLocaleData; private static String sTimeOnlyFormat; private static String sDateOnlyFormat; private static String sDateTimeFormat; + private final DateFormatSymbols dateFormatSymbols; private final LocaleData localeData; private final String dateTimeFormat; private final String timeOnlyFormat; @@ -73,6 +77,7 @@ class TimeFormatter { if (sLocale == null || !(locale.equals(sLocale))) { sLocale = locale; + sDateFormatSymbols = DateFormat.getIcuDateFormatSymbols(locale); sLocaleData = LocaleData.get(locale); Resources r = Resources.getSystem(); @@ -81,6 +86,7 @@ class TimeFormatter { sDateTimeFormat = r.getString(com.android.internal.R.string.date_and_time); } + this.dateFormatSymbols = sDateFormatSymbols; this.dateTimeFormat = sDateTimeFormat; this.timeOnlyFormat = sTimeOnlyFormat; this.dateOnlyFormat = sDateOnlyFormat; @@ -94,8 +100,8 @@ class TimeFormatter { * incorrect digit localization behavior. */ String formatMillisWithFixedFormat(long timeMillis) { - // This method is deliberately not a general purpose replacement for - // format(String, ZoneInfo.WallTime, ZoneInfo): It hard-codes the pattern used; many of the + // This method is deliberately not a general purpose replacement for format(String, + // ZoneInfoData.WallTime, ZoneInfoData): It hard-codes the pattern used; many of the // pattern characters supported by Time.format() have unusual behavior which would make // using java.time.format or similar packages difficult. It would be a lot of work to share // behavior and many internal Android usecases can be covered by this common pattern @@ -144,7 +150,8 @@ class TimeFormatter { /** * Format the specified {@code wallTime} using {@code pattern}. The output is returned. */ - public String format(String pattern, ZoneInfo.WallTime wallTime, ZoneInfo zoneInfo) { + public String format(String pattern, ZoneInfoData.WallTime wallTime, + ZoneInfoData zoneInfoData) { try { StringBuilder stringBuilder = new StringBuilder(); @@ -153,7 +160,7 @@ class TimeFormatter { // and locale sensitive strings are output directly using outputBuilder. numberFormatter = new Formatter(stringBuilder, Locale.US); - formatInternal(pattern, wallTime, zoneInfo); + formatInternal(pattern, wallTime, zoneInfoData); String result = stringBuilder.toString(); // The localizeDigits() behavior is the source of a bug since some formats are defined // as being in ASCII and not localized. @@ -186,13 +193,14 @@ class TimeFormatter { * Format the specified {@code wallTime} using {@code pattern}. The output is written to * {@link #outputBuilder}. */ - private void formatInternal(String pattern, ZoneInfo.WallTime wallTime, ZoneInfo zoneInfo) { + private void formatInternal(String pattern, ZoneInfoData.WallTime wallTime, + ZoneInfoData zoneInfoData) { CharBuffer formatBuffer = CharBuffer.wrap(pattern); while (formatBuffer.remaining() > 0) { boolean outputCurrentChar = true; char currentChar = formatBuffer.get(formatBuffer.position()); if (currentChar == '%') { - outputCurrentChar = handleToken(formatBuffer, wallTime, zoneInfo); + outputCurrentChar = handleToken(formatBuffer, wallTime, zoneInfoData); } if (outputCurrentChar) { outputBuilder.append(formatBuffer.get(formatBuffer.position())); @@ -201,8 +209,8 @@ class TimeFormatter { } } - private boolean handleToken(CharBuffer formatBuffer, ZoneInfo.WallTime wallTime, - ZoneInfo zoneInfo) { + private boolean handleToken(CharBuffer formatBuffer, ZoneInfoData.WallTime wallTime, + ZoneInfoData zoneInfoData) { // The char at formatBuffer.position() is expected to be '%' at this point. int modifier = 0; @@ -247,10 +255,10 @@ class TimeFormatter { outputYear(wallTime.getYear(), true, false, modifier); return false; case 'c': - formatInternal(dateTimeFormat, wallTime, zoneInfo); + formatInternal(dateTimeFormat, wallTime, zoneInfoData); return false; case 'D': - formatInternal("%m/%d/%y", wallTime, zoneInfo); + formatInternal("%m/%d/%y", wallTime, zoneInfoData); return false; case 'd': numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), @@ -272,7 +280,7 @@ class TimeFormatter { wallTime.getMonthDay()); return false; case 'F': - formatInternal("%Y-%m-%d", wallTime, zoneInfo); + formatInternal("%Y-%m-%d", wallTime, zoneInfoData); return false; case 'H': numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), @@ -307,29 +315,31 @@ class TimeFormatter { outputBuilder.append('\n'); return false; case 'p': - modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) ? localeData.amPm[1] - : localeData.amPm[0], modifier); + modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) + ? dateFormatSymbols.getAmPmStrings()[1] + : dateFormatSymbols.getAmPmStrings()[0], modifier); return false; case 'P': - modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) ? localeData.amPm[1] - : localeData.amPm[0], FORCE_LOWER_CASE); + modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) + ? dateFormatSymbols.getAmPmStrings()[1] + : dateFormatSymbols.getAmPmStrings()[0], FORCE_LOWER_CASE); return false; case 'R': - formatInternal("%H:%M", wallTime, zoneInfo); + formatInternal("%H:%M", wallTime, zoneInfoData); return false; case 'r': - formatInternal("%I:%M:%S %p", wallTime, zoneInfo); + formatInternal("%I:%M:%S %p", wallTime, zoneInfoData); return false; case 'S': numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), wallTime.getSecond()); return false; case 's': - int timeInSeconds = wallTime.mktime(zoneInfo); + int timeInSeconds = wallTime.mktime(zoneInfoData); outputBuilder.append(Integer.toString(timeInSeconds)); return false; case 'T': - formatInternal("%H:%M:%S", wallTime, zoneInfo); + formatInternal("%H:%M:%S", wallTime, zoneInfoData); return false; case 't': outputBuilder.append('\t'); @@ -383,7 +393,7 @@ class TimeFormatter { return false; } case 'v': - formatInternal("%e-%b-%Y", wallTime, zoneInfo); + formatInternal("%e-%b-%Y", wallTime, zoneInfoData); return false; case 'W': int n = (wallTime.getYearDay() + DAYSPERWEEK - ( @@ -395,10 +405,10 @@ class TimeFormatter { numberFormatter.format("%d", wallTime.getWeekDay()); return false; case 'X': - formatInternal(timeOnlyFormat, wallTime, zoneInfo); + formatInternal(timeOnlyFormat, wallTime, zoneInfoData); return false; case 'x': - formatInternal(dateOnlyFormat, wallTime, zoneInfo); + formatInternal(dateOnlyFormat, wallTime, zoneInfoData); return false; case 'y': outputYear(wallTime.getYear(), false, true, modifier); @@ -411,7 +421,8 @@ class TimeFormatter { return false; } boolean isDst = wallTime.getIsDst() != 0; - modifyAndAppend(zoneInfo.getDisplayName(isDst, TimeZone.SHORT), modifier); + modifyAndAppend(TimeZone.getTimeZone(zoneInfoData.getID()) + .getDisplayName(isDst, TimeZone.SHORT), modifier); return false; case 'z': { if (wallTime.getIsDst() < 0) { @@ -432,7 +443,7 @@ class TimeFormatter { return false; } case '+': - formatInternal("%a %b %e %H:%M:%S %Z %Y", wallTime, zoneInfo); + formatInternal("%a %b %e %H:%M:%S %Z %Y", wallTime, zoneInfoData); return false; case '%': // If conversion char is undefined, behavior is undefined. Print out the diff --git a/core/java/android/text/method/NumberKeyListener.java b/core/java/android/text/method/NumberKeyListener.java index d40015ee17a8..2b038dd11348 100644 --- a/core/java/android/text/method/NumberKeyListener.java +++ b/core/java/android/text/method/NumberKeyListener.java @@ -29,8 +29,6 @@ import android.text.format.DateFormat; import android.view.KeyEvent; import android.view.View; -import libcore.icu.LocaleData; - import java.util.Collection; import java.util.Locale; @@ -228,7 +226,7 @@ public abstract class NumberKeyListener extends BaseKeyListener if (locale == null) { return false; } - final String[] amPm = LocaleData.get(locale).amPm; + final String[] amPm = DateFormat.getIcuDateFormatSymbols(locale).getAmPmStrings(); for (int i = 0; i < amPm.length; i++) { for (int j = 0; j < amPm[i].length(); j++) { final char ch = amPm[i].charAt(j); diff --git a/core/java/android/timezone/CountryTimeZones.java b/core/java/android/timezone/CountryTimeZones.java index 8fd303b8a64c..44d140242409 100644 --- a/core/java/android/timezone/CountryTimeZones.java +++ b/core/java/android/timezone/CountryTimeZones.java @@ -40,9 +40,9 @@ public final class CountryTimeZones { public static final class TimeZoneMapping { @NonNull - private libcore.timezone.CountryTimeZones.TimeZoneMapping mDelegate; + private com.android.i18n.timezone.CountryTimeZones.TimeZoneMapping mDelegate; - TimeZoneMapping(libcore.timezone.CountryTimeZones.TimeZoneMapping delegate) { + TimeZoneMapping(com.android.i18n.timezone.CountryTimeZones.TimeZoneMapping delegate) { this.mDelegate = Objects.requireNonNull(delegate); } @@ -147,9 +147,9 @@ public final class CountryTimeZones { } @NonNull - private final libcore.timezone.CountryTimeZones mDelegate; + private final com.android.i18n.timezone.CountryTimeZones mDelegate; - CountryTimeZones(libcore.timezone.CountryTimeZones delegate) { + CountryTimeZones(com.android.i18n.timezone.CountryTimeZones delegate) { mDelegate = delegate; } @@ -221,7 +221,7 @@ public final class CountryTimeZones { @Nullable public OffsetResult lookupByOffsetWithBias(long whenMillis, @Nullable TimeZone bias, int totalOffsetMillis, boolean isDst) { - libcore.timezone.CountryTimeZones.OffsetResult delegateOffsetResult = + com.android.i18n.timezone.CountryTimeZones.OffsetResult delegateOffsetResult = mDelegate.lookupByOffsetWithBias( whenMillis, bias, totalOffsetMillis, isDst); return delegateOffsetResult == null ? null : @@ -244,7 +244,7 @@ public final class CountryTimeZones { @Nullable public OffsetResult lookupByOffsetWithBias(long whenMillis, @Nullable TimeZone bias, int totalOffsetMillis) { - libcore.timezone.CountryTimeZones.OffsetResult delegateOffsetResult = + com.android.i18n.timezone.CountryTimeZones.OffsetResult delegateOffsetResult = mDelegate.lookupByOffsetWithBias(whenMillis, bias, totalOffsetMillis); return delegateOffsetResult == null ? null : new OffsetResult( @@ -260,11 +260,12 @@ public final class CountryTimeZones { */ @NonNull public List<TimeZoneMapping> getEffectiveTimeZoneMappingsAt(long whenMillis) { - List<libcore.timezone.CountryTimeZones.TimeZoneMapping> delegateList = + List<com.android.i18n.timezone.CountryTimeZones.TimeZoneMapping> delegateList = mDelegate.getEffectiveTimeZoneMappingsAt(whenMillis); List<TimeZoneMapping> toReturn = new ArrayList<>(delegateList.size()); - for (libcore.timezone.CountryTimeZones.TimeZoneMapping delegateMapping : delegateList) { + for (com.android.i18n.timezone.CountryTimeZones.TimeZoneMapping delegateMapping + : delegateList) { toReturn.add(new TimeZoneMapping(delegateMapping)); } return Collections.unmodifiableList(toReturn); diff --git a/core/java/android/timezone/TelephonyLookup.java b/core/java/android/timezone/TelephonyLookup.java index a4c3fbd33410..c97bf28cebfe 100644 --- a/core/java/android/timezone/TelephonyLookup.java +++ b/core/java/android/timezone/TelephonyLookup.java @@ -41,16 +41,17 @@ public final class TelephonyLookup { public static TelephonyLookup getInstance() { synchronized (sLock) { if (sInstance == null) { - sInstance = new TelephonyLookup(libcore.timezone.TelephonyLookup.getInstance()); + sInstance = new TelephonyLookup(com.android.i18n.timezone.TelephonyLookup + .getInstance()); } return sInstance; } } @NonNull - private final libcore.timezone.TelephonyLookup mDelegate; + private final com.android.i18n.timezone.TelephonyLookup mDelegate; - private TelephonyLookup(@NonNull libcore.timezone.TelephonyLookup delegate) { + private TelephonyLookup(@NonNull com.android.i18n.timezone.TelephonyLookup delegate) { mDelegate = Objects.requireNonNull(delegate); } @@ -60,7 +61,7 @@ public final class TelephonyLookup { */ @Nullable public TelephonyNetworkFinder getTelephonyNetworkFinder() { - libcore.timezone.TelephonyNetworkFinder telephonyNetworkFinderDelegate = + com.android.i18n.timezone.TelephonyNetworkFinder telephonyNetworkFinderDelegate = mDelegate.getTelephonyNetworkFinder(); return telephonyNetworkFinderDelegate != null ? new TelephonyNetworkFinder(telephonyNetworkFinderDelegate) : null; diff --git a/core/java/android/timezone/TelephonyNetwork.java b/core/java/android/timezone/TelephonyNetwork.java index 823cd251fbf0..3b65c6ffc379 100644 --- a/core/java/android/timezone/TelephonyNetwork.java +++ b/core/java/android/timezone/TelephonyNetwork.java @@ -28,9 +28,9 @@ import java.util.Objects; public final class TelephonyNetwork { @NonNull - private final libcore.timezone.TelephonyNetwork mDelegate; + private final com.android.i18n.timezone.TelephonyNetwork mDelegate; - TelephonyNetwork(@NonNull libcore.timezone.TelephonyNetwork delegate) { + TelephonyNetwork(@NonNull com.android.i18n.timezone.TelephonyNetwork delegate) { mDelegate = Objects.requireNonNull(delegate); } diff --git a/core/java/android/timezone/TelephonyNetworkFinder.java b/core/java/android/timezone/TelephonyNetworkFinder.java index 4bfeff8a73ad..c69ddf86d3f8 100644 --- a/core/java/android/timezone/TelephonyNetworkFinder.java +++ b/core/java/android/timezone/TelephonyNetworkFinder.java @@ -29,9 +29,9 @@ import java.util.Objects; public final class TelephonyNetworkFinder { @NonNull - private final libcore.timezone.TelephonyNetworkFinder mDelegate; + private final com.android.i18n.timezone.TelephonyNetworkFinder mDelegate; - TelephonyNetworkFinder(libcore.timezone.TelephonyNetworkFinder delegate) { + TelephonyNetworkFinder(com.android.i18n.timezone.TelephonyNetworkFinder delegate) { mDelegate = Objects.requireNonNull(delegate); } @@ -45,7 +45,7 @@ public final class TelephonyNetworkFinder { Objects.requireNonNull(mcc); Objects.requireNonNull(mnc); - libcore.timezone.TelephonyNetwork telephonyNetworkDelegate = + com.android.i18n.timezone.TelephonyNetwork telephonyNetworkDelegate = mDelegate.findNetworkByMccMnc(mcc, mnc); return telephonyNetworkDelegate != null ? new TelephonyNetwork(telephonyNetworkDelegate) : null; diff --git a/core/java/android/timezone/TimeZoneFinder.java b/core/java/android/timezone/TimeZoneFinder.java index 03f5013f230c..bf4275fb5b56 100644 --- a/core/java/android/timezone/TimeZoneFinder.java +++ b/core/java/android/timezone/TimeZoneFinder.java @@ -41,16 +41,17 @@ public final class TimeZoneFinder { public static TimeZoneFinder getInstance() { synchronized (sLock) { if (sInstance == null) { - sInstance = new TimeZoneFinder(libcore.timezone.TimeZoneFinder.getInstance()); + sInstance = new TimeZoneFinder(com.android.i18n.timezone.TimeZoneFinder + .getInstance()); } } return sInstance; } @NonNull - private final libcore.timezone.TimeZoneFinder mDelegate; + private final com.android.i18n.timezone.TimeZoneFinder mDelegate; - private TimeZoneFinder(@NonNull libcore.timezone.TimeZoneFinder delegate) { + private TimeZoneFinder(@NonNull com.android.i18n.timezone.TimeZoneFinder delegate) { mDelegate = Objects.requireNonNull(delegate); } @@ -70,7 +71,8 @@ public final class TimeZoneFinder { */ @Nullable public CountryTimeZones lookupCountryTimeZones(@NonNull String countryIso) { - libcore.timezone.CountryTimeZones delegate = mDelegate.lookupCountryTimeZones(countryIso); + com.android.i18n.timezone.CountryTimeZones delegate = mDelegate + .lookupCountryTimeZones(countryIso); return delegate == null ? null : new CountryTimeZones(delegate); } } diff --git a/core/java/android/timezone/TzDataSetVersion.java b/core/java/android/timezone/TzDataSetVersion.java index f993012aeb1c..e1fb932b977d 100644 --- a/core/java/android/timezone/TzDataSetVersion.java +++ b/core/java/android/timezone/TzDataSetVersion.java @@ -50,14 +50,14 @@ public final class TzDataSetVersion { * Returns the major tz data format version supported by this device. */ public static int currentFormatMajorVersion() { - return libcore.timezone.TzDataSetVersion.currentFormatMajorVersion(); + return com.android.i18n.timezone.TzDataSetVersion.currentFormatMajorVersion(); } /** * Returns the minor tz data format version supported by this device. */ public static int currentFormatMinorVersion() { - return libcore.timezone.TzDataSetVersion.currentFormatMinorVersion(); + return com.android.i18n.timezone.TzDataSetVersion.currentFormatMinorVersion(); } /** @@ -65,7 +65,7 @@ public final class TzDataSetVersion { * with the current system image, and set of active modules. */ public static boolean isCompatibleWithThisDevice(TzDataSetVersion tzDataSetVersion) { - return libcore.timezone.TzDataSetVersion.isCompatibleWithThisDevice( + return com.android.i18n.timezone.TzDataSetVersion.isCompatibleWithThisDevice( tzDataSetVersion.mDelegate); } @@ -76,8 +76,8 @@ public final class TzDataSetVersion { public static TzDataSetVersion read() throws IOException, TzDataSetException { try { return new TzDataSetVersion( - libcore.timezone.TzDataSetVersion.readTimeZoneModuleVersion()); - } catch (libcore.timezone.TzDataSetVersion.TzDataSetException e) { + com.android.i18n.timezone.TzDataSetVersion.readTimeZoneModuleVersion()); + } catch (com.android.i18n.timezone.TzDataSetVersion.TzDataSetException e) { throw new TzDataSetException(e.getMessage(), e); } } @@ -100,9 +100,9 @@ public final class TzDataSetVersion { } @NonNull - private final libcore.timezone.TzDataSetVersion mDelegate; + private final com.android.i18n.timezone.TzDataSetVersion mDelegate; - private TzDataSetVersion(@NonNull libcore.timezone.TzDataSetVersion delegate) { + private TzDataSetVersion(@NonNull com.android.i18n.timezone.TzDataSetVersion delegate) { mDelegate = Objects.requireNonNull(delegate); } diff --git a/core/java/android/timezone/ZoneInfoDb.java b/core/java/android/timezone/ZoneInfoDb.java index 9354a695812d..65d6eaddf824 100644 --- a/core/java/android/timezone/ZoneInfoDb.java +++ b/core/java/android/timezone/ZoneInfoDb.java @@ -41,16 +41,16 @@ public final class ZoneInfoDb { public static ZoneInfoDb getInstance() { synchronized (sLock) { if (sInstance == null) { - sInstance = new ZoneInfoDb(libcore.timezone.ZoneInfoDb.getInstance()); + sInstance = new ZoneInfoDb(com.android.i18n.timezone.ZoneInfoDb.getInstance()); } } return sInstance; } @NonNull - private final libcore.timezone.ZoneInfoDb mDelegate; + private final com.android.i18n.timezone.ZoneInfoDb mDelegate; - private ZoneInfoDb(libcore.timezone.ZoneInfoDb delegate) { + private ZoneInfoDb(com.android.i18n.timezone.ZoneInfoDb delegate) { mDelegate = Objects.requireNonNull(delegate); } diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java index 6e41fc8017c1..384040013a6d 100644 --- a/core/java/android/util/TimeUtils.java +++ b/core/java/android/util/TimeUtils.java @@ -23,10 +23,10 @@ import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.SystemClock; -import libcore.timezone.CountryTimeZones; -import libcore.timezone.CountryTimeZones.TimeZoneMapping; -import libcore.timezone.TimeZoneFinder; -import libcore.timezone.ZoneInfoDb; +import com.android.i18n.timezone.CountryTimeZones; +import com.android.i18n.timezone.CountryTimeZones.TimeZoneMapping; +import com.android.i18n.timezone.TimeZoneFinder; +import com.android.i18n.timezone.ZoneInfoDb; import java.io.PrintWriter; import java.text.SimpleDateFormat; diff --git a/core/java/android/widget/DatePickerSpinnerDelegate.java b/core/java/android/widget/DatePickerSpinnerDelegate.java index 096e6ea52c8a..fd89b2e09131 100644 --- a/core/java/android/widget/DatePickerSpinnerDelegate.java +++ b/core/java/android/widget/DatePickerSpinnerDelegate.java @@ -34,8 +34,6 @@ import android.view.inputmethod.InputMethodManager; import android.widget.DatePicker.AbstractDatePickerDelegate; import android.widget.NumberPicker.OnValueChangeListener; -import libcore.icu.ICU; - import java.text.DateFormatSymbols; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -459,7 +457,7 @@ class DatePickerSpinnerDelegate extends AbstractDatePickerDelegate { // We use numeric spinners for year and day, but textual months. Ask icu4c what // order the user's locale uses for that combination. http://b/7207103. String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), "yyyyMMMdd"); - char[] order = ICU.getDateFormatOrder(pattern); + char[] order = DateFormat.getDateFormatOrder(pattern); final int spinnerCount = order.length; for (int i = 0; i < spinnerCount; i++) { switch (order[i]) { diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java index d0f80936f477..da2039560306 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -105,16 +105,43 @@ public final class SelectionActionModeHelper { } /** + * Swap the selection index if the start index is greater than end index. + * + * @return the swap result, index 0 is the start index and index 1 is the end index. + */ + private static int[] sortSelectionIndices(int selectionStart, int selectionEnd) { + if (selectionStart < selectionEnd) { + return new int[]{selectionStart, selectionEnd}; + } + return new int[]{selectionEnd, selectionStart}; + } + + /** + * The {@link TextView} selection start and end index may not be sorted, this method will swap + * the {@link TextView} selection index if the start index is greater than end index. + * + * @param textView the selected TextView. + * @return the swap result, index 0 is the start index and index 1 is the end index. + */ + private static int[] sortSelectionIndicesFromTextView(TextView textView) { + int selectionStart = textView.getSelectionStart(); + int selectionEnd = textView.getSelectionEnd(); + + return sortSelectionIndices(selectionStart, selectionEnd); + } + + /** * Starts Selection ActionMode. */ public void startSelectionActionModeAsync(boolean adjustSelection) { // Check if the smart selection should run for editable text. adjustSelection &= getTextClassificationSettings().isSmartSelectionEnabled(); + int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView); mSelectionTracker.onOriginalSelection( getText(mTextView), - mTextView.getSelectionStart(), - mTextView.getSelectionEnd(), + sortedSelectionIndices[0], + sortedSelectionIndices[1], false /*isLink*/); cancelAsyncTask(); if (skipTextClassification()) { @@ -139,12 +166,14 @@ public final class SelectionActionModeHelper { * Starts Link ActionMode. */ public void startLinkActionModeAsync(int start, int end) { - mSelectionTracker.onOriginalSelection(getText(mTextView), start, end, true /*isLink*/); + int[] indexResult = sortSelectionIndices(start, end); + mSelectionTracker.onOriginalSelection(getText(mTextView), indexResult[0], indexResult[1], + true /*isLink*/); cancelAsyncTask(); if (skipTextClassification()) { startLinkActionMode(null); } else { - resetTextClassificationHelper(start, end); + resetTextClassificationHelper(indexResult[0], indexResult[1]); mTextClassificationAsyncTask = new TextClassificationAsyncTask( mTextView, mTextClassificationHelper.getTimeoutDuration(), @@ -173,19 +202,23 @@ public final class SelectionActionModeHelper { /** Reports a selection action event. */ public void onSelectionAction(int menuItemId, @Nullable String actionLabel) { + int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView); mSelectionTracker.onSelectionAction( - mTextView.getSelectionStart(), mTextView.getSelectionEnd(), + sortedSelectionIndices[0], sortedSelectionIndices[1], getActionType(menuItemId), actionLabel, mTextClassification); } public void onSelectionDrag() { + int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView); mSelectionTracker.onSelectionAction( - mTextView.getSelectionStart(), mTextView.getSelectionEnd(), + sortedSelectionIndices[0], sortedSelectionIndices[1], SelectionEvent.ACTION_DRAG, /* actionLabel= */ null, mTextClassification); } public void onTextChanged(int start, int end) { - mSelectionTracker.onTextChanged(start, end, mTextClassification); + int[] sortedSelectionIndices = sortSelectionIndices(start, end); + mSelectionTracker.onTextChanged(sortedSelectionIndices[0], sortedSelectionIndices[1], + mTextClassification); } public boolean resetSelection(int textIndex) { @@ -302,9 +335,10 @@ public final class SelectionActionModeHelper { startSelectionActionMode(startSelectionResult); }; // TODO do not trigger the animation if the change included only non-printable characters + int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView); final boolean didSelectionChange = - result != null && (mTextView.getSelectionStart() != result.mStart - || mTextView.getSelectionEnd() != result.mEnd); + result != null && (sortedSelectionIndices[0] != result.mStart + || sortedSelectionIndices[1] != result.mEnd); if (!didSelectionChange) { onAnimationEndCallback.run(); @@ -454,16 +488,18 @@ public final class SelectionActionModeHelper { if (actionMode != null) { actionMode.invalidate(); } + final int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView); mSelectionTracker.onSelectionUpdated( - mTextView.getSelectionStart(), mTextView.getSelectionEnd(), mTextClassification); + sortedSelectionIndices[0], sortedSelectionIndices[1], mTextClassification); mTextClassificationAsyncTask = null; } private void resetTextClassificationHelper(int selectionStart, int selectionEnd) { if (selectionStart < 0 || selectionEnd < 0) { // Use selection indices - selectionStart = mTextView.getSelectionStart(); - selectionEnd = mTextView.getSelectionEnd(); + int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView); + selectionStart = sortedSelectionIndices[0]; + selectionEnd = sortedSelectionIndices[1]; } mTextClassificationHelper.init( mTextView::getTextClassifier, @@ -603,10 +639,11 @@ public final class SelectionActionModeHelper { mAllowReset = false; boolean selected = editor.selectCurrentWord(); if (selected) { - mSelectionStart = editor.getTextView().getSelectionStart(); - mSelectionEnd = editor.getTextView().getSelectionEnd(); + final int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(textView); + mSelectionStart = sortedSelectionIndices[0]; + mSelectionEnd = sortedSelectionIndices[1]; mLogger.logSelectionAction( - textView.getSelectionStart(), textView.getSelectionEnd(), + sortedSelectionIndices[0], sortedSelectionIndices[1], SelectionEvent.ACTION_RESET, /* actionLabel= */ null, /* classification= */ null); } @@ -1179,8 +1216,9 @@ public final class SelectionActionModeHelper { SelectionResult(int start, int end, @Nullable TextClassification classification, @Nullable TextSelection selection) { - mStart = start; - mEnd = end; + int[] sortedIndices = sortSelectionIndices(start, end); + mStart = sortedIndices[0]; + mEnd = sortedIndices[1]; mClassification = classification; mSelection = selection; } diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java index 51b18473f1ac..1c219eb95479 100644 --- a/core/java/android/widget/TimePicker.java +++ b/core/java/android/widget/TimePicker.java @@ -24,9 +24,11 @@ import android.annotation.Widget; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.TypedArray; +import android.icu.text.DateFormatSymbols; import android.icu.util.Calendar; import android.os.Parcel; import android.os.Parcelable; +import android.text.format.DateFormat; import android.util.AttributeSet; import android.util.Log; import android.util.MathUtils; @@ -39,8 +41,6 @@ import android.view.inspector.InspectableProperty; import com.android.internal.R; -import libcore.icu.LocaleData; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Locale; @@ -421,11 +421,13 @@ public class TimePicker extends FrameLayout { static String[] getAmPmStrings(Context context) { final Locale locale = context.getResources().getConfiguration().locale; - final LocaleData d = LocaleData.get(locale); + DateFormatSymbols dfs = DateFormat.getIcuDateFormatSymbols(locale); + String[] amPm = dfs.getAmPmStrings(); + String[] narrowAmPm = dfs.getAmpmNarrowStrings(); final String[] result = new String[2]; - result[0] = d.amPm[0].length() > 4 ? d.narrowAm : d.amPm[0]; - result[1] = d.amPm[1].length() > 4 ? d.narrowPm : d.amPm[1]; + result[0] = amPm[0].length() > 4 ? narrowAmPm[0] : amPm[0]; + result[1] = amPm[1].length() > 4 ? narrowAmPm[1] : amPm[1]; return result; } diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java index 83c86d5ce36b..bd2fa5965bc9 100644 --- a/core/java/android/widget/TimePickerSpinnerDelegate.java +++ b/core/java/android/widget/TimePickerSpinnerDelegate.java @@ -35,8 +35,6 @@ import android.view.inputmethod.InputMethodManager; import com.android.internal.R; -import libcore.icu.LocaleData; - import java.util.Calendar; /** @@ -143,7 +141,7 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT); // Get the localized am/pm strings and use them in the spinner. - mAmPmStrings = getAmPmStrings(context); + mAmPmStrings = TimePicker.getAmPmStrings(context); // am/pm final View amPmView = mDelegator.findViewById(R.id.amPm); @@ -574,12 +572,4 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { target.setContentDescription(mContext.getString(contDescResId)); } } - - public static String[] getAmPmStrings(Context context) { - String[] result = new String[2]; - LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale); - result[0] = d.amPm[0].length() > 4 ? d.narrowAm : d.amPm[0]; - result[1] = d.amPm[1].length() > 4 ? d.narrowPm : d.amPm[1]; - return result; - } } diff --git a/core/java/com/android/internal/logging/AndroidHandler.java b/core/java/com/android/internal/logging/AndroidHandler.java index f55a31fcc986..119f3662a94f 100644 --- a/core/java/com/android/internal/logging/AndroidHandler.java +++ b/core/java/com/android/internal/logging/AndroidHandler.java @@ -17,9 +17,8 @@ package com.android.internal.logging; import android.util.Log; + import com.android.internal.util.FastPrintWriter; -import dalvik.system.DalvikLogging; -import dalvik.system.DalvikLogHandler; import java.io.PrintWriter; import java.io.StringWriter; @@ -82,7 +81,7 @@ import java.util.logging.Logger; * </tr> * </table> */ -public class AndroidHandler extends Handler implements DalvikLogHandler { +public class AndroidHandler extends Handler { /** * Holds the formatter for all Android log handlers. */ @@ -121,10 +120,32 @@ public class AndroidHandler extends Handler implements DalvikLogHandler { // No need to flush, but must implement abstract method. } + /** + * Returns the short logger tag (up to 23 chars) for the given logger name. + * Traditionally loggers are named by fully-qualified Java classes; this + * method attempts to return a concise identifying part of such names. + */ + private static String loggerNameToTag(String loggerName) { + // Anonymous logger. + if (loggerName == null) { + return "null"; + } + + int length = loggerName.length(); + if (length <= 23) { + return loggerName; + } + + int lastPeriod = loggerName.lastIndexOf("."); + return length - (lastPeriod + 1) <= 23 + ? loggerName.substring(lastPeriod + 1) + : loggerName.substring(loggerName.length() - 23); + } + @Override public void publish(LogRecord record) { int level = getAndroidLevel(record.getLevel()); - String tag = DalvikLogging.loggerNameToTag(record.getLoggerName()); + String tag = loggerNameToTag(record.getLoggerName()); if (!Log.isLoggable(tag, level)) { return; } diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java index 8ea5aa815a1c..b4727499d8ef 100644 --- a/core/java/com/android/internal/net/VpnProfile.java +++ b/core/java/com/android/internal/net/VpnProfile.java @@ -21,6 +21,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.net.Ikev2VpnProfile; import android.net.PlatformVpnProfile; import android.net.ProxyInfo; +import android.net.Uri; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -287,7 +288,7 @@ public final class VpnProfile implements Cloneable, Parcelable { profile.proxy = new ProxyInfo(host, port.isEmpty() ? 0 : Integer.parseInt(port), exclList); } else if (!pacFileUrl.isEmpty()) { - profile.proxy = new ProxyInfo(pacFileUrl); + profile.proxy = new ProxyInfo(Uri.parse(pacFileUrl)); } } // else profile.proxy = null diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java index cffb0ad9fdb9..3d35d2fbaa82 100644 --- a/core/java/com/android/internal/os/KernelWakelockReader.java +++ b/core/java/com/android/internal/os/KernelWakelockReader.java @@ -153,19 +153,32 @@ public class KernelWakelockReader { } /** + * Attempt to wait for suspend_control service if not immediately available. + */ + private ISuspendControlService waitForSuspendControlService() throws ServiceNotFoundException { + final String name = "suspend_control"; + final int numRetries = 5; + for (int i = 0; i < numRetries; i++) { + mSuspendControlService = ISuspendControlService.Stub.asInterface( + ServiceManager.getService(name)); + if (mSuspendControlService != null) { + return mSuspendControlService; + } + } + throw new ServiceNotFoundException(name); + } + + /** * On success, returns the updated stats from SystemSupend, else returns null. */ private KernelWakelockStats getWakelockStatsFromSystemSuspend( final KernelWakelockStats staleStats) { WakeLockInfo[] wlStats = null; - if (mSuspendControlService == null) { - try { - mSuspendControlService = ISuspendControlService.Stub.asInterface( - ServiceManager.getServiceOrThrow("suspend_control")); - } catch (ServiceNotFoundException e) { - Slog.wtf(TAG, "Required service suspend_control not available", e); - return null; - } + try { + mSuspendControlService = waitForSuspendControlService(); + } catch (ServiceNotFoundException e) { + Slog.wtf(TAG, "Required service suspend_control not available", e); + return null; } try { diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index f3c3ac1b94e5..2a29dfb71e6c 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -24,6 +24,7 @@ import android.content.pm.ApplicationInfo; import android.net.Credentials; import android.net.LocalServerSocket; import android.net.LocalSocket; +import android.net.NetworkUtils; import android.os.FactoryTest; import android.os.IVold; import android.os.Process; @@ -239,6 +240,13 @@ public final class Zygote { private Zygote() {} + private static boolean containsInetGid(int[] gids) { + for (int i = 0; i < gids.length; i++) { + if (gids[i] == android.os.Process.INET_GID) return true; + } + return false; + } + /** * Forks a new VM instance. The current VM must have been started * with the -Xzygote flag. <b>NOTE: new instance keeps all @@ -286,6 +294,11 @@ public final class Zygote { if (pid == 0) { // Note that this event ends at the end of handleChildProc, Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork"); + + // If no GIDs were specified, don't make any permissions changes based on groups. + if (gids != null && gids.length > 0) { + NetworkUtils.setAllowNetworkingForProcess(containsInetGid(gids)); + } } // Set the Java Language thread priority to the default value for new apps. diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java index 0c2406559dcc..7a79cc9ef868 100644 --- a/core/java/com/android/internal/util/StateMachine.java +++ b/core/java/com/android/internal/util/StateMachine.java @@ -2088,10 +2088,11 @@ public class StateMachine { pw.println(getName() + ":"); pw.println(" total records=" + getLogRecCount()); for (int i = 0; i < getLogRecSize(); i++) { - pw.println(" rec[" + i + "]: " + getLogRec(i).toString()); + pw.println(" rec[" + i + "]: " + getLogRec(i)); pw.flush(); } - pw.println("curState=" + getCurrentState().getName()); + final IState curState = getCurrentState(); + pw.println("curState=" + (curState == null ? "<QUIT>" : curState.getName())); } @Override diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java index 37365111b0c5..bcb0460ace0f 100644 --- a/core/java/com/android/internal/widget/PointerLocationView.java +++ b/core/java/com/android/internal/widget/PointerLocationView.java @@ -371,7 +371,7 @@ public class PointerLocationView extends View implements InputDeviceListener, } if (haveLast) { canvas.drawLine(lastX, lastY, x, y, mPathPaint); - final Paint paint = ps.mTraceCurrent[i] ? mCurrentPointPaint : mPaint; + final Paint paint = ps.mTraceCurrent[i - 1] ? mCurrentPointPaint : mPaint; canvas.drawPoint(lastX, lastY, paint); drawn = true; } diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index f040f11ffac4..8cc1b8161de9 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -22,7 +22,7 @@ #include "core_jni_helpers.h" #include <HardwareBitmapUploader.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <androidfw/Asset.h> #include <androidfw/ResourceTypes.h> #include <cutils/compiler.h> diff --git a/core/jni/android_app_ActivityThread.cpp b/core/jni/android_app_ActivityThread.cpp index fc88193a8502..002f36cf586d 100644 --- a/core/jni/android_app_ActivityThread.cpp +++ b/core/jni/android_app_ActivityThread.cpp @@ -15,8 +15,9 @@ */ #include "jni.h" + #include "GraphicsJNI.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <minikin/Layout.h> #include <renderthread/RenderProxy.h> diff --git a/core/jni/android_backup_BackupDataInput.cpp b/core/jni/android_backup_BackupDataInput.cpp index aa8acc16bc3f..79fa2a28666c 100644 --- a/core/jni/android_backup_BackupDataInput.cpp +++ b/core/jni/android_backup_BackupDataInput.cpp @@ -17,7 +17,7 @@ #define LOG_TAG "FileBackupHelper_native" #include <utils/Log.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <android_runtime/AndroidRuntime.h> #include <androidfw/BackupHelpers.h> diff --git a/core/jni/android_backup_BackupDataOutput.cpp b/core/jni/android_backup_BackupDataOutput.cpp index 4f5d1f80dffc..324b3e71b8cc 100644 --- a/core/jni/android_backup_BackupDataOutput.cpp +++ b/core/jni/android_backup_BackupDataOutput.cpp @@ -17,7 +17,7 @@ #define LOG_TAG "FileBackupHelper_native" #include <utils/Log.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include "core_jni_helpers.h" #include <androidfw/BackupHelpers.h> diff --git a/core/jni/android_backup_BackupHelperDispatcher.cpp b/core/jni/android_backup_BackupHelperDispatcher.cpp index fac7eba4c0a2..efe7d0b6ad0a 100644 --- a/core/jni/android_backup_BackupHelperDispatcher.cpp +++ b/core/jni/android_backup_BackupHelperDispatcher.cpp @@ -17,7 +17,7 @@ #define LOG_TAG "BackupHelperDispatcher_native" #include <utils/Log.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <android_runtime/AndroidRuntime.h> #include <sys/types.h> diff --git a/core/jni/android_backup_FileBackupHelperBase.cpp b/core/jni/android_backup_FileBackupHelperBase.cpp index 65840ee2c016..5f84fafa383e 100644 --- a/core/jni/android_backup_FileBackupHelperBase.cpp +++ b/core/jni/android_backup_FileBackupHelperBase.cpp @@ -17,7 +17,7 @@ #define LOG_TAG "FileBackupHelper_native" #include <utils/Log.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include "core_jni_helpers.h" #include <androidfw/BackupHelpers.h> diff --git a/core/jni/android_hardware_SerialPort.cpp b/core/jni/android_hardware_SerialPort.cpp index 3ff24468e3aa..d06107e2e0b2 100644 --- a/core/jni/android_hardware_SerialPort.cpp +++ b/core/jni/android_hardware_SerialPort.cpp @@ -19,7 +19,7 @@ #include "utils/Log.h" #include "jni.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include "core_jni_helpers.h" #include <stdio.h> diff --git a/core/jni/android_hardware_UsbDeviceConnection.cpp b/core/jni/android_hardware_UsbDeviceConnection.cpp index b885c285e380..845d65c67719 100644 --- a/core/jni/android_hardware_UsbDeviceConnection.cpp +++ b/core/jni/android_hardware_UsbDeviceConnection.cpp @@ -19,7 +19,7 @@ #include "utils/Log.h" #include "jni.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include "core_jni_helpers.h" #include <usbhost/usbhost.h> diff --git a/core/jni/android_net_LocalSocketImpl.cpp b/core/jni/android_net_LocalSocketImpl.cpp index 1163b860977d..42cf1f479122 100644 --- a/core/jni/android_net_LocalSocketImpl.cpp +++ b/core/jni/android_net_LocalSocketImpl.cpp @@ -16,7 +16,7 @@ #define LOG_TAG "LocalSocketImpl" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include "jni.h" #include "utils/Log.h" #include "utils/misc.h" diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp index 03b9793ccba8..e56809f66dc7 100644 --- a/core/jni/android_net_NetUtils.cpp +++ b/core/jni/android_net_NetUtils.cpp @@ -30,7 +30,7 @@ #include <DnsProxydProtocol.h> // NETID_USE_LOCAL_NAMESERVERS #include <android_runtime/AndroidRuntime.h> #include <cutils/properties.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <nativehelper/ScopedLocalRef.h> #include <utils/Log.h> #include <utils/misc.h> @@ -226,6 +226,11 @@ static jobject android_net_utils_getDnsNetwork(JNIEnv *env, jobject thiz) { class_Network, ctor, dnsNetId & ~NETID_USE_LOCAL_NAMESERVERS, privateDnsBypass); } +static void android_net_utils_setAllowNetworkingForProcess(JNIEnv *env, jobject thiz, + jboolean hasConnectivity) { + setAllowNetworkingForProcess(hasConnectivity == JNI_TRUE); +} + static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, jobject javaFd) { if (javaFd == NULL) { jniThrowNullPointerException(env, NULL); @@ -266,6 +271,7 @@ static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, j /* * JNI registration. */ +// clang-format off static const JNINativeMethod gNetworkUtilMethods[] = { /* name, signature, funcPtr */ { "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork }, @@ -282,7 +288,9 @@ static const JNINativeMethod gNetworkUtilMethods[] = { { "resNetworkResult", "(Ljava/io/FileDescriptor;)Landroid/net/DnsResolver$DnsResponse;", (void*) android_net_utils_resNetworkResult }, { "resNetworkCancel", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_resNetworkCancel }, { "getDnsNetwork", "()Landroid/net/Network;", (void*) android_net_utils_getDnsNetwork }, + { "setAllowNetworkingForProcess", "(Z)V", (void *)android_net_utils_setAllowNetworkingForProcess }, }; +// clang-format on int register_android_net_NetworkUtils(JNIEnv* env) { diff --git a/core/jni/android_opengl_EGL14.cpp b/core/jni/android_opengl_EGL14.cpp index de5e3a52a0c1..2f29cae42d07 100644 --- a/core/jni/android_opengl_EGL14.cpp +++ b/core/jni/android_opengl_EGL14.cpp @@ -20,7 +20,7 @@ #pragma GCC diagnostic ignored "-Wunused-function" #include "jni.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <android_runtime/AndroidRuntime.h> #include <android_runtime/android_view_Surface.h> #include <android_runtime/android_graphics_SurfaceTexture.h> diff --git a/core/jni/android_opengl_EGL15.cpp b/core/jni/android_opengl_EGL15.cpp index 4aeed8765165..b9c36b9ed9aa 100644 --- a/core/jni/android_opengl_EGL15.cpp +++ b/core/jni/android_opengl_EGL15.cpp @@ -20,7 +20,7 @@ #pragma GCC diagnostic ignored "-Wunused-function" #include "jni.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <android_runtime/AndroidRuntime.h> #include <utils/misc.h> diff --git a/core/jni/android_opengl_EGLExt.cpp b/core/jni/android_opengl_EGLExt.cpp index fef8116dc93a..1758807304bf 100644 --- a/core/jni/android_opengl_EGLExt.cpp +++ b/core/jni/android_opengl_EGLExt.cpp @@ -20,7 +20,7 @@ #pragma GCC diagnostic ignored "-Wunused-function" #include "jni.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <android_runtime/AndroidRuntime.h> #include <android_runtime/android_view_Surface.h> #include <android_runtime/android_graphics_SurfaceTexture.h> diff --git a/core/jni/android_opengl_GLES10.cpp b/core/jni/android_opengl_GLES10.cpp index 3d9a3b6687b1..e4d138d92621 100644 --- a/core/jni/android_opengl_GLES10.cpp +++ b/core/jni/android_opengl_GLES10.cpp @@ -24,7 +24,7 @@ #include <GLES/glext.h> #include <jni.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <android_runtime/AndroidRuntime.h> #include <utils/misc.h> #include <assert.h> diff --git a/core/jni/android_opengl_GLES10Ext.cpp b/core/jni/android_opengl_GLES10Ext.cpp index 6d7f41e5b3b2..3638b87e201f 100644 --- a/core/jni/android_opengl_GLES10Ext.cpp +++ b/core/jni/android_opengl_GLES10Ext.cpp @@ -24,7 +24,7 @@ #include <GLES/glext.h> #include <jni.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <android_runtime/AndroidRuntime.h> #include <utils/misc.h> #include <assert.h> diff --git a/core/jni/android_opengl_GLES11.cpp b/core/jni/android_opengl_GLES11.cpp index 39ef41a0e19e..1069a1d3acb1 100644 --- a/core/jni/android_opengl_GLES11.cpp +++ b/core/jni/android_opengl_GLES11.cpp @@ -24,7 +24,7 @@ #include <GLES/glext.h> #include <jni.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <android_runtime/AndroidRuntime.h> #include <utils/misc.h> #include <assert.h> diff --git a/core/jni/android_opengl_GLES11Ext.cpp b/core/jni/android_opengl_GLES11Ext.cpp index 1144d5bfb4e6..86d7ecdce44d 100644 --- a/core/jni/android_opengl_GLES11Ext.cpp +++ b/core/jni/android_opengl_GLES11Ext.cpp @@ -24,7 +24,7 @@ #include <GLES/glext.h> #include <jni.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <android_runtime/AndroidRuntime.h> #include <utils/misc.h> #include <assert.h> diff --git a/core/jni/android_opengl_GLES20.cpp b/core/jni/android_opengl_GLES20.cpp index 2add72d14c7b..49baa51f2342 100644 --- a/core/jni/android_opengl_GLES20.cpp +++ b/core/jni/android_opengl_GLES20.cpp @@ -24,7 +24,7 @@ #include <GLES2/gl2ext.h> #include <jni.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <android_runtime/AndroidRuntime.h> #include <utils/misc.h> #include <assert.h> diff --git a/core/jni/android_opengl_GLES30.cpp b/core/jni/android_opengl_GLES30.cpp index a9c021951758..32a2a24c2d2d 100644 --- a/core/jni/android_opengl_GLES30.cpp +++ b/core/jni/android_opengl_GLES30.cpp @@ -24,7 +24,7 @@ #include <GLES3/gl3ext.h> #include <jni.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <android_runtime/AndroidRuntime.h> #include <utils/misc.h> #include <assert.h> diff --git a/core/jni/android_opengl_GLES31.cpp b/core/jni/android_opengl_GLES31.cpp index 456da93784d5..afe7c63b6d47 100644 --- a/core/jni/android_opengl_GLES31.cpp +++ b/core/jni/android_opengl_GLES31.cpp @@ -22,7 +22,7 @@ #include <stdint.h> #include <GLES3/gl31.h> #include <jni.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <android_runtime/AndroidRuntime.h> #include <utils/misc.h> #include <assert.h> diff --git a/core/jni/android_opengl_GLES31Ext.cpp b/core/jni/android_opengl_GLES31Ext.cpp index dcaf4a58cd53..81274331ffa4 100644 --- a/core/jni/android_opengl_GLES31Ext.cpp +++ b/core/jni/android_opengl_GLES31Ext.cpp @@ -23,7 +23,7 @@ #include <GLES2/gl2ext.h> #include <jni.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <android_runtime/AndroidRuntime.h> #include <utils/misc.h> #include <assert.h> diff --git a/core/jni/android_opengl_GLES32.cpp b/core/jni/android_opengl_GLES32.cpp index 6bdc711d64a2..07a794d0ef19 100644 --- a/core/jni/android_opengl_GLES32.cpp +++ b/core/jni/android_opengl_GLES32.cpp @@ -22,7 +22,7 @@ #include <stdint.h> #include <GLES3/gl32.h> #include <jni.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <android_runtime/AndroidRuntime.h> #include <utils/misc.h> #include <assert.h> diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 8f9f8d211596..cd3611546852 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -40,7 +40,7 @@ #include <utils/misc.h> #include <utils/String8.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <nativehelper/ScopedUtfChars.h> #include "jni.h" #include <dmabufinfo/dmabufinfo.h> diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp index b6427c9aa01c..48f33a6a3d77 100644 --- a/core/jni/android_os_HwBinder.cpp +++ b/core/jni/android_os_HwBinder.cpp @@ -339,6 +339,10 @@ static jobject JHwBinder_native_getService( return JHwRemoteBinder::NewObject(env, service); } +void JHwBinder_native_setTrebleTestingOverride(JNIEnv*, jclass, jboolean testingOverride) { + hardware::details::setTrebleTestingOverride(testingOverride); +} + void JHwBinder_native_configureRpcThreadpool(JNIEnv *, jclass, jlong maxThreads, jboolean callerWillJoin) { CHECK(maxThreads > 0); @@ -368,6 +372,9 @@ static JNINativeMethod gMethods[] = { { "getService", "(Ljava/lang/String;Ljava/lang/String;Z)L" PACKAGE_PATH "/IHwBinder;", (void *)JHwBinder_native_getService }, + { "setTrebleTestingOverride", "(Z)V", + (void *)JHwBinder_native_setTrebleTestingOverride }, + { "configureRpcThreadpool", "(JZ)V", (void *)JHwBinder_native_configureRpcThreadpool }, diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp index a88f8919ed08..ff336ee64b54 100644 --- a/core/jni/android_os_HwParcel.cpp +++ b/core/jni/android_os_HwParcel.cpp @@ -122,10 +122,18 @@ void signalExceptionForError(JNIEnv *env, status_t err, bool canThrowRemoteExcep std::stringstream ss; ss << "HwBinder Error: (" << err << ")"; - jniThrowException( - env, - canThrowRemoteException ? "android/os/RemoteException" : "java/lang/RuntimeException", - ss.str().c_str()); + const char* exception = nullptr; + if (canThrowRemoteException) { + if (err == DEAD_OBJECT) { + exception = "android/os/DeadObjectException"; + } else { + exception = "android/os/RemoteException"; + } + } else { + exception = "java/lang/RuntimeException"; + } + + jniThrowException(env, exception, ss.str().c_str()); break; } diff --git a/core/jni/android_os_MemoryFile.cpp b/core/jni/android_os_MemoryFile.cpp index b21566bbc186..8d91e635454c 100644 --- a/core/jni/android_os_MemoryFile.cpp +++ b/core/jni/android_os_MemoryFile.cpp @@ -19,7 +19,7 @@ #include <cutils/ashmem.h> #include "core_jni_helpers.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <unistd.h> #include <sys/mman.h> diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp index eb8d26e7bf71..7247ee068334 100644 --- a/core/jni/android_os_Parcel.cpp +++ b/core/jni/android_os_Parcel.cpp @@ -20,7 +20,7 @@ #include "android_os_Parcel.h" #include "android_util_Binder.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <fcntl.h> #include <stdio.h> diff --git a/core/jni/android_os_SELinux.cpp b/core/jni/android_os_SELinux.cpp index 7634547ae0bf..b772dfd1eb14 100644 --- a/core/jni/android_os_SELinux.cpp +++ b/core/jni/android_os_SELinux.cpp @@ -21,7 +21,7 @@ #include <utils/Log.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include "jni.h" #include "core_jni_helpers.h" #include "selinux/selinux.h" diff --git a/core/jni/android_os_SharedMemory.cpp b/core/jni/android_os_SharedMemory.cpp index a812c35036f3..dc86187d8fea 100644 --- a/core/jni/android_os_SharedMemory.cpp +++ b/core/jni/android_os_SharedMemory.cpp @@ -22,7 +22,7 @@ #include <utils/Log.h> #include <nativehelper/jni_macros.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <nativehelper/ScopedLocalRef.h> #include <algorithm> diff --git a/core/jni/android_server_NetworkManagementSocketTagger.cpp b/core/jni/android_server_NetworkManagementSocketTagger.cpp index 58295af50135..afad08a87d37 100644 --- a/core/jni/android_server_NetworkManagementSocketTagger.cpp +++ b/core/jni/android_server_NetworkManagementSocketTagger.cpp @@ -17,7 +17,7 @@ #define LOG_TAG "NMST_QTagUidNative" #include <utils/Log.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include "jni.h" #include <utils/misc.h> diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index 9342088cef3f..6fd9c7ef9bdc 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -48,7 +48,7 @@ #include "core_jni_helpers.h" #include "jni.h" -#include "nativehelper/JNIHelp.h" +#include "nativehelper/JNIPlatformHelp.h" #include "nativehelper/ScopedPrimitiveArray.h" #include "nativehelper/ScopedStringChars.h" #include "nativehelper/ScopedUtfChars.h" diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 18be3741e805..fbb35e055e16 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -95,6 +95,19 @@ #include "nativebridge/native_bridge.h" +/* Functions in the callchain during the fork shall not be protected with + Armv8.3-A Pointer Authentication, otherwise child will not be able to return. */ +#ifdef __ARM_FEATURE_PAC_DEFAULT +#ifdef __ARM_FEATURE_BTI_DEFAULT +#define NO_PAC_FUNC __attribute__((target("branch-protection=bti"))) +#else +#define NO_PAC_FUNC __attribute__((target("branch-protection=none"))) +#endif /* __ARM_FEATURE_BTI_DEFAULT */ +#else /* !__ARM_FEATURE_PAC_DEFAULT */ +#define NO_PAC_FUNC +#endif /* __ARM_FEATURE_PAC_DEFAULT */ + + namespace { // TODO (chriswailes): Add a function to initialize native Zygote data. @@ -985,7 +998,23 @@ static void ClearUsapTable() { gUsapPoolCount = 0; } +NO_PAC_FUNC +static void PAuthKeyChange(JNIEnv* env) { +#ifdef __aarch64__ + unsigned long int hwcaps = getauxval(AT_HWCAP); + if (hwcaps & HWCAP_PACA) { + const unsigned long key_mask = PR_PAC_APIAKEY | PR_PAC_APIBKEY | + PR_PAC_APDAKEY | PR_PAC_APDBKEY | PR_PAC_APGAKEY; + if (prctl(PR_PAC_RESET_KEYS, key_mask, 0, 0, 0) != 0) { + ALOGE("Failed to change the PAC keys: %s", strerror(errno)); + RuntimeAbort(env, __LINE__, "PAC key change failed."); + } + } +#endif +} + // Utility routine to fork a process from the zygote. +NO_PAC_FUNC static pid_t ForkCommon(JNIEnv* env, bool is_system_server, const std::vector<int>& fds_to_close, const std::vector<int>& fds_to_ignore, @@ -1036,6 +1065,7 @@ static pid_t ForkCommon(JNIEnv* env, bool is_system_server, } // The child process. + PAuthKeyChange(env); PreApplicationInit(); // Clean up any descriptors which must be closed immediately @@ -1486,6 +1516,7 @@ static void com_android_internal_os_Zygote_nativePreApplicationInit(JNIEnv*, jcl PreApplicationInit(); } +NO_PAC_FUNC static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( JNIEnv* env, jclass, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, @@ -1533,6 +1564,7 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( return pid; } +NO_PAC_FUNC static jint com_android_internal_os_Zygote_nativeForkSystemServer( JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities, @@ -1600,6 +1632,7 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer( * @param is_priority_fork Controls the nice level assigned to the newly created process * @return */ +NO_PAC_FUNC static jint com_android_internal_os_Zygote_nativeForkUsap(JNIEnv* env, jclass, jint read_pipe_fd, diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp index 6b893cb94444..ffc1ddc03355 100644 --- a/core/jni/com_google_android_gles_jni_GLImpl.cpp +++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp @@ -21,7 +21,7 @@ #pragma GCC diagnostic ignored "-Wunused-function" #include "jni.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <android_runtime/AndroidRuntime.h> #include <utils/misc.h> diff --git a/core/jni/core_jni_helpers.h b/core/jni/core_jni_helpers.h index f03f42737134..41ccd56925aa 100644 --- a/core/jni/core_jni_helpers.h +++ b/core/jni/core_jni_helpers.h @@ -17,7 +17,7 @@ #ifndef CORE_JNI_HELPERS #define CORE_JNI_HELPERS -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <nativehelper/scoped_local_ref.h> #include <nativehelper/scoped_utf_chars.h> #include <android_runtime/AndroidRuntime.h> diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index 59797f7f4bf6..51266de83355 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -2422,6 +2422,11 @@ enum PageId { // OS: Q SETTINGS_GESTURE_TAP = 1751; + // OPEN: Settings > Security & screen lock -> Encryption & credentials > Install a certificate + // CATEGORY: SETTINGS + // OS: R + INSTALL_CERTIFICATE_FROM_STORAGE = 1803; + // OPEN: Settings > Developer Options > Platform Compat // CATEGORY: SETTINGS // OS: R diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index cc988ff94926..85a224206346 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -228,6 +228,8 @@ <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED" /> <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY" /> <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY" /> + <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_READ_STATUS_CHANGED" /> + <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_DELETED_STATUS_CHANGED" /> <protected-broadcast android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_SENT" /> <protected-broadcast diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index ad1f1f005baa..3d73ade33d5b 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3064,6 +3064,11 @@ empty string is passed in --> <string name="config_wlan_data_service_package" translatable="false"></string> + <!-- Boolean indicating whether the Iwlan data service supports persistence of iwlan ipsec + tunnels across service restart. If iwlan tunnels are not persisted across restart, + Framework will clean up dangling data connections when service restarts --> + <bool name="config_wlan_data_service_conn_persistence_on_restart">true</bool> + <!-- Cellular data service class name to bind to by default. If none is specified in an overlay, an empty string is passed in --> <string name="config_wwan_data_service_class" translatable="false"></string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index b0ee810fee41..ece65ff21487 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -298,6 +298,7 @@ <java-symbol type="string" name="config_wlan_network_service_package" /> <java-symbol type="string" name="config_wwan_network_service_class" /> <java-symbol type="string" name="config_wlan_network_service_class" /> + <java-symbol type="bool" name="config_wlan_data_service_conn_persistence_on_restart" /> <java-symbol type="string" name="config_wwan_data_service_package" /> <java-symbol type="string" name="config_wlan_data_service_package" /> <java-symbol type="string" name="config_wwan_data_service_class" /> diff --git a/core/tests/bluetoothtests/AndroidManifest.xml b/core/tests/bluetoothtests/AndroidManifest.xml index 7f9d8749358c..6849a90f5010 100644 --- a/core/tests/bluetoothtests/AndroidManifest.xml +++ b/core/tests/bluetoothtests/AndroidManifest.xml @@ -15,14 +15,18 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.bluetooth.tests" > + package="com.android.bluetooth.tests" + android:sharedUserId="android.uid.bluetooth" > <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> + <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" /> <uses-permission android:name="android.permission.BROADCAST_STICKY" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> + <uses-permission android:name="android.permission.RECEIVE_SMS" /> + <uses-permission android:name="android.permission.READ_SMS"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java index 4b32ceae0617..89dbe3f75b56 100644 --- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java +++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java @@ -360,6 +360,30 @@ public class BluetoothStressTest extends InstrumentationTestCase { mTestUtils.unpair(mAdapter, device); } + /* Make sure there is at least 1 unread message in the last week on remote device */ + public void testMceSetMessageStatus() { + int iterations = BluetoothTestRunner.sMceSetMessageStatusIterations; + if (iterations == 0) { + return; + } + + BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); + mTestUtils.enable(mAdapter); + mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.MAP_CLIENT, null); + mTestUtils.mceGetUnreadMessage(mAdapter, device); + + for (int i = 0; i < iterations; i++) { + mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.READ); + mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.UNREAD); + } + + /** + * It is hard to find device to support set undeleted status, so just + * set deleted in 1 iteration + **/ + mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.DELETED); + } + private void sleep(long time) { try { Thread.sleep(time); diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java index 56e691d8c246..d19c2c3e7e24 100644 --- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java +++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java @@ -40,6 +40,7 @@ import android.util.Log; * [-e connect_input_iterations <iterations>] \ * [-e connect_pan_iterations <iterations>] \ * [-e start_stop_sco_iterations <iterations>] \ + * [-e mce_set_message_status_iterations <iterations>] \ * [-e pair_address <address>] \ * [-e headset_address <address>] \ * [-e a2dp_address <address>] \ @@ -64,6 +65,7 @@ public class BluetoothTestRunner extends InstrumentationTestRunner { public static int sConnectInputIterations = 100; public static int sConnectPanIterations = 100; public static int sStartStopScoIterations = 100; + public static int sMceSetMessageStatusIterations = 100; public static String sDeviceAddress = ""; public static byte[] sDevicePairPin = {'1', '2', '3', '4'}; @@ -173,6 +175,15 @@ public class BluetoothTestRunner extends InstrumentationTestRunner { } } + val = arguments.getString("mce_set_message_status_iterations"); + if (val != null) { + try { + sMceSetMessageStatusIterations = Integer.parseInt(val); + } catch (NumberFormatException e) { + // Invalid argument, fall back to default value + } + } + val = arguments.getString("device_address"); if (val != null) { sDeviceAddress = val; diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java index ed613c36b89b..409025bc670d 100644 --- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java +++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java @@ -56,6 +56,10 @@ public class BluetoothTestUtils extends Assert { private static final int CONNECT_PROXY_TIMEOUT = 5000; /** Time between polls in ms. */ private static final int POLL_TIME = 100; + /** Timeout to get map message in ms. */ + private static final int GET_UNREAD_MESSAGE_TIMEOUT = 10000; + /** Timeout to set map message status in ms. */ + private static final int SET_MESSAGE_STATUS_TIMEOUT = 2000; private abstract class FlagReceiver extends BroadcastReceiver { private int mExpectedFlags = 0; @@ -98,6 +102,8 @@ public class BluetoothTestUtils extends Assert { private static final int STATE_TURNING_ON_FLAG = 1 << 6; private static final int STATE_ON_FLAG = 1 << 7; private static final int STATE_TURNING_OFF_FLAG = 1 << 8; + private static final int STATE_GET_MESSAGE_FINISHED_FLAG = 1 << 9; + private static final int STATE_SET_MESSAGE_STATUS_FINISHED_FLAG = 1 << 10; public BluetoothReceiver(int expectedFlags) { super(expectedFlags); @@ -231,6 +237,9 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.PAN: mConnectionAction = BluetoothPan.ACTION_CONNECTION_STATE_CHANGED; break; + case BluetoothProfile.MAP_CLIENT: + mConnectionAction = BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED; + break; default: mConnectionAction = null; } @@ -308,6 +317,34 @@ public class BluetoothTestUtils extends Assert { } } + + private class MceSetMessageStatusReceiver extends FlagReceiver { + private static final int MESSAGE_RECEIVED_FLAG = 1; + private static final int STATUS_CHANGED_FLAG = 1 << 1; + + public MceSetMessageStatusReceiver(int expectedFlags) { + super(expectedFlags); + } + + @Override + public void onReceive(Context context, Intent intent) { + if (BluetoothMapClient.ACTION_MESSAGE_RECEIVED.equals(intent.getAction())) { + String handle = intent.getStringExtra(BluetoothMapClient.EXTRA_MESSAGE_HANDLE); + assertNotNull(handle); + setFiredFlag(MESSAGE_RECEIVED_FLAG); + mMsgHandle = handle; + } else if (BluetoothMapClient.ACTION_MESSAGE_DELETED_STATUS_CHANGED.equals(intent.getAction())) { + int result = intent.getIntExtra(BluetoothMapClient.EXTRA_RESULT_CODE, BluetoothMapClient.RESULT_FAILURE); + assertEquals(result, BluetoothMapClient.RESULT_SUCCESS); + setFiredFlag(STATUS_CHANGED_FLAG); + } else if (BluetoothMapClient.ACTION_MESSAGE_READ_STATUS_CHANGED.equals(intent.getAction())) { + int result = intent.getIntExtra(BluetoothMapClient.EXTRA_RESULT_CODE, BluetoothMapClient.RESULT_FAILURE); + assertEquals(result, BluetoothMapClient.RESULT_SUCCESS); + setFiredFlag(STATUS_CHANGED_FLAG); + } + } + } + private BluetoothProfile.ServiceListener mServiceListener = new BluetoothProfile.ServiceListener() { @Override @@ -326,6 +363,9 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.PAN: mPan = (BluetoothPan) proxy; break; + case BluetoothProfile.MAP_CLIENT: + mMce = (BluetoothMapClient) proxy; + break; } } } @@ -346,6 +386,9 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.PAN: mPan = null; break; + case BluetoothProfile.MAP_CLIENT: + mMce = null; + break; } } } @@ -362,6 +405,8 @@ public class BluetoothTestUtils extends Assert { private BluetoothHeadset mHeadset = null; private BluetoothHidHost mInput = null; private BluetoothPan mPan = null; + private BluetoothMapClient mMce = null; + private String mMsgHandle = null; /** * Creates a utility instance for testing Bluetooth. @@ -898,7 +943,7 @@ public class BluetoothTestUtils extends Assert { * @param adapter The BT adapter. * @param device The remote device. * @param profile The profile to connect. One of {@link BluetoothProfile#A2DP}, - * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#HID_HOST}. + * {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#HID_HOST} or {@link BluetoothProfile#MAP_CLIENT}.. * @param methodName The method name to printed in the logs. If null, will be * "connectProfile(profile=<profile>, device=<device>)" */ @@ -941,6 +986,8 @@ public class BluetoothTestUtils extends Assert { assertTrue(((BluetoothHeadset)proxy).connect(device)); } else if (profile == BluetoothProfile.HID_HOST) { assertTrue(((BluetoothHidHost)proxy).connect(device)); + } else if (profile == BluetoothProfile.MAP_CLIENT) { + assertTrue(((BluetoothMapClient)proxy).connect(device)); } break; default: @@ -1016,6 +1063,8 @@ public class BluetoothTestUtils extends Assert { assertTrue(((BluetoothHeadset)proxy).disconnect(device)); } else if (profile == BluetoothProfile.HID_HOST) { assertTrue(((BluetoothHidHost)proxy).disconnect(device)); + } else if (profile == BluetoothProfile.MAP_CLIENT) { + assertTrue(((BluetoothMapClient)proxy).disconnect(device)); } break; case BluetoothProfile.STATE_DISCONNECTED: @@ -1373,6 +1422,89 @@ public class BluetoothTestUtils extends Assert { } } + public void mceGetUnreadMessage(BluetoothAdapter adapter, BluetoothDevice device) { + int mask; + String methodName = "getUnreadMessage"; + + if (!adapter.isEnabled()) { + fail(String.format("%s bluetooth not enabled", methodName)); + } + + if (!adapter.getBondedDevices().contains(device)) { + fail(String.format("%s device not paired", methodName)); + } + + mMce = (BluetoothMapClient) connectProxy(adapter, BluetoothProfile.MAP_CLIENT); + assertNotNull(mMce); + + if (mMce.getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) { + fail(String.format("%s device is not connected", methodName)); + } + + mMsgHandle = null; + mask = MceSetMessageStatusReceiver.MESSAGE_RECEIVED_FLAG; + MceSetMessageStatusReceiver receiver = getMceSetMessageStatusReceiver(device, mask); + assertTrue(mMce.getUnreadMessages(device)); + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < GET_UNREAD_MESSAGE_TIMEOUT) { + if ((receiver.getFiredFlags() & mask) == mask) { + writeOutput(String.format("%s completed", methodName)); + removeReceiver(receiver); + return; + } + sleep(POLL_TIME); + } + int firedFlags = receiver.getFiredFlags(); + removeReceiver(receiver); + fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)", + methodName, mMce.getConnectionState(device), BluetoothMapClient.STATE_CONNECTED, firedFlags, mask)); + } + + /** + * Set a message to read/unread/deleted/undeleted + */ + public void mceSetMessageStatus(BluetoothAdapter adapter, BluetoothDevice device, int status) { + int mask; + String methodName = "setMessageStatus"; + + if (!adapter.isEnabled()) { + fail(String.format("%s bluetooth not enabled", methodName)); + } + + if (!adapter.getBondedDevices().contains(device)) { + fail(String.format("%s device not paired", methodName)); + } + + mMce = (BluetoothMapClient) connectProxy(adapter, BluetoothProfile.MAP_CLIENT); + assertNotNull(mMce); + + if (mMce.getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) { + fail(String.format("%s device is not connected", methodName)); + } + + assertNotNull(mMsgHandle); + mask = MceSetMessageStatusReceiver.STATUS_CHANGED_FLAG; + MceSetMessageStatusReceiver receiver = getMceSetMessageStatusReceiver(device, mask); + + assertTrue(mMce.setMessageStatus(device, mMsgHandle, status)); + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < SET_MESSAGE_STATUS_TIMEOUT) { + if ((receiver.getFiredFlags() & mask) == mask) { + writeOutput(String.format("%s completed", methodName)); + removeReceiver(receiver); + return; + } + sleep(POLL_TIME); + } + + int firedFlags = receiver.getFiredFlags(); + removeReceiver(receiver); + fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)", + methodName, mMce.getConnectionState(device), BluetoothPan.STATE_CONNECTED, firedFlags, mask)); + } + private void addReceiver(BroadcastReceiver receiver, String[] actions) { IntentFilter filter = new IntentFilter(); for (String action: actions) { @@ -1408,7 +1540,8 @@ public class BluetoothTestUtils extends Assert { String[] actions = { BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED, BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED, - BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED}; + BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED, + BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED}; ConnectProfileReceiver receiver = new ConnectProfileReceiver(device, profile, expectedFlags); addReceiver(receiver, actions); @@ -1430,6 +1563,16 @@ public class BluetoothTestUtils extends Assert { return receiver; } + private MceSetMessageStatusReceiver getMceSetMessageStatusReceiver(BluetoothDevice device, + int expectedFlags) { + String[] actions = {BluetoothMapClient.ACTION_MESSAGE_RECEIVED, + BluetoothMapClient.ACTION_MESSAGE_READ_STATUS_CHANGED, + BluetoothMapClient.ACTION_MESSAGE_DELETED_STATUS_CHANGED}; + MceSetMessageStatusReceiver receiver = new MceSetMessageStatusReceiver(expectedFlags); + addReceiver(receiver, actions); + return receiver; + } + private void removeReceiver(BroadcastReceiver receiver) { mContext.unregisterReceiver(receiver); mReceivers.remove(receiver); @@ -1456,6 +1599,10 @@ public class BluetoothTestUtils extends Assert { if (mPan != null) { return mPan; } + case BluetoothProfile.MAP_CLIENT: + if (mMce != null) { + return mMce; + } break; default: return null; @@ -1483,6 +1630,11 @@ public class BluetoothTestUtils extends Assert { sleep(POLL_TIME); } return mPan; + case BluetoothProfile.MAP_CLIENT: + while (mMce == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { + sleep(POLL_TIME); + } + return mMce; default: return null; } diff --git a/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java b/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java index d00d052db590..0af8c728aba3 100644 --- a/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java +++ b/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java @@ -154,6 +154,29 @@ public class EuiccProfileInfoTest { } @Test + public void testBuilder_BasedOnAnotherProfileWithEmptyAccessRules() { + EuiccProfileInfo p = + new EuiccProfileInfo.Builder("21430000000000006587") + .setNickname("profile nickname") + .setProfileName("profile name") + .setServiceProviderName("service provider") + .setCarrierIdentifier( + new CarrierIdentifier( + new byte[] {0x23, 0x45, 0x67}, + "123", + "45")) + .setState(EuiccProfileInfo.PROFILE_STATE_ENABLED) + .setProfileClass(EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL) + .setPolicyRules(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE) + .setUiccAccessRule(null) + .build(); + + EuiccProfileInfo copied = new EuiccProfileInfo.Builder(p).build(); + + assertEquals(null, copied.getUiccAccessRules()); + } + + @Test public void testEqualsHashCode() { EuiccProfileInfo p = new EuiccProfileInfo.Builder("21430000000000006587") diff --git a/core/tests/coretests/src/android/text/format/DateFormatTest.java b/core/tests/coretests/src/android/text/format/DateFormatTest.java index 5a0a84db5905..a3434e885012 100644 --- a/core/tests/coretests/src/android/text/format/DateFormatTest.java +++ b/core/tests/coretests/src/android/text/format/DateFormatTest.java @@ -16,9 +16,12 @@ package android.text.format; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import android.icu.text.DateFormatSymbols; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; @@ -27,6 +30,7 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Arrays; import java.util.Locale; @Presubmit @@ -55,4 +59,80 @@ public class DateFormatTest { assertFalse(DateFormat.is24HourLocale(Locale.US)); assertTrue(DateFormat.is24HourLocale(Locale.GERMANY)); } + + @Test + public void testgetIcuDateFormatSymbols() { + DateFormatSymbols dfs = DateFormat.getIcuDateFormatSymbols(Locale.US); + assertEquals("AM", dfs.getAmPmStrings()[0]); + assertEquals("PM", dfs.getAmPmStrings()[1]); + assertEquals("a", dfs.getAmpmNarrowStrings()[0]); + assertEquals("p", dfs.getAmpmNarrowStrings()[1]); + } + + @Test + public void testGetDateFormatOrder() { + // lv and fa use differing orders depending on whether you're using numeric or + // textual months. + Locale lv = new Locale("lv"); + assertEquals("[d, M, y]", Arrays.toString(DateFormat.getDateFormatOrder( + best(lv, "yyyy-M-dd")))); + assertEquals("[y, d, M]", Arrays.toString(DateFormat.getDateFormatOrder( + best(lv, "yyyy-MMM-dd")))); + assertEquals("[d, M, \u0000]", Arrays.toString(DateFormat.getDateFormatOrder( + best(lv, "MMM-dd")))); + Locale fa = new Locale("fa"); + assertEquals("[y, M, d]", Arrays.toString(DateFormat.getDateFormatOrder( + best(fa, "yyyy-M-dd")))); + assertEquals("[d, M, y]", Arrays.toString(DateFormat.getDateFormatOrder( + best(fa, "yyyy-MMM-dd")))); + assertEquals("[d, M, \u0000]", Arrays.toString(DateFormat.getDateFormatOrder( + best(fa, "MMM-dd")))); + + // English differs on each side of the Atlantic. + Locale enUS = Locale.US; + assertEquals("[M, d, y]", Arrays.toString(DateFormat.getDateFormatOrder( + best(enUS, "yyyy-M-dd")))); + assertEquals("[M, d, y]", Arrays.toString(DateFormat.getDateFormatOrder( + best(enUS, "yyyy-MMM-dd")))); + assertEquals("[M, d, \u0000]", Arrays.toString(DateFormat.getDateFormatOrder( + best(enUS, "MMM-dd")))); + Locale enGB = Locale.UK; + assertEquals("[d, M, y]", Arrays.toString(DateFormat.getDateFormatOrder( + best(enGB, "yyyy-M-dd")))); + assertEquals("[d, M, y]", Arrays.toString(DateFormat.getDateFormatOrder( + best(enGB, "yyyy-MMM-dd")))); + assertEquals("[d, M, \u0000]", Arrays.toString(DateFormat.getDateFormatOrder( + best(enGB, "MMM-dd")))); + + assertEquals("[y, M, d]", Arrays.toString(DateFormat.getDateFormatOrder( + "yyyy - 'why' '' 'ddd' MMM-dd"))); + + try { + DateFormat.getDateFormatOrder("the quick brown fox jumped over the lazy dog"); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + DateFormat.getDateFormatOrder("'"); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + DateFormat.getDateFormatOrder("yyyy'"); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + DateFormat.getDateFormatOrder("yyyy'MMM"); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + private static String best(Locale l, String skeleton) { + return DateFormat.getBestDateTimePattern(l, skeleton); + } } diff --git a/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java new file mode 100644 index 000000000000..0f17d27048f3 --- /dev/null +++ b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java @@ -0,0 +1,671 @@ +/* + * Copyright (C) 2013 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.text.format; + +import static android.icu.util.TimeZone.GMT_ZONE; +import static android.icu.util.ULocale.ENGLISH; +import static android.text.format.DateIntervalFormat.formatDateRange; +import static android.text.format.DateUtils.FORMAT_12HOUR; +import static android.text.format.DateUtils.FORMAT_24HOUR; +import static android.text.format.DateUtils.FORMAT_ABBREV_ALL; +import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; +import static android.text.format.DateUtils.FORMAT_ABBREV_TIME; +import static android.text.format.DateUtils.FORMAT_ABBREV_WEEKDAY; +import static android.text.format.DateUtils.FORMAT_NO_MONTH_DAY; +import static android.text.format.DateUtils.FORMAT_NO_YEAR; +import static android.text.format.DateUtils.FORMAT_NUMERIC_DATE; +import static android.text.format.DateUtils.FORMAT_SHOW_DATE; +import static android.text.format.DateUtils.FORMAT_SHOW_TIME; +import static android.text.format.DateUtils.FORMAT_SHOW_WEEKDAY; +import static android.text.format.DateUtils.FORMAT_SHOW_YEAR; +import static android.text.format.DateUtils.FORMAT_UTC; + +import static org.junit.Assert.assertEquals; + +import android.icu.util.Calendar; +import android.icu.util.TimeZone; +import android.icu.util.ULocale; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.function.BiFunction; + +@Presubmit +@SmallTest +@RunWith(AndroidJUnit4.class) +public class DateIntervalFormatTest { + private static final long MINUTE = 60 * 1000; + private static final long HOUR = 60 * MINUTE; + private static final long DAY = 24 * HOUR; + private static final long MONTH = 31 * DAY; + private static final long YEAR = 12 * MONTH; + + // These are the old CTS tests for DateIntervalFormat.formatDateRange. + @Test + public void test_formatDateInterval() throws Exception { + TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles"); + + Calendar c = Calendar.getInstance(tz, ULocale.US); + c.set(Calendar.MONTH, Calendar.JANUARY); + c.set(Calendar.DAY_OF_MONTH, 19); + c.set(Calendar.HOUR_OF_DAY, 3); + c.set(Calendar.MINUTE, 30); + c.set(Calendar.SECOND, 15); + c.set(Calendar.MILLISECOND, 0); + long timeWithCurrentYear = c.getTimeInMillis(); + + c.set(Calendar.YEAR, 2009); + long fixedTime = c.getTimeInMillis(); + + c.set(Calendar.MINUTE, 0); + c.set(Calendar.SECOND, 0); + long onTheHour = c.getTimeInMillis(); + + long noonDuration = (8 * 60 + 30) * 60 * 1000 - 15 * 1000; + long midnightDuration = (3 * 60 + 30) * 60 * 1000 + 15 * 1000; + + ULocale de_DE = new ULocale("de", "DE"); + ULocale en_US = new ULocale("en", "US"); + ULocale es_ES = new ULocale("es", "ES"); + ULocale es_US = new ULocale("es", "US"); + + assertEquals("Monday", + formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_WEEKDAY)); + assertEquals("January 19", + formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR, + FORMAT_SHOW_DATE)); + assertEquals("3:30 AM", formatDateRange(en_US, tz, fixedTime, fixedTime, FORMAT_SHOW_TIME)); + assertEquals("January 19, 2009", + formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_YEAR)); + assertEquals("January 19", + formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR, FORMAT_NO_YEAR)); + assertEquals("January", + formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR, + FORMAT_NO_MONTH_DAY)); + assertEquals("3:30 AM", + formatDateRange(en_US, tz, fixedTime, fixedTime, FORMAT_12HOUR | FORMAT_SHOW_TIME)); + assertEquals("03:30", + formatDateRange(en_US, tz, fixedTime, fixedTime, FORMAT_24HOUR | FORMAT_SHOW_TIME)); + assertEquals("3:30 AM", formatDateRange(en_US, tz, fixedTime, fixedTime, + FORMAT_12HOUR /*| FORMAT_CAP_AMPM*/ | FORMAT_SHOW_TIME)); + assertEquals("12:00 PM", + formatDateRange(en_US, tz, fixedTime + noonDuration, fixedTime + noonDuration, + FORMAT_12HOUR | FORMAT_SHOW_TIME)); + assertEquals("12:00 PM", + formatDateRange(en_US, tz, fixedTime + noonDuration, fixedTime + noonDuration, + FORMAT_12HOUR | FORMAT_SHOW_TIME /*| FORMAT_CAP_NOON*/)); + assertEquals("12:00 PM", + formatDateRange(en_US, tz, fixedTime + noonDuration, fixedTime + noonDuration, + FORMAT_12HOUR /*| FORMAT_NO_NOON*/ | FORMAT_SHOW_TIME)); + assertEquals("12:00 AM", formatDateRange(en_US, tz, fixedTime - midnightDuration, + fixedTime - midnightDuration, + FORMAT_12HOUR | FORMAT_SHOW_TIME /*| FORMAT_NO_MIDNIGHT*/)); + assertEquals("3:30 AM", + formatDateRange(en_US, tz, fixedTime, fixedTime, FORMAT_SHOW_TIME | FORMAT_UTC)); + assertEquals("3 AM", formatDateRange(en_US, tz, onTheHour, onTheHour, + FORMAT_SHOW_TIME | FORMAT_ABBREV_TIME)); + assertEquals("Mon", formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR, + FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_WEEKDAY)); + assertEquals("Jan 19", + formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR, + FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH)); + assertEquals("Jan 19", + formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + + assertEquals("1/19/2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * HOUR, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("1/19/2009 – 1/22/2009", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("1/19/2009 – 4/22/2009", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("1/19/2009 – 2/9/2012", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + + assertEquals("19.1.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + HOUR, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("19.–22.01.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("19.01. – 22.04.2009", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("19.01.2009 – 09.02.2012", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + + assertEquals("19/1/2009", formatDateRange(es_US, tz, fixedTime, fixedTime + HOUR, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("19/1/2009–22/1/2009", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("19/1/2009–22/4/2009", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("19/1/2009–9/2/2012", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + + assertEquals("19/1/2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + HOUR, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("19/1/2009–22/1/2009", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("19/1/2009–22/4/2009", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("19/1/2009–9/2/2012", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + + // These are some random other test cases I came up with. + + assertEquals("January 19 – 22, 2009", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, 0)); + assertEquals("Jan 19 – 22, 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("Mon, Jan 19 – Thu, Jan 22, 2009", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL)); + assertEquals("Monday, January 19 – Thursday, January 22, 2009", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY)); + + assertEquals("January 19 – April 22, 2009", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, 0)); + assertEquals("Jan 19 – Apr 22, 2009", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("Mon, Jan 19 – Wed, Apr 22, 2009", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL)); + assertEquals("January – April 2009", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY)); + + assertEquals("Jan 19, 2009 – Feb 9, 2012", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("Jan 2009 – Feb 2012", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL)); + assertEquals("January 19, 2009 – February 9, 2012", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR, 0)); + assertEquals("Monday, January 19, 2009 – Thursday, February 9, 2012", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY)); + + // The same tests but for de_DE. + + assertEquals("19.–22. Januar 2009", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, 0)); + assertEquals("19.–22. Jan. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("Mo., 19. – Do., 22. Jan. 2009", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL)); + assertEquals("Montag, 19. – Donnerstag, 22. Januar 2009", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY)); + + assertEquals("19. Januar – 22. April 2009", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, 0)); + assertEquals("19. Jan. – 22. Apr. 2009", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("Mo., 19. Jan. – Mi., 22. Apr. 2009", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL)); + assertEquals("Januar–April 2009", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY)); + + assertEquals("19. Jan. 2009 – 9. Feb. 2012", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("Jan. 2009 – Feb. 2012", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL)); + assertEquals("19. Januar 2009 – 9. Februar 2012", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, 0)); + assertEquals("Montag, 19. Januar 2009 – Donnerstag, 9. Februar 2012", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY)); + + // The same tests but for es_US. + + assertEquals("19–22 de enero de 2009", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, 0)); + assertEquals("19–22 de ene. de 2009", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("lun., 19 de ene. – jue., 22 de ene. de 2009", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL)); + assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY)); + + assertEquals("19 de enero–22 de abril de 2009", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, 0)); + assertEquals("19 de ene. – 22 de abr. 2009", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("lun., 19 de ene. – mié., 22 de abr. de 2009", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL)); + assertEquals("enero–abril de 2009", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY)); + + assertEquals("19 de ene. de 2009 – 9 de feb. de 2012", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("ene. de 2009 – feb. de 2012", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL)); + assertEquals("19 de enero de 2009–9 de febrero de 2012", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, 0)); + assertEquals("lunes, 19 de enero de 2009–jueves, 9 de febrero de 2012", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY)); + + // The same tests but for es_ES. + + assertEquals("19–22 de enero de 2009", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, 0)); + assertEquals("19–22 ene. 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("lun., 19 ene. – jue., 22 ene. 2009", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL)); + assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY)); + + assertEquals("19 de enero–22 de abril de 2009", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, 0)); + assertEquals("19 ene. – 22 abr. 2009", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("lun., 19 ene. – mié., 22 abr. 2009", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL)); + assertEquals("enero–abril de 2009", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY)); + + assertEquals("19 ene. 2009 – 9 feb. 2012", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("ene. 2009 – feb. 2012", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL)); + assertEquals("19 de enero de 2009–9 de febrero de 2012", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, 0)); + assertEquals("lunes, 19 de enero de 2009–jueves, 9 de febrero de 2012", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY)); + } + + // http://b/8862241 - we should be able to format dates past 2038. + // See also http://code.google.com/p/android/issues/detail?id=13050. + @Test + public void test8862241() throws Exception { + ULocale l = ULocale.US; + TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles"); + Calendar c = Calendar.getInstance(tz, l); + c.clear(); + c.set(2042, Calendar.JANUARY, 19, 3, 30); + long jan_19_2042 = c.getTimeInMillis(); + c.set(2046, Calendar.OCTOBER, 4, 3, 30); + long oct_4_2046 = c.getTimeInMillis(); + int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL; + assertEquals("Jan 19, 2042 – Oct 4, 2046", + formatDateRange(l, tz, jan_19_2042, oct_4_2046, flags)); + } + + // http://b/10089890 - we should take the given time zone into account. + @Test + public void test10089890() throws Exception { + ULocale l = ULocale.US; + TimeZone utc = TimeZone.getTimeZone("UTC"); + TimeZone pacific = TimeZone.getTimeZone("America/Los_Angeles"); + int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL | FORMAT_SHOW_TIME | FORMAT_24HOUR; + + // The Unix epoch is UTC, so 0 is 1970-01-01T00:00Z... + assertEquals("Jan 1, 1970, 00:00 – Jan 2, 1970, 00:00", + formatDateRange(l, utc, 0, DAY + 1, flags)); + // But MTV is hours behind, so 0 was still the afternoon of the previous day... + assertEquals("Dec 31, 1969, 16:00 – Jan 1, 1970, 16:00", + formatDateRange(l, pacific, 0, DAY, flags)); + } + + // http://b/10318326 - we can drop the minutes in a 12-hour time if they're zero, + // but not if we're using the 24-hour clock. That is: "4 PM" is reasonable, "16" is not. + @Test + public void test10318326() throws Exception { + long midnight = 0; + long teaTime = 16 * HOUR; + + int time12 = FORMAT_12HOUR | FORMAT_SHOW_TIME; + int time24 = FORMAT_24HOUR | FORMAT_SHOW_TIME; + int abbr12 = time12 | FORMAT_ABBREV_ALL; + int abbr24 = time24 | FORMAT_ABBREV_ALL; + + ULocale l = ULocale.US; + TimeZone utc = TimeZone.getTimeZone("UTC"); + + // Full length on-the-hour times. + assertEquals("00:00", formatDateRange(l, utc, midnight, midnight, time24)); + assertEquals("12:00 AM", formatDateRange(l, utc, midnight, midnight, time12)); + assertEquals("16:00", formatDateRange(l, utc, teaTime, teaTime, time24)); + assertEquals("4:00 PM", formatDateRange(l, utc, teaTime, teaTime, time12)); + + // Abbreviated on-the-hour times. + assertEquals("00:00", formatDateRange(l, utc, midnight, midnight, abbr24)); + assertEquals("12 AM", formatDateRange(l, utc, midnight, midnight, abbr12)); + assertEquals("16:00", formatDateRange(l, utc, teaTime, teaTime, abbr24)); + assertEquals("4 PM", formatDateRange(l, utc, teaTime, teaTime, abbr12)); + + // Abbreviated on-the-hour ranges. + assertEquals("00:00 – 16:00", formatDateRange(l, utc, midnight, teaTime, abbr24)); + assertEquals("12 AM – 4 PM", formatDateRange(l, utc, midnight, teaTime, abbr12)); + + // Abbreviated mixed ranges. + assertEquals("00:00 – 16:01", formatDateRange(l, utc, midnight, teaTime + MINUTE, abbr24)); + assertEquals("12:00 AM – 4:01 PM", + formatDateRange(l, utc, midnight, teaTime + MINUTE, abbr12)); + } + + // http://b/10560853 - when the time is not displayed, an end time 0 ms into the next day is + // considered to belong to the previous day. + @Test + public void test10560853_when_time_not_displayed() throws Exception { + ULocale l = ULocale.US; + TimeZone utc = TimeZone.getTimeZone("UTC"); + + long midnight = 0; + long midnightNext = 1 * DAY; + + int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY; + + // An all-day event runs until 0 milliseconds into the next day, but is formatted as if it's + // just the first day. + assertEquals("Thursday, January 1, 1970", + formatDateRange(l, utc, midnight, midnightNext, flags)); + + // Run one millisecond over, though, and you're into the next day. + long nextMorning = 1 * DAY + 1; + assertEquals("Thursday, January 1 – Friday, January 2, 1970", + formatDateRange(l, utc, midnight, nextMorning, flags)); + + // But the same reasoning applies for that day. + long nextMidnight = 2 * DAY; + assertEquals("Thursday, January 1 – Friday, January 2, 1970", + formatDateRange(l, utc, midnight, nextMidnight, flags)); + } + + // http://b/10560853 - when the start and end times are otherwise on the same day, + // an end time 0 ms into the next day is considered to belong to the previous day. + @Test + public void test10560853_for_single_day_events() throws Exception { + ULocale l = ULocale.US; + TimeZone utc = TimeZone.getTimeZone("UTC"); + + int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR | FORMAT_SHOW_DATE; + + assertEquals("January 1, 1970, 22:00 – 00:00", + formatDateRange(l, utc, 22 * HOUR, 24 * HOUR, flags)); + assertEquals("January 1, 1970, 22:00 – January 2, 1970, 00:30", + formatDateRange(l, utc, 22 * HOUR, 24 * HOUR + 30 * MINUTE, flags)); + } + + // The fix for http://b/10560853 didn't work except for the day around the epoch, which was + // all the unit test checked! + @Test + public void test_single_day_events_later_than_epoch() throws Exception { + ULocale l = ULocale.US; + TimeZone utc = TimeZone.getTimeZone("UTC"); + + int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR | FORMAT_SHOW_DATE; + + Calendar c = Calendar.getInstance(utc, l); + c.clear(); + c.set(1980, Calendar.JANUARY, 1, 0, 0); + long jan_1_1980 = c.getTimeInMillis(); + assertEquals("January 1, 1980, 22:00 – 00:00", + formatDateRange(l, utc, jan_1_1980 + 22 * HOUR, jan_1_1980 + 24 * HOUR, flags)); + assertEquals("January 1, 1980, 22:00 – January 2, 1980, 00:30", + formatDateRange(l, utc, jan_1_1980 + 22 * HOUR, + jan_1_1980 + 24 * HOUR + 30 * MINUTE, flags)); + } + + // The fix for http://b/10560853 didn't work except for UTC, which was + // all the unit test checked! + @Test + public void test_single_day_events_not_in_UTC() throws Exception { + ULocale l = ULocale.US; + TimeZone pacific = TimeZone.getTimeZone("America/Los_Angeles"); + + int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR | FORMAT_SHOW_DATE; + + Calendar c = Calendar.getInstance(pacific, l); + c.clear(); + c.set(1980, Calendar.JANUARY, 1, 0, 0); + long jan_1_1980 = c.getTimeInMillis(); + assertEquals("January 1, 1980, 22:00 – 00:00", + formatDateRange(l, pacific, jan_1_1980 + 22 * HOUR, jan_1_1980 + 24 * HOUR, flags)); + + c.set(1980, Calendar.JULY, 1, 0, 0); + long jul_1_1980 = c.getTimeInMillis(); + assertEquals("July 1, 1980, 22:00 – 00:00", + formatDateRange(l, pacific, jul_1_1980 + 22 * HOUR, jul_1_1980 + 24 * HOUR, flags)); + } + + // http://b/10209343 - even if the caller didn't explicitly ask us to include the year, + // we should do so for years other than the current year. + @Test + public void test10209343_when_not_this_year() { + ULocale l = ULocale.US; + TimeZone utc = TimeZone.getTimeZone("UTC"); + + int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_TIME | FORMAT_24HOUR; + + assertEquals("Thursday, January 1, 1970, 00:00", formatDateRange(l, utc, 0L, 0L, flags)); + + long t1833 = ((long) Integer.MIN_VALUE + Integer.MIN_VALUE) * 1000L; + assertEquals("Sunday, November 24, 1833, 17:31", + formatDateRange(l, utc, t1833, t1833, flags)); + + long t1901 = Integer.MIN_VALUE * 1000L; + assertEquals("Friday, December 13, 1901, 20:45", + formatDateRange(l, utc, t1901, t1901, flags)); + + long t2038 = Integer.MAX_VALUE * 1000L; + assertEquals("Tuesday, January 19, 2038, 03:14", + formatDateRange(l, utc, t2038, t2038, flags)); + + long t2106 = (2L + Integer.MAX_VALUE + Integer.MAX_VALUE) * 1000L; + assertEquals("Sunday, February 7, 2106, 06:28", + formatDateRange(l, utc, t2106, t2106, flags)); + } + + // http://b/10209343 - for the current year, we should honor the FORMAT_SHOW_YEAR flags. + @Test + public void test10209343_when_this_year() { + // Construct a date in the current year (whenever the test happens to be run). + ULocale l = ULocale.US; + TimeZone utc = TimeZone.getTimeZone("UTC"); + Calendar c = Calendar.getInstance(utc, l); + c.set(Calendar.MONTH, Calendar.FEBRUARY); + c.set(Calendar.DAY_OF_MONTH, 10); + c.set(Calendar.HOUR_OF_DAY, 0); + long thisYear = c.getTimeInMillis(); + + // You don't get the year if it's this year... + assertEquals("February 10", formatDateRange(l, utc, thisYear, thisYear, FORMAT_SHOW_DATE)); + + // ...unless you explicitly ask for it. + assertEquals(String.format("February 10, %d", c.get(Calendar.YEAR)), + formatDateRange(l, utc, thisYear, thisYear, FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR)); + + // ...or it's not actually this year... + Calendar c2 = (Calendar) c.clone(); + c2.set(Calendar.YEAR, 1980); + long oldYear = c2.getTimeInMillis(); + assertEquals("February 10, 1980", + formatDateRange(l, utc, oldYear, oldYear, FORMAT_SHOW_DATE)); + + // (But you can disable that!) + assertEquals("February 10", + formatDateRange(l, utc, oldYear, oldYear, FORMAT_SHOW_DATE | FORMAT_NO_YEAR)); + + // ...or the start and end years aren't the same... + assertEquals(String.format("February 10, 1980 – February 10, %d", c.get(Calendar.YEAR)), + formatDateRange(l, utc, oldYear, thisYear, FORMAT_SHOW_DATE)); + + // (And you can't avoid that --- icu4c steps in and overrides you.) + assertEquals(String.format("February 10, 1980 – February 10, %d", c.get(Calendar.YEAR)), + formatDateRange(l, utc, oldYear, thisYear, FORMAT_SHOW_DATE | FORMAT_NO_YEAR)); + } + + // http://b/8467515 - yet another y2k38 bug report. + @Test + public void test8467515() throws Exception { + ULocale l = ULocale.US; + TimeZone utc = TimeZone.getTimeZone("UTC"); + int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_YEAR | FORMAT_ABBREV_MONTH + | FORMAT_ABBREV_WEEKDAY; + long t; + + Calendar calendar = Calendar.getInstance(utc, l); + calendar.clear(); + + calendar.set(2038, Calendar.JANUARY, 19, 12, 0, 0); + t = calendar.getTimeInMillis(); + assertEquals("Tue, Jan 19, 2038", formatDateRange(l, utc, t, t, flags)); + + calendar.set(1900, Calendar.JANUARY, 1, 0, 0, 0); + t = calendar.getTimeInMillis(); + assertEquals("Mon, Jan 1, 1900", formatDateRange(l, utc, t, t, flags)); + } + + // http://b/12004664 + @Test + public void test12004664() throws Exception { + TimeZone utc = TimeZone.getTimeZone("UTC"); + Calendar c = Calendar.getInstance(utc, ULocale.US); + c.clear(); + c.set(Calendar.YEAR, 1980); + c.set(Calendar.MONTH, Calendar.FEBRUARY); + c.set(Calendar.DAY_OF_MONTH, 10); + c.set(Calendar.HOUR_OF_DAY, 0); + long thisYear = c.getTimeInMillis(); + + int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_YEAR; + assertEquals("Sunday, February 10, 1980", + formatDateRange(new ULocale("en", "US"), utc, thisYear, thisYear, flags)); + + // If we supported non-Gregorian calendars, this is what that we'd expect for these + // ULocales. + // This is really the correct behavior, but since java.util.Calendar currently only supports + // the Gregorian calendar, we want to deliberately force icu4c to agree, otherwise we'd have + // a mix of calendars throughout an app's UI depending on whether Java or native code + // formatted + // the date. + // assertEquals("یکشنبه ۲۱ بهمن ۱۳۵۸ ه.ش.", formatDateRange(new ULocale("fa"), utc, + // thisYear, thisYear, flags)); + // assertEquals("AP ۱۳۵۸ سلواغه ۲۱, یکشنبه", formatDateRange(new ULocale("ps"), utc, + // thisYear, thisYear, flags)); + // assertEquals("วันอาทิตย์ 10 กุมภาพันธ์ 2523", formatDateRange(new ULocale("th"), utc, + // thisYear, thisYear, flags)); + + // For now, here are the localized Gregorian strings instead... + assertEquals("یکشنبه ۱۰ فوریهٔ ۱۹۸۰", + formatDateRange(new ULocale("fa"), utc, thisYear, thisYear, flags)); + assertEquals("يونۍ د ۱۹۸۰ د فبروري ۱۰", + formatDateRange(new ULocale("ps"), utc, thisYear, thisYear, flags)); + assertEquals("วันอาทิตย์ที่ 10 กุมภาพันธ์ ค.ศ. 1980", + formatDateRange(new ULocale("th"), utc, thisYear, thisYear, flags)); + } + + // http://b/13234532 + @Test + public void test13234532() throws Exception { + ULocale l = ULocale.US; + TimeZone utc = TimeZone.getTimeZone("UTC"); + + int flags = FORMAT_SHOW_TIME | FORMAT_ABBREV_ALL | FORMAT_12HOUR; + + assertEquals("10 – 11 AM", formatDateRange(l, utc, 10 * HOUR, 11 * HOUR, flags)); + assertEquals("11 AM – 1 PM", formatDateRange(l, utc, 11 * HOUR, 13 * HOUR, flags)); + assertEquals("2 – 3 PM", formatDateRange(l, utc, 14 * HOUR, 15 * HOUR, flags)); + } + + // http://b/20708022 + @Test + public void testEndOfDayOnLastDayOfMonth() throws Exception { + final ULocale locale = new ULocale("en"); + final TimeZone timeZone = TimeZone.getTimeZone("UTC"); + + assertEquals("11:00 PM – 12:00 AM", formatDateRange(locale, timeZone, + 1430434800000L, 1430438400000L, FORMAT_SHOW_TIME)); + } + + // http://b/68847519 + @Test + public void testEndAtMidnight_dateAndTime() { + BiFunction<Long, Long, String> fmt = (from, to) -> formatDateRange( + ENGLISH, GMT_ZONE, from, to, FORMAT_SHOW_DATE | FORMAT_SHOW_TIME | FORMAT_24HOUR); + // If we're showing times and the end-point is midnight the following day, we want the + // behaviour of suppressing the date for the end... + assertEquals("February 27, 2007, 04:00 – 00:00", fmt.apply(1172548800000L, 1172620800000L)); + // ...unless the start-point is also midnight, in which case we need dates to disambiguate. + assertEquals("February 27, 2007, 00:00 – February 28, 2007, 00:00", + fmt.apply(1172534400000L, 1172620800000L)); + // We want to show the date if the end-point is a millisecond after midnight the following + // day, or if it is exactly midnight the day after that. + assertEquals("February 27, 2007, 04:00 – February 28, 2007, 00:00", + fmt.apply(1172548800000L, 1172620800001L)); + assertEquals("February 27, 2007, 04:00 – March 1, 2007, 00:00", + fmt.apply(1172548800000L, 1172707200000L)); + // We want to show the date if the start-point is anything less than a minute after + // midnight, + // since that gets displayed as midnight... + assertEquals("February 27, 2007, 00:00 – February 28, 2007, 00:00", + fmt.apply(1172534459999L, 1172620800000L)); + // ...but not if it is exactly one minute after midnight. + assertEquals("February 27, 2007, 00:01 – 00:00", fmt.apply(1172534460000L, 1172620800000L)); + } + + // http://b/68847519 + @Test + public void testEndAtMidnight_dateOnly() { + BiFunction<Long, Long, String> fmt = (from, to) -> formatDateRange( + ENGLISH, GMT_ZONE, from, to, FORMAT_SHOW_DATE); + // If we're only showing dates and the end-point is midnight of any day, we want the + // behaviour of showing an end date one earlier. So if the end-point is March 2, 2007 00:00, + // show March 1, 2007 instead (whether the start-point is midnight or not). + assertEquals("February 27 – March 1, 2007", fmt.apply(1172534400000L, 1172793600000L)); + assertEquals("February 27 – March 1, 2007", fmt.apply(1172548800000L, 1172793600000L)); + // We want to show the true date if the end-point is a millisecond after midnight. + assertEquals("February 27 – March 2, 2007", fmt.apply(1172534400000L, 1172793600001L)); + + // 2006-02-27 00:00:00.000 GMT - 2007-03-02 00:00:00.000 GMT + assertEquals("February 27, 2006 – March 1, 2007", + fmt.apply(1140998400000L, 1172793600000L)); + + // Spans a leap year's Feb 29th. + assertEquals("February 27 – March 1, 2004", fmt.apply(1077840000000L, 1078185600000L)); + } +} diff --git a/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java b/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java new file mode 100644 index 000000000000..4b3b5735b4f3 --- /dev/null +++ b/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java @@ -0,0 +1,762 @@ +/* + * Copyright (C) 2015 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.text.format; + +import static android.text.format.DateUtils.FORMAT_ABBREV_ALL; +import static android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE; +import static android.text.format.DateUtils.FORMAT_NO_YEAR; +import static android.text.format.DateUtils.FORMAT_NUMERIC_DATE; +import static android.text.format.DateUtils.FORMAT_SHOW_YEAR; +import static android.text.format.RelativeDateTimeFormatter.DAY_IN_MILLIS; +import static android.text.format.RelativeDateTimeFormatter.HOUR_IN_MILLIS; +import static android.text.format.RelativeDateTimeFormatter.MINUTE_IN_MILLIS; +import static android.text.format.RelativeDateTimeFormatter.SECOND_IN_MILLIS; +import static android.text.format.RelativeDateTimeFormatter.WEEK_IN_MILLIS; +import static android.text.format.RelativeDateTimeFormatter.YEAR_IN_MILLIS; +import static android.text.format.RelativeDateTimeFormatter.getRelativeDateTimeString; +import static android.text.format.RelativeDateTimeFormatter.getRelativeTimeSpanString; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Calendar; +import java.util.Locale; +import java.util.TimeZone; + +@Presubmit +@SmallTest +@RunWith(AndroidJUnit4.class) +public class RelativeDateTimeFormatterTest { + + // Tests adopted from CTS tests for DateUtils.getRelativeTimeSpanString. + @Test + public void test_getRelativeTimeSpanStringCTS() throws Exception { + Locale en_US = new Locale("en", "US"); + TimeZone tz = TimeZone.getTimeZone("GMT"); + Calendar cal = Calendar.getInstance(tz, en_US); + // Feb 5, 2015 at 10:50 GMT + cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0); + final long baseTime = cal.getTimeInMillis(); + + assertEquals("0 minutes ago", + getRelativeTimeSpanString(en_US, tz, baseTime - SECOND_IN_MILLIS, baseTime, + MINUTE_IN_MILLIS, 0)); + assertEquals("In 0 minutes", + getRelativeTimeSpanString(en_US, tz, baseTime + SECOND_IN_MILLIS, baseTime, + MINUTE_IN_MILLIS, 0)); + + assertEquals("1 minute ago", + getRelativeTimeSpanString(en_US, tz, 0, MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 0)); + assertEquals("In 1 minute", + getRelativeTimeSpanString(en_US, tz, MINUTE_IN_MILLIS, 0, MINUTE_IN_MILLIS, 0)); + + assertEquals("42 minutes ago", + getRelativeTimeSpanString(en_US, tz, baseTime - 42 * MINUTE_IN_MILLIS, baseTime, + MINUTE_IN_MILLIS, 0)); + assertEquals("In 42 minutes", + getRelativeTimeSpanString(en_US, tz, baseTime + 42 * MINUTE_IN_MILLIS, baseTime, + MINUTE_IN_MILLIS, 0)); + + final long TWO_HOURS_IN_MS = 2 * HOUR_IN_MILLIS; + assertEquals("2 hours ago", + getRelativeTimeSpanString(en_US, tz, baseTime - TWO_HOURS_IN_MS, baseTime, + MINUTE_IN_MILLIS, FORMAT_NUMERIC_DATE)); + assertEquals("In 2 hours", + getRelativeTimeSpanString(en_US, tz, baseTime + TWO_HOURS_IN_MS, baseTime, + MINUTE_IN_MILLIS, FORMAT_NUMERIC_DATE)); + + assertEquals("In 42 min.", + getRelativeTimeSpanString(en_US, tz, baseTime + (42 * MINUTE_IN_MILLIS), baseTime, + MINUTE_IN_MILLIS, FORMAT_ABBREV_RELATIVE)); + + assertEquals("Tomorrow", + getRelativeTimeSpanString(en_US, tz, DAY_IN_MILLIS, 0, DAY_IN_MILLIS, 0)); + assertEquals("In 2 days", + getRelativeTimeSpanString(en_US, tz, 2 * DAY_IN_MILLIS, 0, DAY_IN_MILLIS, 0)); + assertEquals("Yesterday", + getRelativeTimeSpanString(en_US, tz, 0, DAY_IN_MILLIS, DAY_IN_MILLIS, 0)); + assertEquals("2 days ago", + getRelativeTimeSpanString(en_US, tz, 0, 2 * DAY_IN_MILLIS, DAY_IN_MILLIS, 0)); + + final long DAY_DURATION = 5 * 24 * 60 * 60 * 1000; + assertEquals("5 days ago", + getRelativeTimeSpanString(en_US, tz, baseTime - DAY_DURATION, baseTime, + DAY_IN_MILLIS, 0)); + } + + private void test_getRelativeTimeSpanString_helper(long delta, long minResolution, int flags, + String expectedInPast, + String expectedInFuture) throws Exception { + Locale en_US = new Locale("en", "US"); + TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles"); + Calendar cal = Calendar.getInstance(tz, en_US); + // Feb 5, 2015 at 10:50 PST + cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0); + final long base = cal.getTimeInMillis(); + + assertEquals(expectedInPast, + getRelativeTimeSpanString(en_US, tz, base - delta, base, minResolution, flags)); + assertEquals(expectedInFuture, + getRelativeTimeSpanString(en_US, tz, base + delta, base, minResolution, flags)); + } + + private void test_getRelativeTimeSpanString_helper(long delta, long minResolution, + String expectedInPast, + String expectedInFuture) throws Exception { + test_getRelativeTimeSpanString_helper(delta, minResolution, 0, expectedInPast, + expectedInFuture); + } + + @Test + public void test_getRelativeTimeSpanString() throws Exception { + + test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, 0, "0 seconds ago", + "0 seconds ago"); + test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, 0, "1 minute ago", + "In 1 minute"); + test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, 0, "1 minute ago", + "In 1 minute"); + test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, 0, "5 days ago", "In 5 days"); + + test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, + "0 seconds ago", + "0 seconds ago"); + test_getRelativeTimeSpanString_helper(1 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, + "1 second ago", + "In 1 second"); + test_getRelativeTimeSpanString_helper(2 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, + "2 seconds ago", + "In 2 seconds"); + test_getRelativeTimeSpanString_helper(25 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, + "25 seconds ago", + "In 25 seconds"); + test_getRelativeTimeSpanString_helper(75 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, + "1 minute ago", + "In 1 minute"); + test_getRelativeTimeSpanString_helper(5000 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, + "1 hour ago", + "In 1 hour"); + + test_getRelativeTimeSpanString_helper(0 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, + "0 minutes ago", + "0 minutes ago"); + test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, + "1 minute ago", + "In 1 minute"); + test_getRelativeTimeSpanString_helper(2 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, + "2 minutes ago", + "In 2 minutes"); + test_getRelativeTimeSpanString_helper(25 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, + "25 minutes ago", + "In 25 minutes"); + test_getRelativeTimeSpanString_helper(75 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "1 hour ago", + "In 1 hour"); + test_getRelativeTimeSpanString_helper(720 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, + "12 hours ago", + "In 12 hours"); + + test_getRelativeTimeSpanString_helper(0 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "0 hours ago", + "0 hours ago"); + test_getRelativeTimeSpanString_helper(1 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "1 hour ago", + "In 1 hour"); + test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "2 hours ago", + "In 2 hours"); + test_getRelativeTimeSpanString_helper(5 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "5 hours ago", + "In 5 hours"); + test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "20 hours ago", + "In 20 hours"); + + test_getRelativeTimeSpanString_helper(0 * DAY_IN_MILLIS, DAY_IN_MILLIS, "Today", "Today"); + test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday", + "Tomorrow"); + test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday", + "Tomorrow"); + test_getRelativeTimeSpanString_helper(2 * DAY_IN_MILLIS, DAY_IN_MILLIS, "2 days ago", + "In 2 days"); + test_getRelativeTimeSpanString_helper(25 * DAY_IN_MILLIS, DAY_IN_MILLIS, "January 11", + "March 2"); + + test_getRelativeTimeSpanString_helper(0 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "0 weeks ago", + "0 weeks ago"); + test_getRelativeTimeSpanString_helper(1 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "1 week ago", + "In 1 week"); + test_getRelativeTimeSpanString_helper(2 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "2 weeks ago", + "In 2 weeks"); + test_getRelativeTimeSpanString_helper(25 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "25 weeks ago", + "In 25 weeks"); + + // duration >= minResolution + test_getRelativeTimeSpanString_helper(30 * SECOND_IN_MILLIS, 0, "30 seconds ago", + "In 30 seconds"); + test_getRelativeTimeSpanString_helper(30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, + "30 minutes ago", "In 30 minutes"); + test_getRelativeTimeSpanString_helper(30 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, "Yesterday", + "Tomorrow"); + test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, MINUTE_IN_MILLIS, "5 days ago", + "In 5 days"); + test_getRelativeTimeSpanString_helper(30 * WEEK_IN_MILLIS, MINUTE_IN_MILLIS, + "July 10, 2014", + "September 3"); + test_getRelativeTimeSpanString_helper(5 * 365 * DAY_IN_MILLIS, MINUTE_IN_MILLIS, + "February 6, 2010", "February 4, 2020"); + + test_getRelativeTimeSpanString_helper(60 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, + "1 minute ago", + "In 1 minute"); + test_getRelativeTimeSpanString_helper(120 * SECOND_IN_MILLIS - 1, MINUTE_IN_MILLIS, + "1 minute ago", "In 1 minute"); + test_getRelativeTimeSpanString_helper(60 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, "1 hour ago", + "In 1 hour"); + test_getRelativeTimeSpanString_helper(120 * MINUTE_IN_MILLIS - 1, HOUR_IN_MILLIS, + "1 hour ago", + "In 1 hour"); + test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Today", "Today"); + test_getRelativeTimeSpanString_helper(12 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday", + "Today"); + test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday", + "Tomorrow"); + test_getRelativeTimeSpanString_helper(48 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "2 days ago", + "In 2 days"); + test_getRelativeTimeSpanString_helper(45 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "2 days ago", + "In 2 days"); + test_getRelativeTimeSpanString_helper(7 * DAY_IN_MILLIS, WEEK_IN_MILLIS, "1 week ago", + "In 1 week"); + test_getRelativeTimeSpanString_helper(14 * DAY_IN_MILLIS - 1, WEEK_IN_MILLIS, "1 week ago", + "In 1 week"); + + // duration < minResolution + test_getRelativeTimeSpanString_helper(59 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, + "0 minutes ago", + "In 0 minutes"); + test_getRelativeTimeSpanString_helper(59 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, "0 hours ago", + "In 0 hours"); + test_getRelativeTimeSpanString_helper(HOUR_IN_MILLIS - 1, HOUR_IN_MILLIS, "0 hours ago", + "In 0 hours"); + test_getRelativeTimeSpanString_helper(DAY_IN_MILLIS - 1, DAY_IN_MILLIS, "Yesterday", + "Tomorrow"); + test_getRelativeTimeSpanString_helper(20 * SECOND_IN_MILLIS, WEEK_IN_MILLIS, "0 weeks ago", + "In 0 weeks"); + test_getRelativeTimeSpanString_helper(WEEK_IN_MILLIS - 1, WEEK_IN_MILLIS, "0 weeks ago", + "In 0 weeks"); + } + + @Test + public void test_getRelativeTimeSpanStringAbbrev() throws Exception { + int flags = FORMAT_ABBREV_RELATIVE; + + test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, 0, flags, "0 sec. ago", + "0 sec. ago"); + test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, 0, flags, "1 min. ago", + "In 1 min."); + test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, 0, flags, "5 days ago", + "In 5 days"); + + test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags, + "0 sec. ago", "0 sec. ago"); + test_getRelativeTimeSpanString_helper(1 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags, + "1 sec. ago", "In 1 sec."); + test_getRelativeTimeSpanString_helper(2 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags, + "2 sec. ago", "In 2 sec."); + test_getRelativeTimeSpanString_helper(25 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags, + "25 sec. ago", "In 25 sec."); + test_getRelativeTimeSpanString_helper(75 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags, + "1 min. ago", "In 1 min."); + test_getRelativeTimeSpanString_helper(5000 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags, + "1 hr. ago", "In 1 hr."); + + test_getRelativeTimeSpanString_helper(0 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags, + "0 min. ago", "0 min. ago"); + test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags, + "1 min. ago", "In 1 min."); + test_getRelativeTimeSpanString_helper(2 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags, + "2 min. ago", "In 2 min."); + test_getRelativeTimeSpanString_helper(25 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags, + "25 min. ago", "In 25 min."); + test_getRelativeTimeSpanString_helper(75 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags, + "1 hr. ago", "In 1 hr."); + test_getRelativeTimeSpanString_helper(720 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags, + "12 hr. ago", "In 12 hr."); + + test_getRelativeTimeSpanString_helper(0 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags, + "0 hr. ago", "0 hr. ago"); + test_getRelativeTimeSpanString_helper(1 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags, + "1 hr. ago", "In 1 hr."); + test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags, + "2 hr. ago", "In 2 hr."); + test_getRelativeTimeSpanString_helper(5 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags, + "5 hr. ago", "In 5 hr."); + test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags, + "20 hr. ago", "In 20 hr."); + + test_getRelativeTimeSpanString_helper(0 * DAY_IN_MILLIS, DAY_IN_MILLIS, flags, "Today", + "Today"); + test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags, + "Yesterday", "Tomorrow"); + test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags, + "Yesterday", "Tomorrow"); + test_getRelativeTimeSpanString_helper(2 * DAY_IN_MILLIS, DAY_IN_MILLIS, flags, + "2 days ago", "In 2 days"); + test_getRelativeTimeSpanString_helper(25 * DAY_IN_MILLIS, DAY_IN_MILLIS, flags, + "January 11", "March 2"); + + test_getRelativeTimeSpanString_helper(0 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags, + "0 wk. ago", "0 wk. ago"); + test_getRelativeTimeSpanString_helper(1 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags, + "1 wk. ago", "In 1 wk."); + test_getRelativeTimeSpanString_helper(2 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags, + "2 wk. ago", "In 2 wk."); + test_getRelativeTimeSpanString_helper(25 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags, + "25 wk. ago", "In 25 wk."); + + // duration >= minResolution + test_getRelativeTimeSpanString_helper(30 * SECOND_IN_MILLIS, 0, flags, "30 sec. ago", + "In 30 sec."); + test_getRelativeTimeSpanString_helper(30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags, + "30 min. ago", "In 30 min."); + test_getRelativeTimeSpanString_helper(30 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, flags, + "Yesterday", "Tomorrow"); + test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, MINUTE_IN_MILLIS, flags, + "5 days ago", "In 5 days"); + test_getRelativeTimeSpanString_helper(30 * WEEK_IN_MILLIS, MINUTE_IN_MILLIS, flags, + "July 10, 2014", "September 3"); + test_getRelativeTimeSpanString_helper(5 * 365 * DAY_IN_MILLIS, MINUTE_IN_MILLIS, flags, + "February 6, 2010", "February 4, 2020"); + + test_getRelativeTimeSpanString_helper(60 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, flags, + "1 min. ago", "In 1 min."); + test_getRelativeTimeSpanString_helper(120 * SECOND_IN_MILLIS - 1, MINUTE_IN_MILLIS, flags, + "1 min. ago", "In 1 min."); + test_getRelativeTimeSpanString_helper(60 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, flags, + "1 hr. ago", "In 1 hr."); + test_getRelativeTimeSpanString_helper(120 * MINUTE_IN_MILLIS - 1, HOUR_IN_MILLIS, flags, + "1 hr. ago", "In 1 hr."); + test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags, "Today", + "Today"); + test_getRelativeTimeSpanString_helper(12 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags, + "Yesterday", "Today"); + test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags, + "Yesterday", "Tomorrow"); + test_getRelativeTimeSpanString_helper(48 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags, + "2 days ago", "In 2 days"); + test_getRelativeTimeSpanString_helper(45 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags, + "2 days ago", "In 2 days"); + test_getRelativeTimeSpanString_helper(7 * DAY_IN_MILLIS, WEEK_IN_MILLIS, flags, + "1 wk. ago", "In 1 wk."); + test_getRelativeTimeSpanString_helper(14 * DAY_IN_MILLIS - 1, WEEK_IN_MILLIS, flags, + "1 wk. ago", "In 1 wk."); + + // duration < minResolution + test_getRelativeTimeSpanString_helper(59 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, flags, + "0 min. ago", "In 0 min."); + test_getRelativeTimeSpanString_helper(59 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, flags, + "0 hr. ago", "In 0 hr."); + test_getRelativeTimeSpanString_helper(HOUR_IN_MILLIS - 1, HOUR_IN_MILLIS, flags, + "0 hr. ago", "In 0 hr."); + test_getRelativeTimeSpanString_helper(DAY_IN_MILLIS - 1, DAY_IN_MILLIS, flags, + "Yesterday", "Tomorrow"); + test_getRelativeTimeSpanString_helper(20 * SECOND_IN_MILLIS, WEEK_IN_MILLIS, flags, + "0 wk. ago", "In 0 wk."); + test_getRelativeTimeSpanString_helper(WEEK_IN_MILLIS - 1, WEEK_IN_MILLIS, flags, + "0 wk. ago", "In 0 wk."); + + } + + @Test + public void test_getRelativeTimeSpanStringGerman() throws Exception { + // Bug: 19744876 + // We need to specify the timezone and the time explicitly. Otherwise it + // may not always give a correct answer of "tomorrow" by using + // (now + DAY_IN_MILLIS). + Locale de_DE = new Locale("de", "DE"); + TimeZone tz = TimeZone.getTimeZone("Europe/Berlin"); + Calendar cal = Calendar.getInstance(tz, de_DE); + // Feb 5, 2015 at 10:50 CET + cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0); + final long now = cal.getTimeInMillis(); + + // 42 minutes ago + assertEquals("Vor 42 Minuten", getRelativeTimeSpanString(de_DE, tz, + now - 42 * MINUTE_IN_MILLIS, now, MINUTE_IN_MILLIS, 0)); + // In 42 minutes + assertEquals("In 42 Minuten", getRelativeTimeSpanString(de_DE, tz, + now + 42 * MINUTE_IN_MILLIS, now, MINUTE_IN_MILLIS, 0)); + // Yesterday + assertEquals("Gestern", getRelativeTimeSpanString(de_DE, tz, + now - DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0)); + // The day before yesterday + assertEquals("Vorgestern", getRelativeTimeSpanString(de_DE, tz, + now - 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0)); + // Tomorrow + assertEquals("Morgen", getRelativeTimeSpanString(de_DE, tz, + now + DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0)); + // The day after tomorrow + assertEquals("Übermorgen", getRelativeTimeSpanString(de_DE, tz, + now + 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0)); + } + + @Test + public void test_getRelativeTimeSpanStringFrench() throws Exception { + Locale fr_FR = new Locale("fr", "FR"); + TimeZone tz = TimeZone.getTimeZone("Europe/Paris"); + Calendar cal = Calendar.getInstance(tz, fr_FR); + // Feb 5, 2015 at 10:50 CET + cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0); + final long now = cal.getTimeInMillis(); + + // 42 minutes ago + assertEquals("Il y a 42 minutes", getRelativeTimeSpanString(fr_FR, tz, + now - (42 * MINUTE_IN_MILLIS), now, MINUTE_IN_MILLIS, 0)); + // In 42 minutes + assertEquals("Dans 42 minutes", getRelativeTimeSpanString(fr_FR, tz, + now + (42 * MINUTE_IN_MILLIS), now, MINUTE_IN_MILLIS, 0)); + // Yesterday + assertEquals("Hier", getRelativeTimeSpanString(fr_FR, tz, + now - DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0)); + // The day before yesterday + assertEquals("Avant-hier", getRelativeTimeSpanString(fr_FR, tz, + now - 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0)); + // Tomorrow + assertEquals("Demain", getRelativeTimeSpanString(fr_FR, tz, + now + DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0)); + // The day after tomorrow + assertEquals("Après-demain", getRelativeTimeSpanString(fr_FR, tz, + now + 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0)); + } + + // Tests adopted from CTS tests for DateUtils.getRelativeDateTimeString. + @Test + public void test_getRelativeDateTimeStringCTS() throws Exception { + Locale en_US = Locale.getDefault(); + TimeZone tz = TimeZone.getDefault(); + final long baseTime = System.currentTimeMillis(); + + final long DAY_DURATION = 5 * 24 * 60 * 60 * 1000; + assertNotNull(getRelativeDateTimeString(en_US, tz, baseTime - DAY_DURATION, baseTime, + MINUTE_IN_MILLIS, DAY_IN_MILLIS, + FORMAT_NUMERIC_DATE)); + } + + @Test + public void test_getRelativeDateTimeString() throws Exception { + Locale en_US = new Locale("en", "US"); + TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles"); + Calendar cal = Calendar.getInstance(tz, en_US); + // Feb 5, 2015 at 10:50 PST + cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0); + final long base = cal.getTimeInMillis(); + + assertEquals("5 seconds ago, 10:49 AM", + getRelativeDateTimeString(en_US, tz, base - 5 * SECOND_IN_MILLIS, base, 0, + MINUTE_IN_MILLIS, 0)); + assertEquals("5 min. ago, 10:45 AM", + getRelativeDateTimeString(en_US, tz, base - 5 * MINUTE_IN_MILLIS, base, 0, + HOUR_IN_MILLIS, FORMAT_ABBREV_RELATIVE)); + assertEquals("0 hr. ago, 10:45 AM", + getRelativeDateTimeString(en_US, tz, base - 5 * MINUTE_IN_MILLIS, base, + HOUR_IN_MILLIS, DAY_IN_MILLIS, FORMAT_ABBREV_RELATIVE)); + assertEquals("5 hours ago, 5:50 AM", + getRelativeDateTimeString(en_US, tz, base - 5 * HOUR_IN_MILLIS, base, + HOUR_IN_MILLIS, DAY_IN_MILLIS, 0)); + assertEquals("Yesterday, 7:50 PM", + getRelativeDateTimeString(en_US, tz, base - 15 * HOUR_IN_MILLIS, base, 0, + WEEK_IN_MILLIS, FORMAT_ABBREV_RELATIVE)); + assertEquals("5 days ago, 10:50 AM", + getRelativeDateTimeString(en_US, tz, base - 5 * DAY_IN_MILLIS, base, 0, + WEEK_IN_MILLIS, 0)); + assertEquals("Jan 29, 10:50 AM", + getRelativeDateTimeString(en_US, tz, base - 7 * DAY_IN_MILLIS, base, 0, + WEEK_IN_MILLIS, 0)); + assertEquals("11/27/2014, 10:50 AM", + getRelativeDateTimeString(en_US, tz, base - 10 * WEEK_IN_MILLIS, base, 0, + WEEK_IN_MILLIS, 0)); + assertEquals("11/27/2014, 10:50 AM", + getRelativeDateTimeString(en_US, tz, base - 10 * WEEK_IN_MILLIS, base, 0, + YEAR_IN_MILLIS, 0)); + + // User-supplied flags should be ignored when formatting the date clause. + final int FORMAT_SHOW_WEEKDAY = 0x00002; + assertEquals("11/27/2014, 10:50 AM", + getRelativeDateTimeString(en_US, tz, base - 10 * WEEK_IN_MILLIS, base, 0, + WEEK_IN_MILLIS, + FORMAT_ABBREV_ALL | FORMAT_SHOW_WEEKDAY)); + } + + @Test + public void test_getRelativeDateTimeStringDST() throws Exception { + Locale en_US = new Locale("en", "US"); + TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles"); + Calendar cal = Calendar.getInstance(tz, en_US); + + // DST starts on Mar 9, 2014 at 2:00 AM. + // So 5 hours before 3:15 AM should be formatted as 'Yesterday, 9:15 PM'. + cal.set(2014, Calendar.MARCH, 9, 3, 15, 0); + long base = cal.getTimeInMillis(); + assertEquals("Yesterday, 9:15 PM", + getRelativeDateTimeString(en_US, tz, base - 5 * HOUR_IN_MILLIS, base, 0, + WEEK_IN_MILLIS, 0)); + + // 1 hour after 2:00 AM should be formatted as 'In 1 hour, 4:00 AM'. + cal.set(2014, Calendar.MARCH, 9, 2, 0, 0); + base = cal.getTimeInMillis(); + assertEquals("In 1 hour, 4:00 AM", + getRelativeDateTimeString(en_US, tz, base + 1 * HOUR_IN_MILLIS, base, 0, + WEEK_IN_MILLIS, 0)); + + // DST ends on Nov 2, 2014 at 2:00 AM. Clocks are turned backward 1 hour to + // 1:00 AM. 8 hours before 5:20 AM should be 'Yesterday, 10:20 PM'. + cal.set(2014, Calendar.NOVEMBER, 2, 5, 20, 0); + base = cal.getTimeInMillis(); + assertEquals("Yesterday, 10:20 PM", + getRelativeDateTimeString(en_US, tz, base - 8 * HOUR_IN_MILLIS, base, 0, + WEEK_IN_MILLIS, 0)); + + cal.set(2014, Calendar.NOVEMBER, 2, 0, 45, 0); + base = cal.getTimeInMillis(); + // 45 minutes after 0:45 AM should be 'In 45 minutes, 1:30 AM'. + assertEquals("In 45 minutes, 1:30 AM", + getRelativeDateTimeString(en_US, tz, base + 45 * MINUTE_IN_MILLIS, base, 0, + WEEK_IN_MILLIS, 0)); + // 45 minutes later, it should be 'In 45 minutes, 1:15 AM'. + assertEquals("In 45 minutes, 1:15 AM", + getRelativeDateTimeString(en_US, tz, base + 90 * MINUTE_IN_MILLIS, + base + 45 * MINUTE_IN_MILLIS, 0, WEEK_IN_MILLIS, 0)); + // Another 45 minutes later, it should be 'In 45 minutes, 2:00 AM'. + assertEquals("In 45 minutes, 2:00 AM", + getRelativeDateTimeString(en_US, tz, base + 135 * MINUTE_IN_MILLIS, + base + 90 * MINUTE_IN_MILLIS, 0, WEEK_IN_MILLIS, 0)); + } + + @Test + public void test_getRelativeDateTimeStringItalian() throws Exception { + Locale it_IT = new Locale("it", "IT"); + TimeZone tz = TimeZone.getTimeZone("Europe/Rome"); + Calendar cal = Calendar.getInstance(tz, it_IT); + // 05 febbraio 2015 20:15 + cal.set(2015, Calendar.FEBRUARY, 5, 20, 15, 0); + final long base = cal.getTimeInMillis(); + + assertEquals("5 secondi fa, 20:14", + getRelativeDateTimeString(it_IT, tz, base - 5 * SECOND_IN_MILLIS, base, 0, + MINUTE_IN_MILLIS, 0)); + assertEquals("5 min fa, 20:10", + getRelativeDateTimeString(it_IT, tz, base - 5 * MINUTE_IN_MILLIS, base, 0, + HOUR_IN_MILLIS, FORMAT_ABBREV_RELATIVE)); + assertEquals("0 h fa, 20:10", + getRelativeDateTimeString(it_IT, tz, base - 5 * MINUTE_IN_MILLIS, base, + HOUR_IN_MILLIS, DAY_IN_MILLIS, FORMAT_ABBREV_RELATIVE)); + assertEquals("Ieri, 22:15", + getRelativeDateTimeString(it_IT, tz, base - 22 * HOUR_IN_MILLIS, base, 0, + WEEK_IN_MILLIS, FORMAT_ABBREV_RELATIVE)); + assertEquals("5 giorni fa, 20:15", + getRelativeDateTimeString(it_IT, tz, base - 5 * DAY_IN_MILLIS, base, 0, + WEEK_IN_MILLIS, 0)); + assertEquals("27/11/2014, 20:15", + getRelativeDateTimeString(it_IT, tz, base - 10 * WEEK_IN_MILLIS, base, 0, + WEEK_IN_MILLIS, 0)); + } + + // http://b/5252772: detect the actual date difference + @Test + public void test5252772() throws Exception { + Locale en_US = new Locale("en", "US"); + TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles"); + + // Now is Sep 2, 2011, 10:23 AM PDT. + Calendar nowCalendar = Calendar.getInstance(tz, en_US); + nowCalendar.set(2011, Calendar.SEPTEMBER, 2, 10, 23, 0); + final long now = nowCalendar.getTimeInMillis(); + + // Sep 1, 2011, 10:24 AM + Calendar yesterdayCalendar1 = Calendar.getInstance(tz, en_US); + yesterdayCalendar1.set(2011, Calendar.SEPTEMBER, 1, 10, 24, 0); + long yesterday1 = yesterdayCalendar1.getTimeInMillis(); + assertEquals("Yesterday, 10:24 AM", + getRelativeDateTimeString(en_US, tz, yesterday1, now, MINUTE_IN_MILLIS, + WEEK_IN_MILLIS, 0)); + + // Sep 1, 2011, 10:22 AM + Calendar yesterdayCalendar2 = Calendar.getInstance(tz, en_US); + yesterdayCalendar2.set(2011, Calendar.SEPTEMBER, 1, 10, 22, 0); + long yesterday2 = yesterdayCalendar2.getTimeInMillis(); + assertEquals("Yesterday, 10:22 AM", + getRelativeDateTimeString(en_US, tz, yesterday2, now, MINUTE_IN_MILLIS, + WEEK_IN_MILLIS, 0)); + + // Aug 31, 2011, 10:24 AM + Calendar twoDaysAgoCalendar1 = Calendar.getInstance(tz, en_US); + twoDaysAgoCalendar1.set(2011, Calendar.AUGUST, 31, 10, 24, 0); + long twoDaysAgo1 = twoDaysAgoCalendar1.getTimeInMillis(); + assertEquals("2 days ago, 10:24 AM", + getRelativeDateTimeString(en_US, tz, twoDaysAgo1, now, MINUTE_IN_MILLIS, + WEEK_IN_MILLIS, 0)); + + // Aug 31, 2011, 10:22 AM + Calendar twoDaysAgoCalendar2 = Calendar.getInstance(tz, en_US); + twoDaysAgoCalendar2.set(2011, Calendar.AUGUST, 31, 10, 22, 0); + long twoDaysAgo2 = twoDaysAgoCalendar2.getTimeInMillis(); + assertEquals("2 days ago, 10:22 AM", + getRelativeDateTimeString(en_US, tz, twoDaysAgo2, now, MINUTE_IN_MILLIS, + WEEK_IN_MILLIS, 0)); + + // Sep 3, 2011, 10:22 AM + Calendar tomorrowCalendar1 = Calendar.getInstance(tz, en_US); + tomorrowCalendar1.set(2011, Calendar.SEPTEMBER, 3, 10, 22, 0); + long tomorrow1 = tomorrowCalendar1.getTimeInMillis(); + assertEquals("Tomorrow, 10:22 AM", + getRelativeDateTimeString(en_US, tz, tomorrow1, now, MINUTE_IN_MILLIS, + WEEK_IN_MILLIS, 0)); + + // Sep 3, 2011, 10:24 AM + Calendar tomorrowCalendar2 = Calendar.getInstance(tz, en_US); + tomorrowCalendar2.set(2011, Calendar.SEPTEMBER, 3, 10, 24, 0); + long tomorrow2 = tomorrowCalendar2.getTimeInMillis(); + assertEquals("Tomorrow, 10:24 AM", + getRelativeDateTimeString(en_US, tz, tomorrow2, now, MINUTE_IN_MILLIS, + WEEK_IN_MILLIS, 0)); + + // Sep 4, 2011, 10:22 AM + Calendar twoDaysLaterCalendar1 = Calendar.getInstance(tz, en_US); + twoDaysLaterCalendar1.set(2011, Calendar.SEPTEMBER, 4, 10, 22, 0); + long twoDaysLater1 = twoDaysLaterCalendar1.getTimeInMillis(); + assertEquals("In 2 days, 10:22 AM", + getRelativeDateTimeString(en_US, tz, twoDaysLater1, now, MINUTE_IN_MILLIS, + WEEK_IN_MILLIS, 0)); + + // Sep 4, 2011, 10:24 AM + Calendar twoDaysLaterCalendar2 = Calendar.getInstance(tz, en_US); + twoDaysLaterCalendar2.set(2011, Calendar.SEPTEMBER, 4, 10, 24, 0); + long twoDaysLater2 = twoDaysLaterCalendar2.getTimeInMillis(); + assertEquals("In 2 days, 10:24 AM", + getRelativeDateTimeString(en_US, tz, twoDaysLater2, now, MINUTE_IN_MILLIS, + WEEK_IN_MILLIS, 0)); + } + + // b/19822016: show / hide the year based on the dates in the arguments. + @Test + public void test_bug19822016() throws Exception { + Locale en_US = new Locale("en", "US"); + TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles"); + Calendar cal = Calendar.getInstance(tz, en_US); + // Feb 5, 2012 at 10:50 PST + cal.set(2012, Calendar.FEBRUARY, 5, 10, 50, 0); + long base = cal.getTimeInMillis(); + + assertEquals("Feb 5, 5:50 AM", getRelativeDateTimeString(en_US, tz, + base - 5 * HOUR_IN_MILLIS, base, 0, MINUTE_IN_MILLIS, 0)); + assertEquals("Jan 29, 10:50 AM", getRelativeDateTimeString(en_US, tz, + base - 7 * DAY_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0)); + assertEquals("11/27/2011, 10:50 AM", getRelativeDateTimeString(en_US, tz, + base - 10 * WEEK_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0)); + + assertEquals("January 6", getRelativeTimeSpanString(en_US, tz, + base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0)); + assertEquals("January 6", getRelativeTimeSpanString(en_US, tz, + base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR)); + assertEquals("January 6, 2012", getRelativeTimeSpanString(en_US, tz, + base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR)); + assertEquals("December 7, 2011", getRelativeTimeSpanString(en_US, tz, + base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0)); + assertEquals("December 7, 2011", getRelativeTimeSpanString(en_US, tz, + base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR)); + assertEquals("December 7", getRelativeTimeSpanString(en_US, tz, + base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR)); + + // Feb 5, 2018 at 10:50 PST + cal.set(2018, Calendar.FEBRUARY, 5, 10, 50, 0); + base = cal.getTimeInMillis(); + assertEquals("Feb 5, 5:50 AM", getRelativeDateTimeString(en_US, tz, + base - 5 * HOUR_IN_MILLIS, base, 0, MINUTE_IN_MILLIS, 0)); + assertEquals("Jan 29, 10:50 AM", getRelativeDateTimeString(en_US, tz, + base - 7 * DAY_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0)); + assertEquals("11/27/2017, 10:50 AM", getRelativeDateTimeString(en_US, tz, + base - 10 * WEEK_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0)); + + assertEquals("January 6", getRelativeTimeSpanString(en_US, tz, + base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0)); + assertEquals("January 6", getRelativeTimeSpanString(en_US, tz, + base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR)); + assertEquals("January 6, 2018", getRelativeTimeSpanString(en_US, tz, + base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR)); + assertEquals("December 7, 2017", getRelativeTimeSpanString(en_US, tz, + base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0)); + assertEquals("December 7, 2017", getRelativeTimeSpanString(en_US, tz, + base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR)); + assertEquals("December 7", getRelativeTimeSpanString(en_US, tz, + base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR)); + } + + // Check for missing ICU data. http://b/25821045 + @Test + public void test_bug25821045() { + final TimeZone tz = TimeZone.getDefault(); + final long now = System.currentTimeMillis(); + final long time = now + 1000; + final int minResolution = 1000 * 60; + final int transitionResolution = minResolution; + final int flags = FORMAT_ABBREV_RELATIVE; + // Exercise all available locales, forcing the ICU implementation to pre-cache the data. + // This + // highlights data issues. It can take a while. + for (Locale locale : Locale.getAvailableLocales()) { + // In (e.g.) ICU56 an exception is thrown on the first use for a locale if required + // data for + // the "other" plural is missing. It doesn't matter what is actually formatted. + try { + RelativeDateTimeFormatter.getRelativeDateTimeString( + locale, tz, time, now, minResolution, transitionResolution, flags); + } catch (IllegalStateException e) { + fail("Failed to format for " + locale); + } + } + } + + // Check for ICU data lookup fallback failure. http://b/25883157 + @Test + public void test_bug25883157() { + final Locale locale = new Locale("en", "GB"); + final TimeZone tz = TimeZone.getTimeZone("GMT"); + + final Calendar cal = Calendar.getInstance(tz, locale); + cal.set(2015, Calendar.JUNE, 19, 12, 0, 0); + + final long base = cal.getTimeInMillis(); + final long time = base + 2 * WEEK_IN_MILLIS; + + assertEquals("In 2 wk", getRelativeTimeSpanString( + locale, tz, time, base, WEEK_IN_MILLIS, FORMAT_ABBREV_RELATIVE)); + } + + // http://b/63745717 + @Test + public void test_combineDateAndTime_apostrophe() { + final Locale locale = new Locale("fr"); + android.icu.text.RelativeDateTimeFormatter icuFormatter = + android.icu.text.RelativeDateTimeFormatter.getInstance(locale); + assertEquals("D à T", icuFormatter.combineDateAndTime("D", "T")); + // Ensure single quote ' and curly braces {} are not interpreted in input values. + assertEquals("D'x' à T{0}", icuFormatter.combineDateAndTime("D'x'", "T{0}")); + } +} diff --git a/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java b/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java index 76aa93f7e8be..edf473eac1b1 100644 --- a/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java @@ -16,27 +16,25 @@ package com.android.internal.util; -import java.util.Collection; -import java.util.Iterator; - import android.os.Debug; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.os.SystemClock; import android.os.test.TestLooper; - -import android.test.suitebuilder.annotation.Suppress; -import com.android.internal.util.State; -import com.android.internal.util.StateMachine; -import com.android.internal.util.StateMachine.LogRec; - import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; import android.util.Log; +import com.android.internal.util.StateMachine.LogRec; + import junit.framework.TestCase; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Collection; +import java.util.Iterator; + /** * Test for StateMachine. */ @@ -2013,4 +2011,12 @@ public class StateMachineTest extends TestCase { private static void tloge(String s) { Log.e(TAG, s); } + + public void testDumpDoesNotThrowNpeAfterQuit() { + final Hsm1 sm = Hsm1.makeHsm1(); + sm.quitNow(); + final StringWriter stringWriter = new StringWriter(); + final PrintWriter printWriter = new PrintWriter(stringWriter); + sm.dump(null, printWriter, new String[0]); + } } diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 0d12e1f7f83d..99b5a062e44e 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -50,6 +50,14 @@ prebuilt_etc { } prebuilt_etc { + name: "privapp_whitelist_com.android.cellbroadcastreceiver", + system_ext_specific: true, + sub_dir: "permissions", + src: "com.android.cellbroadcastreceiver.xml", + filename_from_src: true, +} + +prebuilt_etc { name: "privapp_whitelist_com.android.contacts", product_specific: true, sub_dir: "permissions", diff --git a/data/etc/com.android.cellbroadcastreceiver.xml b/data/etc/com.android.cellbroadcastreceiver.xml new file mode 100644 index 000000000000..dd2df42e442f --- /dev/null +++ b/data/etc/com.android.cellbroadcastreceiver.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.cellbroadcastreceiver"> + <permission name="android.permission.INTERACT_ACROSS_USERS"/> + <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.MODIFY_PHONE_STATE"/> + <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> + <permission name="android.permission.RECEIVE_EMERGENCY_BROADCAST"/> + <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/> + </privapp-permissions> +</permissions> diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl index 8699cb491dff..e0fca1e044df 100644 --- a/data/keyboards/Generic.kl +++ b/data/keyboards/Generic.kl @@ -352,7 +352,7 @@ key 397 CALENDAR key 402 CHANNEL_UP key 403 CHANNEL_DOWN # key 404 "KEY_FIRST" -# key 405 "KEY_LAST" +key 405 LAST_CHANNEL # key 406 "KEY_AB" # key 407 "KEY_NEXT" # key 408 "KEY_RESTART" diff --git a/drm/jni/android_drm_DrmManagerClient.cpp b/drm/jni/android_drm_DrmManagerClient.cpp index 52ea8e3b161f..e2bb6a6f4d1a 100644 --- a/drm/jni/android_drm_DrmManagerClient.cpp +++ b/drm/jni/android_drm_DrmManagerClient.cpp @@ -19,7 +19,7 @@ #include <utils/Log.h> #include <jni.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <nativehelper/ScopedLocalRef.h> #include <android_runtime/AndroidRuntime.h> diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java index 493c85a930be..4eb6e420c07f 100644 --- a/identity/java/android/security/identity/IdentityCredential.java +++ b/identity/java/android/security/identity/IdentityCredential.java @@ -41,19 +41,18 @@ public abstract class IdentityCredential { /** * Create an ephemeral key pair to use to establish a secure channel with a reader. * - * <p>Most applications will use only the public key, and only to send it to the reader, - * allowing the private key to be used internally for {@link #encryptMessageToReader(byte[])} - * and {@link #decryptMessageFromReader(byte[])}. The private key is also provided for - * applications that wish to use a cipher suite that is not supported by - * {@link IdentityCredentialStore}. + * <p>Applications should use this key-pair for the communications channel with the reader + * using a protocol / cipher-suite appropriate for the application. One example of such a + * protocol is the one used for Mobile Driving Licenses, see ISO 18013-5 section 9.2.1 "Session + * encryption". * * @return ephemeral key pair to use to establish a secure channel with a reader. */ public @NonNull abstract KeyPair createEphemeralKeyPair(); /** - * Set the ephemeral public key provided by the reader. This must be called before - * {@link #encryptMessageToReader} or {@link #decryptMessageFromReader} can be called. + * Set the ephemeral public key provided by the reader. If called, this must be called before + * {@link #getEntries(byte[], Map, byte[], byte[])} is called. * * @param readerEphemeralPublicKey The ephemeral public key provided by the reader to * establish a secure session. @@ -65,6 +64,11 @@ public abstract class IdentityCredential { /** * Encrypt a message for transmission to the reader. * + * <p>Do not use. In this version of the API, this method produces an incorrect + * result. Instead, applications should implement message encryption/decryption themselves as + * detailed in the {@link #createEphemeralKeyPair()} method. In a future API-level, this + * method will be deprecated. + * * @param messagePlaintext unencrypted message to encrypt. * @return encrypted message. */ @@ -73,6 +77,11 @@ public abstract class IdentityCredential { /** * Decrypt a message received from the reader. * + * <p>Do not use. In this version of the API, this method produces an incorrect + * result. Instead, applications should implement message encryption/decryption themselves as + * detailed in the {@link #createEphemeralKeyPair()} method. In a future API-level, this + * method will be deprecated. + * * @param messageCiphertext encrypted message to decrypt. * @return decrypted message. * @throws MessageDecryptionException if the ciphertext couldn't be decrypted. @@ -178,7 +187,7 @@ public abstract class IdentityCredential { * * <p>If {@code readerAuth} is not {@code null} it must be the bytes of a {@code COSE_Sign1} * structure as defined in RFC 8152. For the payload nil shall be used and the - * detached payload is the ReaderAuthentication CBOR described below. + * detached payload is the ReaderAuthenticationBytes CBOR described below. * <pre> * ReaderAuthentication = [ * "ReaderAuthentication", @@ -186,7 +195,9 @@ public abstract class IdentityCredential { * ItemsRequestBytes * ] * - * ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest) ; Bytes of ItemsRequest + * ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest) + * + * ReaderAuthenticationBytes = #6.24(bstr .cbor ReaderAuthentication) * </pre> * * <p>where {@code ItemsRequestBytes} are the bytes in the {@code requestMessage} parameter. diff --git a/identity/java/android/security/identity/ResultData.java b/identity/java/android/security/identity/ResultData.java index 37de2c4a50ea..71860d261285 100644 --- a/identity/java/android/security/identity/ResultData.java +++ b/identity/java/android/security/identity/ResultData.java @@ -68,8 +68,8 @@ public abstract class ResultData { * {@link #getMessageAuthenticationCode()} can be used to get a MAC. * * <p>The CBOR structure which is cryptographically authenticated is the - * {@code DeviceAuthentication} structure according to the following - * <a href="https://tools.ietf.org/html/draft-ietf-cbor-cddl-06">CDDL</a> schema: + * {@code DeviceAuthenticationBytes} structure according to the following + * <a href="https://tools.ietf.org/html/rfc8610">CDDL</a> schema: * * <pre> * DeviceAuthentication = [ @@ -80,15 +80,9 @@ public abstract class ResultData { * ] * * DocType = tstr - * - * SessionTranscript = [ - * DeviceEngagementBytes, - * EReaderKeyBytes - * ] - * - * DeviceEngagementBytes = #6.24(bstr .cbor DeviceEngagement) - * EReaderKeyBytes = #6.24(bstr .cbor EReaderKey.Pub) + * SessionTranscript = any * DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces) + * DeviceAuthenticationBytes = #6.24(bstr .cbor DeviceAuthentication) * </pre> * * <p>where @@ -115,7 +109,7 @@ public abstract class ResultData { public abstract @NonNull byte[] getAuthenticatedData(); /** - * Returns a message authentication code over the {@code DeviceAuthentication} CBOR + * Returns a message authentication code over the {@code DeviceAuthenticationBytes} CBOR * specified in {@link #getAuthenticatedData()}, to prove to the reader that the data * is from a trusted credential. * diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java index 7282bcfe4445..62194d84f4f5 100644 --- a/keystore/java/android/security/Credentials.java +++ b/keystore/java/android/security/Credentials.java @@ -74,6 +74,15 @@ public class Credentials { /** Key containing suffix of lockdown VPN profile. */ public static final String LOCKDOWN_VPN = "LOCKDOWN_VPN"; + /** Name of CA certificate usage. */ + public static final String CERTIFICATE_USAGE_CA = "ca"; + + /** Name of User certificate usage. */ + public static final String CERTIFICATE_USAGE_USER = "user"; + + /** Name of WIFI certificate usage. */ + public static final String CERTIFICATE_USAGE_WIFI = "wifi"; + /** Data type for public keys. */ public static final String EXTRA_PUBLIC_KEY = "KEY"; @@ -94,19 +103,19 @@ public class Credentials { public static final String EXTRA_INSTALL_AS_UID = "install_as_uid"; /** - * Intent extra: name for the user's private key. + * Intent extra: type of the certificate to install */ - public static final String EXTRA_USER_PRIVATE_KEY_NAME = "user_private_key_name"; + public static final String EXTRA_CERTIFICATE_USAGE = "certificate_install_usage"; /** - * Intent extra: data for the user's private key in PEM-encoded PKCS#8. + * Intent extra: name for the user's key pair. */ - public static final String EXTRA_USER_PRIVATE_KEY_DATA = "user_private_key_data"; + public static final String EXTRA_USER_KEY_ALIAS = "user_key_pair_name"; /** - * Intent extra: name for the user's certificate. + * Intent extra: data for the user's private key in PEM-encoded PKCS#8. */ - public static final String EXTRA_USER_CERTIFICATE_NAME = "user_certificate_name"; + public static final String EXTRA_USER_PRIVATE_KEY_DATA = "user_private_key_data"; /** * Intent extra: data for the user's certificate in PEM-encoded X.509. @@ -114,11 +123,6 @@ public class Credentials { public static final String EXTRA_USER_CERTIFICATE_DATA = "user_certificate_data"; /** - * Intent extra: name for CA certificate chain - */ - public static final String EXTRA_CA_CERTIFICATES_NAME = "ca_certificates_name"; - - /** * Intent extra: data for CA certificate chain in PEM-encoded X.509. */ public static final String EXTRA_CA_CERTIFICATES_DATA = "ca_certificates_data"; diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl index b3cdff7eedf7..97da3cc6f80f 100644 --- a/keystore/java/android/security/IKeyChainService.aidl +++ b/keystore/java/android/security/IKeyChainService.aidl @@ -43,7 +43,8 @@ interface IKeyChainService { String installCaCertificate(in byte[] caCertificate); // APIs used by DevicePolicyManager - boolean installKeyPair(in byte[] privateKey, in byte[] userCert, in byte[] certChain, String alias); + boolean installKeyPair( + in byte[] privateKey, in byte[] userCert, in byte[] certChain, String alias, int uid); boolean removeKeyPair(String alias); // APIs used by Settings diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index 1829d2f406b4..254456cea536 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -343,6 +343,16 @@ public final class KeyChain { public static final int KEY_ATTESTATION_FAILURE = 4; /** + * Used by DPC or delegated app in + * {@link android.app.admin.DeviceAdminReceiver#onChoosePrivateKeyAlias} or + * {@link android.app.admin.DelegatedAdminReceiver#onChoosePrivateKeyAlias} to identify that + * the requesting app is not granted access to any key, and nor will the user be able to grant + * access manually. + */ + public static final String KEY_ALIAS_SELECTION_DENIED = + "android:alias-selection-denied"; + + /** * Returns an {@code Intent} that can be used for credential * installation. The intent may be used without any extras, in * which case the user will be able to install credentials from diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java index 11775caa8ad4..6e389389fbc0 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -31,7 +31,6 @@ import com.android.org.bouncycastle.asn1.ASN1InputStream; import com.android.org.bouncycastle.asn1.ASN1Integer; import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier; import com.android.org.bouncycastle.asn1.DERBitString; -import com.android.org.bouncycastle.asn1.DERInteger; import com.android.org.bouncycastle.asn1.DERNull; import com.android.org.bouncycastle.asn1.DERSequence; import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; @@ -699,8 +698,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato sigAlgOid = X9ObjectIdentifiers.ecdsa_with_SHA256; sigAlgId = new AlgorithmIdentifier(sigAlgOid); ASN1EncodableVector v = new ASN1EncodableVector(); - v.add(new DERInteger(0)); - v.add(new DERInteger(0)); + v.add(new ASN1Integer(BigInteger.valueOf(0))); + v.add(new ASN1Integer(BigInteger.valueOf(0))); signature = new DERSequence().getEncoded(); break; case KeymasterDefs.KM_ALGORITHM_RSA: diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index 5c2ef15cbd1e..5e480a66c355 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -24,37 +24,10 @@ #include <log/log.h> -#include <SkBitmap.h> -#include <SkCanvas.h> -#include <SkColor.h> -#include <SkPaint.h> -#include <SkBlendMode.h> +#include <memory> namespace android { -// --- WeakLooperCallback --- - -class WeakLooperCallback: public LooperCallback { -protected: - virtual ~WeakLooperCallback() { } - -public: - explicit WeakLooperCallback(const wp<LooperCallback>& callback) : - mCallback(callback) { - } - - virtual int handleEvent(int fd, int events, void* data) { - sp<LooperCallback> callback = mCallback.promote(); - if (callback != NULL) { - return callback->handleEvent(fd, events, data); - } - return 0; // the client is gone, remove the callback - } - -private: - wp<LooperCallback> mCallback; -}; - // --- PointerController --- // Time to wait before starting the fade when the pointer is inactive. @@ -70,29 +43,50 @@ static const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms // The number of events to be read at once for DisplayEventReceiver. static const int EVENT_BUFFER_SIZE = 100; -// --- PointerController --- - -PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy, - const sp<Looper>& looper, const sp<SpriteController>& spriteController) : - mPolicy(policy), mLooper(looper), mSpriteController(spriteController) { - mHandler = new WeakMessageHandler(this); - mCallback = new WeakLooperCallback(this); - - if (mDisplayEventReceiver.initCheck() == NO_ERROR) { - mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK, - Looper::EVENT_INPUT, mCallback, nullptr); +std::shared_ptr<PointerController> PointerController::create( + const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, + const sp<SpriteController>& spriteController) { + std::shared_ptr<PointerController> controller = std::shared_ptr<PointerController>( + new PointerController(policy, looper, spriteController)); + + /* + * Now we need to hook up the constructed PointerController object to its callbacks. + * + * This must be executed after the constructor but before any other methods on PointerController + * in order to ensure that the fully constructed object is visible on the Looper thread, since + * that may be a different thread than where the PointerController is initially constructed. + * + * Unfortunately, this cannot be done as part of the constructor since we need to hand out + * weak_ptr's which themselves cannot be constructed until there's at least one shared_ptr. + */ + + controller->mHandler->pointerController = controller; + controller->mCallback->pointerController = controller; + if (controller->mDisplayEventReceiver.initCheck() == NO_ERROR) { + controller->mLooper->addFd(controller->mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK, + Looper::EVENT_INPUT, controller->mCallback, nullptr); } else { ALOGE("Failed to initialize DisplayEventReceiver."); } + return controller; +} +PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy, + const sp<Looper>& looper, + const sp<SpriteController>& spriteController) + : mPolicy(policy), + mLooper(looper), + mSpriteController(spriteController), + mHandler(new MessageHandler()), + mCallback(new LooperCallback()) { AutoMutex _l(mLock); mLocked.animationPending = false; - mLocked.presentation = PRESENTATION_POINTER; + mLocked.presentation = Presentation::POINTER; mLocked.presentationChanged = false; - mLocked.inactivityTimeout = INACTIVITY_TIMEOUT_NORMAL; + mLocked.inactivityTimeout = InactivityTimeout::NORMAL; mLocked.pointerFadeDirection = 0; mLocked.pointerX = 0; @@ -227,7 +221,7 @@ void PointerController::fade(Transition transition) { removeInactivityTimeoutLocked(); // Start fading. - if (transition == TRANSITION_IMMEDIATE) { + if (transition == Transition::IMMEDIATE) { mLocked.pointerFadeDirection = 0; mLocked.pointerAlpha = 0.0f; updatePointerLocked(); @@ -244,7 +238,7 @@ void PointerController::unfade(Transition transition) { resetInactivityTimeoutLocked(); // Start unfading. - if (transition == TRANSITION_IMMEDIATE) { + if (transition == Transition::IMMEDIATE) { mLocked.pointerFadeDirection = 0; mLocked.pointerAlpha = 1.0f; updatePointerLocked(); @@ -268,7 +262,7 @@ void PointerController::setPresentation(Presentation presentation) { return; } - if (presentation == PRESENTATION_POINTER) { + if (presentation == Presentation::POINTER) { if (mLocked.additionalMouseResources.empty()) { mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, &mLocked.animationResources, @@ -486,24 +480,35 @@ void PointerController::setCustomPointerIcon(const SpriteIcon& icon) { updatePointerLocked(); } -void PointerController::handleMessage(const Message& message) { +void PointerController::MessageHandler::handleMessage(const Message& message) { + std::shared_ptr<PointerController> controller = pointerController.lock(); + + if (controller == nullptr) { + ALOGE("PointerController instance was released before processing message: what=%d", + message.what); + return; + } switch (message.what) { case MSG_INACTIVITY_TIMEOUT: - doInactivityTimeout(); + controller->doInactivityTimeout(); break; } } -int PointerController::handleEvent(int /* fd */, int events, void* /* data */) { +int PointerController::LooperCallback::handleEvent(int /* fd */, int events, void* /* data */) { + std::shared_ptr<PointerController> controller = pointerController.lock(); + if (controller == nullptr) { + ALOGW("PointerController instance was released with pending callbacks. events=0x%x", + events); + return 0; // Remove the callback, the PointerController is gone anyways + } if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { - ALOGE("Display event receiver pipe was closed or an error occurred. " - "events=0x%x", events); + ALOGE("Display event receiver pipe was closed or an error occurred. events=0x%x", events); return 0; // remove the callback } if (!(events & Looper::EVENT_INPUT)) { - ALOGW("Received spurious callback for unhandled poll event. " - "events=0x%x", events); + ALOGW("Received spurious callback for unhandled poll event. events=0x%x", events); return 1; // keep the callback } @@ -511,7 +516,7 @@ int PointerController::handleEvent(int /* fd */, int events, void* /* data */) { ssize_t n; nsecs_t timestamp; DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; - while ((n = mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { + while ((n = controller->mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { for (size_t i = 0; i < static_cast<size_t>(n); ++i) { if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { timestamp = buf[i].header.timestamp; @@ -520,7 +525,7 @@ int PointerController::handleEvent(int /* fd */, int events, void* /* data */) { } } if (gotVsync) { - doAnimate(timestamp); + controller->doAnimate(timestamp); } return 1; // keep the callback } @@ -619,7 +624,7 @@ bool PointerController::doBitmapAnimationLocked(nsecs_t timestamp) { } void PointerController::doInactivityTimeout() { - fade(TRANSITION_GRADUAL); + fade(Transition::GRADUAL); } void PointerController::startAnimationLocked() { @@ -633,8 +638,9 @@ void PointerController::startAnimationLocked() { void PointerController::resetInactivityTimeoutLocked() { mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT); - nsecs_t timeout = mLocked.inactivityTimeout == INACTIVITY_TIMEOUT_SHORT - ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL; + nsecs_t timeout = mLocked.inactivityTimeout == InactivityTimeout::SHORT + ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT + : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL; mLooper->sendMessageDelayed(timeout, mHandler, MSG_INACTIVITY_TIMEOUT); } @@ -661,7 +667,7 @@ void PointerController::updatePointerLocked() REQUIRES(mLock) { } if (mLocked.pointerIconChanged || mLocked.presentationChanged) { - if (mLocked.presentation == PRESENTATION_POINTER) { + if (mLocked.presentation == Presentation::POINTER) { if (mLocked.requestedPointerType == mPolicy->getDefaultPointerIconId()) { mLocked.pointerSprite->setIcon(mLocked.pointerIcon); } else { @@ -737,7 +743,7 @@ PointerController::Spot* PointerController::removeFirstFadingSpotLocked(std::vec return spot; } } - return NULL; + return nullptr; } void PointerController::releaseSpotLocked(Spot* spot) { @@ -778,7 +784,7 @@ void PointerController::loadResourcesLocked() REQUIRES(mLock) { mLocked.additionalMouseResources.clear(); mLocked.animationResources.clear(); - if (mLocked.presentation == PRESENTATION_POINTER) { + if (mLocked.presentation == Presentation::POINTER) { mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, &mLocked.animationResources, mLocked.viewport.displayId); } diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index ebc622bae302..14c0679654c6 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -17,19 +17,20 @@ #ifndef _UI_POINTER_CONTROLLER_H #define _UI_POINTER_CONTROLLER_H -#include "SpriteController.h" - -#include <map> -#include <vector> - -#include <ui/DisplayInfo.h> +#include <PointerControllerInterface.h> +#include <gui/DisplayEventReceiver.h> #include <input/DisplayViewport.h> #include <input/Input.h> -#include <PointerControllerInterface.h> +#include <ui/DisplayInfo.h> #include <utils/BitSet.h> -#include <utils/RefBase.h> #include <utils/Looper.h> -#include <gui/DisplayEventReceiver.h> +#include <utils/RefBase.h> + +#include <map> +#include <memory> +#include <vector> + +#include "SpriteController.h" namespace android { @@ -70,25 +71,22 @@ public: virtual int32_t getCustomPointerIconId() = 0; }; - /* * Tracks pointer movements and draws the pointer sprite to a surface. * * Handles pointer acceleration and animation. */ -class PointerController : public PointerControllerInterface, public MessageHandler, - public LooperCallback { -protected: - virtual ~PointerController(); - +class PointerController : public PointerControllerInterface { public: - enum InactivityTimeout { - INACTIVITY_TIMEOUT_NORMAL = 0, - INACTIVITY_TIMEOUT_SHORT = 1, + static std::shared_ptr<PointerController> create( + const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, + const sp<SpriteController>& spriteController); + enum class InactivityTimeout { + NORMAL = 0, + SHORT = 1, }; - PointerController(const sp<PointerControllerPolicyInterface>& policy, - const sp<Looper>& looper, const sp<SpriteController>& spriteController); + virtual ~PointerController(); virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; @@ -113,8 +111,8 @@ public: void reloadPointerResources(); private: - static const size_t MAX_RECYCLED_SPRITES = 12; - static const size_t MAX_SPOTS = 12; + static constexpr size_t MAX_RECYCLED_SPRITES = 12; + static constexpr size_t MAX_SPOTS = 12; enum { MSG_INACTIVITY_TIMEOUT, @@ -130,8 +128,13 @@ private: float x, y; inline Spot(uint32_t id, const sp<Sprite>& sprite) - : id(id), sprite(sprite), alpha(1.0f), scale(1.0f), - x(0.0f), y(0.0f), lastIcon(NULL) { } + : id(id), + sprite(sprite), + alpha(1.0f), + scale(1.0f), + x(0.0f), + y(0.0f), + lastIcon(nullptr) {} void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId); @@ -139,12 +142,24 @@ private: const SpriteIcon* lastIcon; }; + class MessageHandler : public virtual android::MessageHandler { + public: + void handleMessage(const Message& message) override; + std::weak_ptr<PointerController> pointerController; + }; + + class LooperCallback : public virtual android::LooperCallback { + public: + int handleEvent(int fd, int events, void* data) override; + std::weak_ptr<PointerController> pointerController; + }; + mutable Mutex mLock; sp<PointerControllerPolicyInterface> mPolicy; sp<Looper> mLooper; sp<SpriteController> mSpriteController; - sp<WeakMessageHandler> mHandler; + sp<MessageHandler> mHandler; sp<LooperCallback> mCallback; DisplayEventReceiver mDisplayEventReceiver; @@ -181,14 +196,15 @@ private: int32_t buttonState; std::map<int32_t /* displayId */, std::vector<Spot*>> spotsByDisplay; - std::vector<sp<Sprite> > recycledSprites; + std::vector<sp<Sprite>> recycledSprites; } mLocked GUARDED_BY(mLock); + PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, + const sp<SpriteController>& spriteController); + bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; void setPositionLocked(float x, float y); - void handleMessage(const Message& message); - int handleEvent(int fd, int events, void* data); void doAnimate(nsecs_t timestamp); bool doFadingAnimationLocked(nsecs_t timestamp); bool doBitmapAnimationLocked(nsecs_t timestamp); diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp index a15742671dc7..6e129a064385 100644 --- a/libs/input/tests/PointerController_test.cpp +++ b/libs/input/tests/PointerController_test.cpp @@ -136,7 +136,7 @@ protected: sp<MockSprite> mPointerSprite; sp<MockPointerControllerPolicyInterface> mPolicy; sp<MockSpriteController> mSpriteController; - sp<PointerController> mPointerController; + std::shared_ptr<PointerController> mPointerController; private: void loopThread(); @@ -160,7 +160,7 @@ PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<Moc EXPECT_CALL(*mSpriteController, createSprite()) .WillOnce(Return(mPointerSprite)); - mPointerController = new PointerController(mPolicy, mLooper, mSpriteController); + mPointerController = PointerController::create(mPolicy, mLooper, mSpriteController); } PointerControllerTest::~PointerControllerTest() { @@ -193,7 +193,7 @@ void PointerControllerTest::loopThread() { TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) { ensureDisplayViewportIsSet(); - mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); + mPointerController->unfade(PointerController::Transition::IMMEDIATE); std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT); EXPECT_CALL(*mPointerSprite, setVisible(true)); @@ -208,7 +208,7 @@ TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) { TEST_F(PointerControllerTest, updatePointerIcon) { ensureDisplayViewportIsSet(); - mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); + mPointerController->unfade(PointerController::Transition::IMMEDIATE); int32_t type = CURSOR_TYPE_ADDITIONAL; std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type); @@ -224,7 +224,7 @@ TEST_F(PointerControllerTest, updatePointerIcon) { TEST_F(PointerControllerTest, setCustomPointerIcon) { ensureDisplayViewportIsSet(); - mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); + mPointerController->unfade(PointerController::Transition::IMMEDIATE); int32_t style = CURSOR_TYPE_CUSTOM; float hotSpotX = 15; @@ -246,13 +246,13 @@ TEST_F(PointerControllerTest, setCustomPointerIcon) { } TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) { - mPointerController->setPresentation(PointerController::PRESENTATION_POINTER); + mPointerController->setPresentation(PointerController::Presentation::POINTER); mPointerController->setSpots(nullptr, nullptr, BitSet32(), -1); mPointerController->clearSpots(); mPointerController->setPosition(1.0f, 1.0f); mPointerController->move(1.0f, 1.0f); - mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); - mPointerController->fade(PointerController::TRANSITION_IMMEDIATE); + mPointerController->unfade(PointerController::Transition::IMMEDIATE); + mPointerController->fade(PointerController::Transition::IMMEDIATE); EXPECT_TRUE(mPolicy->noResourcesAreLoaded()); diff --git a/media/jni/android_media_JetPlayer.cpp b/media/jni/android_media_JetPlayer.cpp index 8a05f85e4285..481f80b278f8 100644 --- a/media/jni/android_media_JetPlayer.cpp +++ b/media/jni/android_media_JetPlayer.cpp @@ -23,7 +23,7 @@ #include <fcntl.h> #include <jni.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include "core_jni_helpers.h" #include <utils/Log.h> diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp index f5ae9d0d5d2f..0d395e58a3c5 100644 --- a/media/jni/android_media_MediaExtractor.cpp +++ b/media/jni/android_media_MediaExtractor.cpp @@ -28,7 +28,7 @@ #include "android_runtime/Log.h" #include "android_util_Binder.h" #include "jni.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <android/hardware/cas/1.0/BpHwCas.h> #include <android/hardware/cas/1.0/BnHwCas.h> diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp index a4807843d7d8..91cd6d3826f6 100644 --- a/media/jni/android_media_MediaMetadataRetriever.cpp +++ b/media/jni/android_media_MediaMetadataRetriever.cpp @@ -30,7 +30,7 @@ #include <private/media/VideoFrame.h> #include "jni.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include "android_runtime/AndroidRuntime.h" #include "android_media_MediaDataSource.h" #include "android_media_Streams.h" diff --git a/media/jni/android_media_MediaMuxer.cpp b/media/jni/android_media_MediaMuxer.cpp index f0aa4c3f1ab6..243ee4f7ebba 100644 --- a/media/jni/android_media_MediaMuxer.cpp +++ b/media/jni/android_media_MediaMuxer.cpp @@ -21,7 +21,7 @@ #include "android_media_Streams.h" #include "android_runtime/AndroidRuntime.h" #include "jni.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <unistd.h> #include <fcntl.h> diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index ec0ce8759571..52e266129875 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -32,7 +32,7 @@ #include <fcntl.h> #include <utils/threads.h> #include "jni.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include "android_runtime/AndroidRuntime.h" #include "android_runtime/android_view_Surface.h" #include "android_runtime/Log.h" diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp index 24fff0635238..a70de6d6f5d3 100644 --- a/media/jni/android_media_MediaRecorder.cpp +++ b/media/jni/android_media_MediaRecorder.cpp @@ -37,7 +37,7 @@ #include <nativehelper/ScopedUtfChars.h> #include "jni.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include "android_media_AudioErrors.h" #include "android_media_MediaMetricsJNI.h" #include "android_media_MicrophoneInfo.h" diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp index 58044c0307eb..1fb0faa5cd58 100644 --- a/media/jni/android_media_MediaScanner.cpp +++ b/media/jni/android_media_MediaScanner.cpp @@ -24,7 +24,7 @@ #include <private/media/VideoFrame.h> #include "jni.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include "android_runtime/AndroidRuntime.h" #include "android_runtime/Log.h" #include <android-base/macros.h> // for FALLTHROUGH_INTENDED diff --git a/media/jni/android_mtp_MtpServer.cpp b/media/jni/android_mtp_MtpServer.cpp index 39ff04ab2a0a..8a1ae9252ca3 100644 --- a/media/jni/android_mtp_MtpServer.cpp +++ b/media/jni/android_mtp_MtpServer.cpp @@ -25,7 +25,7 @@ #include <utils/threads.h> #include "jni.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include "android_runtime/AndroidRuntime.h" #include "private/android_filesystem_config.h" diff --git a/media/jni/audioeffect/Visualizer.cpp b/media/jni/audioeffect/Visualizer.cpp index efeb3352d393..f4d65d0a397f 100644 --- a/media/jni/audioeffect/Visualizer.cpp +++ b/media/jni/audioeffect/Visualizer.cpp @@ -25,6 +25,7 @@ #include <limits.h> #include <audio_utils/fixedfft.h> +#include <cutils/bitops.h> #include <utils/Thread.h> #include "Visualizer.h" diff --git a/media/jni/soundpool/android_media_SoundPool.cpp b/media/jni/soundpool/android_media_SoundPool.cpp index f6706369f379..bfecd6b30179 100644 --- a/media/jni/soundpool/android_media_SoundPool.cpp +++ b/media/jni/soundpool/android_media_SoundPool.cpp @@ -21,7 +21,7 @@ #include <utils/Log.h> #include <jni.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <android_runtime/AndroidRuntime.h> #include "SoundPool.h" diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt index 3ad076f60c08..c55eeeb50b26 100644 --- a/non-updatable-api/current.txt +++ b/non-updatable-api/current.txt @@ -40944,6 +40944,7 @@ package android.security { field public static final String EXTRA_KEY_ALIAS = "android.security.extra.KEY_ALIAS"; field public static final String EXTRA_NAME = "name"; field public static final String EXTRA_PKCS12 = "PKCS12"; + field public static final String KEY_ALIAS_SELECTION_DENIED = "android:alias-selection-denied"; } public interface KeyChainAliasCallback { @@ -44999,7 +45000,7 @@ package android.telephony { public abstract class CellLocation { ctor public CellLocation(); method public static android.telephony.CellLocation getEmpty(); - method public static void requestLocationUpdate(); + method @Deprecated public static void requestLocationUpdate(); } public abstract class CellSignalStrength { diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java index 231809bfece4..e5fd0ba5d9bc 100644 --- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java +++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java @@ -32,12 +32,11 @@ import androidx.annotation.VisibleForTesting; import androidx.core.text.BidiFormatter; import androidx.core.text.TextDirectionHeuristicsCompat; +import com.android.i18n.timezone.CountryTimeZones; +import com.android.i18n.timezone.CountryTimeZones.TimeZoneMapping; +import com.android.i18n.timezone.TimeZoneFinder; import com.android.settingslib.R; -import libcore.timezone.CountryTimeZones; -import libcore.timezone.CountryTimeZones.TimeZoneMapping; -import libcore.timezone.TimeZoneFinder; - import org.xmlpull.v1.XmlPullParserException; import java.util.ArrayList; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index 17481bf601e1..c8aa22fc28f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -640,8 +640,7 @@ public class MobileSignalController extends SignalController< + " dataState=" + state.getDataRegistrationState()); } mServiceState = state; - // onDisplayInfoChanged is invoked directly after onServiceStateChanged, so not calling - // updateTelephony() to prevent icon flickering in case of overrides. + updateTelephony(); } @Override @@ -651,10 +650,6 @@ public class MobileSignalController extends SignalController< + " type=" + networkType); } mDataState = state; - if (networkType != mTelephonyDisplayInfo.getNetworkType()) { - mTelephonyDisplayInfo = new TelephonyDisplayInfo(networkType, - TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE); - } updateTelephony(); } diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp index 4116afca9816..f89ee24f9594 100644 --- a/packages/Tethering/Android.bp +++ b/packages/Tethering/Android.bp @@ -27,7 +27,8 @@ java_defaults { "androidx.annotation_annotation", "netd_aidl_interface-unstable-java", "netlink-client", - "networkstack-aidl-interfaces-java", + // TODO: use networkstack-client instead of just including the AIDL interface + "networkstack-aidl-interfaces-unstable-java", "android.hardware.tetheroffload.config-V1.0-java", "android.hardware.tetheroffload.control-V1.0-java", "net-utils-framework-common", diff --git a/packages/Tethering/common/TetheringLib/api/module-lib-current.txt b/packages/Tethering/common/TetheringLib/api/module-lib-current.txt index 754584e70fad..6ddb122936e7 100644 --- a/packages/Tethering/common/TetheringLib/api/module-lib-current.txt +++ b/packages/Tethering/common/TetheringLib/api/module-lib-current.txt @@ -1,24 +1,6 @@ // Signature format: 2.0 package android.net { - public final class TetheredClient implements android.os.Parcelable { - ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int); - method public int describeContents(); - method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses(); - method @NonNull public android.net.MacAddress getMacAddress(); - method public int getTetheringType(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR; - } - - public static final class TetheredClient.AddressInfo implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public android.net.LinkAddress getAddress(); - method @Nullable public String getHostname(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR; - } - public final class TetheringConstants { field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType"; field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback"; @@ -38,69 +20,15 @@ package android.net { method @NonNull public String[] getTetheringErroredIfaces(); method public boolean isTetheringSupported(); method public boolean isTetheringSupported(@NonNull String); - method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener); method public void requestLatestTetheringEntitlementResult(int, @NonNull android.os.ResultReceiver, boolean); method @Deprecated public int setUsbTethering(boolean); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering(); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int); method @Deprecated public int tether(@NonNull String); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback); method @Deprecated public int untether(@NonNull String); - field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED"; - field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY"; - field public static final String EXTRA_ACTIVE_TETHER = "tetherArray"; - field public static final String EXTRA_AVAILABLE_TETHER = "availableArray"; - field public static final String EXTRA_ERRORED_TETHER = "erroredArray"; - field public static final int TETHERING_BLUETOOTH = 2; // 0x2 - field public static final int TETHERING_ETHERNET = 5; // 0x5 - field public static final int TETHERING_INVALID = -1; // 0xffffffff - field public static final int TETHERING_NCM = 4; // 0x4 - field public static final int TETHERING_USB = 1; // 0x1 - field public static final int TETHERING_WIFI = 0; // 0x0 - field public static final int TETHERING_WIFI_P2P = 3; // 0x3 - field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc - field public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; // 0x9 - field public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; // 0x8 - field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd - field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa - field public static final int TETHER_ERROR_INTERNAL_ERROR = 5; // 0x5 - field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf - field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe - field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 - field public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; // 0xb - field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2 - field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6 - field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4 - field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1 - field public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; // 0x10 - field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3 - field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7 - field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2 - field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1 - field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0 - } - - public static interface TetheringManager.OnTetheringEntitlementResultListener { - method public void onTetheringEntitlementResult(int); - } - - public static interface TetheringManager.StartTetheringCallback { - method public default void onTetheringFailed(int); - method public default void onTetheringStarted(); } public static interface TetheringManager.TetheringEventCallback { - method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>); - method public default void onError(@NonNull String, int); - method public default void onOffloadStatusChanged(int); method public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps); - method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>); - method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>); - method public default void onTetheringSupported(boolean); - method public default void onUpstreamChanged(@Nullable android.net.Network); } public static class TetheringManager.TetheringInterfaceRegexps { @@ -109,21 +37,5 @@ package android.net { method @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs(); } - public static class TetheringManager.TetheringRequest { - method @Nullable public android.net.LinkAddress getClientStaticIpv4Address(); - method @Nullable public android.net.LinkAddress getLocalIpv4Address(); - method public boolean getShouldShowEntitlementUi(); - method public int getTetheringType(); - method public boolean isExemptFromEntitlementCheck(); - } - - public static class TetheringManager.TetheringRequest.Builder { - ctor public TetheringManager.TetheringRequest.Builder(int); - method @NonNull public android.net.TetheringManager.TetheringRequest build(); - method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); - method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean); - method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress); - } - } diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java index 48be0d96425c..645b00001330 100644 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java +++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java @@ -16,8 +16,6 @@ package android.net; -import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; - import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -36,7 +34,6 @@ import java.util.Objects; * @hide */ @SystemApi -@SystemApi(client = MODULE_LIBRARIES) @TestApi public final class TetheredClient implements Parcelable { @NonNull diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java index 87e5c1e52198..49c7fe20660d 100644 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java +++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java @@ -55,7 +55,6 @@ import java.util.function.Supplier; * @hide */ @SystemApi -@SystemApi(client = MODULE_LIBRARIES) @TestApi public class TetheringManager { private static final String TAG = TetheringManager.class.getSimpleName(); diff --git a/packages/Tethering/jarjar-rules.txt b/packages/Tethering/jarjar-rules.txt index e90a2ccaa2a3..2c4059dda9b5 100644 --- a/packages/Tethering/jarjar-rules.txt +++ b/packages/Tethering/jarjar-rules.txt @@ -12,6 +12,9 @@ rule com.android.internal.util.State* com.android.networkstack.tethering.util.St rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1 rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.tethering.util.TrafficStatsConstants@1 -rule android.net.LocalLog* com.android.networkstack.tethering.LocalLog@1 +rule android.util.LocalLog* com.android.networkstack.tethering.util.LocalLog@1 rule android.net.shared.Inet4AddressUtils* com.android.networkstack.tethering.shared.Inet4AddressUtils@1 + +# Classes from net-utils-framework-common +rule com.android.net.module.util.** com.android.networkstack.tethering.util.@1
\ No newline at end of file diff --git a/packages/Tethering/jni/android_net_util_TetheringUtils.cpp b/packages/Tethering/jni/android_net_util_TetheringUtils.cpp index 549344064405..f6eb40a842d5 100644 --- a/packages/Tethering/jni/android_net_util_TetheringUtils.cpp +++ b/packages/Tethering/jni/android_net_util_TetheringUtils.cpp @@ -18,6 +18,7 @@ #include <error.h> #include <jni.h> #include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIHelpCompat.h> #include <nativehelper/ScopedUtfChars.h> #include <net/if.h> #include <netinet/icmp6.h> diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java index 593d04a06b93..a0198cc9c126 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java @@ -273,8 +273,9 @@ public class TetheringNotificationUpdater { mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), 0 /* requestCode */, new Intent(Settings.ACTION_TETHER_SETTINGS) - .setPackage(getSettingsPackageName(mContext.getPackageManager())), - Intent.FLAG_ACTIVITY_NEW_TASK | PendingIntent.FLAG_IMMUTABLE, + .setPackage(getSettingsPackageName(mContext.getPackageManager())) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), + PendingIntent.FLAG_IMMUTABLE, null /* options */); showNotification(R.drawable.stat_sys_tether_general, title, message, @@ -317,8 +318,9 @@ public class TetheringNotificationUpdater { mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), 0 /* requestCode */, new Intent(Settings.ACTION_TETHER_SETTINGS) - .setPackage(getSettingsPackageName(mContext.getPackageManager())), - Intent.FLAG_ACTIVITY_NEW_TASK | PendingIntent.FLAG_IMMUTABLE, + .setPackage(getSettingsPackageName(mContext.getPackageManager())) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), + PendingIntent.FLAG_IMMUTABLE, null /* options */); showNotification(R.drawable.stat_sys_tether_general, title, message, diff --git a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java index 74df11370e50..9bb01ae5df1d 100644 --- a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java +++ b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java @@ -42,6 +42,7 @@ import android.net.dhcp.DhcpPacket; import android.os.Handler; import android.os.HandlerThread; import android.os.SystemClock; +import android.os.SystemProperties; import android.system.Os; import android.util.Log; @@ -101,17 +102,21 @@ public class EthernetTetheringTest { private UiAutomation mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); + private boolean mRunTests; @Before public void setUp() throws Exception { - mHandlerThread = new HandlerThread(getClass().getSimpleName()); - mHandlerThread.start(); - mHandler = new Handler(mHandlerThread.getLooper()); - mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm); // Needed to create a TestNetworkInterface, to call requestTetheredInterface, and to receive // tethered client callbacks. mUiAutomation.adoptShellPermissionIdentity( MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, TETHER_PRIVILEGED); + mRunTests = mTm.isTetheringSupported() && mEm != null; + assumeTrue(mRunTests); + + mHandlerThread = new HandlerThread(getClass().getSimpleName()); + mHandlerThread.start(); + mHandler = new Handler(mHandlerThread.getLooper()); + mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm); } private void cleanUp() throws Exception { @@ -135,7 +140,7 @@ public class EthernetTetheringTest { @After public void tearDown() throws Exception { try { - cleanUp(); + if (mRunTests) cleanUp(); } finally { mUiAutomation.dropShellPermissionIdentity(); } @@ -224,9 +229,19 @@ public class EthernetTetheringTest { } + private boolean isAdbOverNetwork() { + // If adb TCP port opened, this test may running by adb over network. + return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1) + || (SystemProperties.getInt("service.adb.tcp.port", -1) > -1); + } + @Test public void testPhysicalEthernet() throws Exception { assumeTrue(mEm.isAvailable()); + // Do not run this test if adb is over network and ethernet is connected. + // It is likely the adb run over ethernet, the adb would break when ethernet is switching + // from client mode to server mode. See b/160389275. + assumeFalse(isAdbOverNetwork()); // Get an interface to use. final String iface = mTetheredInterfaceRequester.getInterface(); diff --git a/packages/Tethering/tests/privileged/Android.bp b/packages/Tethering/tests/privileged/Android.bp new file mode 100644 index 000000000000..a0fb24603a93 --- /dev/null +++ b/packages/Tethering/tests/privileged/Android.bp @@ -0,0 +1,30 @@ +// +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +android_test { + name: "TetheringPrivilegedTests", + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], + certificate: "networkstack", + platform_apis: true, + test_suites: [ + "general-tests", + "mts", + ], + compile_multilib: "both", +} diff --git a/packages/Tethering/tests/privileged/AndroidManifest.xml b/packages/Tethering/tests/privileged/AndroidManifest.xml new file mode 100644 index 000000000000..49eba15d13d4 --- /dev/null +++ b/packages/Tethering/tests/privileged/AndroidManifest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.networkstack.tethering.tests.privileged" + android:sharedUserId="android.uid.networkstack"> + + <!-- Note: do not add any privileged or signature permissions that are granted + to the network stack and its shared uid apps. Otherwise, the test APK will + install, but when the device is rebooted, it will bootloop because this + test APK is not in the privileged permission allow list --> + + <application android:debuggable="true"> + <uses-library android:name="android.test.runner" /> + </application> + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.networkstack.tethering.tests.privileged" + android:label="Tethering privileged tests"> + </instrumentation> +</manifest> diff --git a/packages/Tethering/tests/unit/jarjar-rules.txt b/packages/Tethering/tests/unit/jarjar-rules.txt index 1ea56cdf1a3d..ec2d2b02004e 100644 --- a/packages/Tethering/tests/unit/jarjar-rules.txt +++ b/packages/Tethering/tests/unit/jarjar-rules.txt @@ -8,4 +8,4 @@ rule com.android.internal.util.State* com.android.networkstack.tethering.util.St rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1 rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.tethering.util.TrafficStatsConstants@1 -rule android.net.LocalLog* com.android.networkstack.tethering.LocalLog@1 +rule android.util.LocalLog* com.android.networkstack.tethering.util.LocalLog@1 diff --git a/packages/services/PacProcessor/AndroidManifest.xml b/packages/services/PacProcessor/AndroidManifest.xml index 6740c169ff21..ad1326194a4d 100644 --- a/packages/services/PacProcessor/AndroidManifest.xml +++ b/packages/services/PacProcessor/AndroidManifest.xml @@ -5,7 +5,9 @@ <uses-permission android:name="android.permission.INTERNET" /> <application - android:label="@string/app_name"> + android:label="@string/app_name" + android:defaultToDeviceProtectedStorage="true" + android:directBootAware="true"> <service android:name=".PacService" android:exported="true"> diff --git a/packages/services/PacProcessor/src/com/android/net/IProxyService.aidl b/packages/services/PacProcessor/src/com/android/net/IProxyService.aidl index 4e54aba5c3bf..1bbc90d604f9 100644 --- a/packages/services/PacProcessor/src/com/android/net/IProxyService.aidl +++ b/packages/services/PacProcessor/src/com/android/net/IProxyService.aidl @@ -21,7 +21,4 @@ interface IProxyService String resolvePacFile(String host, String url); oneway void setPacFile(String scriptContents); - - oneway void startPacSystem(); - oneway void stopPacSystem(); } diff --git a/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java b/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java index b006d6e1fa7b..3c25bfd380f2 100644 --- a/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java +++ b/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java @@ -83,15 +83,5 @@ public class PacService extends Service { } mPacNative.setCurrentProxyScript(script); } - - @Override - public void startPacSystem() throws RemoteException { - //TODO: remove - } - - @Override - public void stopPacSystem() throws RemoteException { - //TODO: remove - } } } diff --git a/rs/jni/Android.mk b/rs/jni/Android.mk index 0854b9582187..def67ceb596f 100644 --- a/rs/jni/Android.mk +++ b/rs/jni/Android.mk @@ -19,10 +19,10 @@ LOCAL_SHARED_LIBRARIES := \ libjnigraphics LOCAL_HEADER_LIBRARIES := \ + jni_headers \ libbase_headers LOCAL_C_INCLUDES += \ - $(JNI_H_INCLUDE) \ frameworks/rs LOCAL_CFLAGS += -Wno-unused-parameter diff --git a/services/Android.bp b/services/Android.bp index 57d9a3850e86..dc948e95076c 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -111,7 +111,6 @@ droidstubs { " --hide InternalClasses" + // com.android.* classes are okay in this interface // TODO: remove the --hide options below " --hide-package com.google.android.startop.iorap" + - " --hide ReferencesHidden" + " --hide DeprecationMismatch" + " --hide HiddenTypedefConstant", visibility: ["//visibility:private"], @@ -144,4 +143,7 @@ java_library { srcs: [":services-stubs.sources"], installable: false, static_libs: ["android_module_lib_stubs_current"], + sdk_version: "none", + system_modules: "none", + java_version: "1.8", } diff --git a/services/backup/OWNERS b/services/backup/OWNERS index 9c21e8fe5e45..7c7e74285bf5 100644 --- a/services/backup/OWNERS +++ b/services/backup/OWNERS @@ -1,9 +1,12 @@ +# Bug component: 656484 + +aabhinav@google.com alsutton@google.com -anniemeng@google.com -brufino@google.com bryanmawhinney@google.com -ctate@google.com -jorlow@google.com +jstemmer@google.com nathch@google.com +niagra@google.com +niamhfw@google.com +philippov@google.com rthakohov@google.com - +tobiast@google.com diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 52cc8f633dc1..e1bcb0c21ce5 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -4315,9 +4315,11 @@ public class ConnectivityService extends IConnectivityManager.Stub enforceInternetPermission(); final int uid = Binder.getCallingUid(); final int connectivityInfo = encodeBool(hasConnectivity); - mHandler.sendMessage( - mHandler.obtainMessage(EVENT_REVALIDATE_NETWORK, uid, connectivityInfo, network)); + // Handle ConnectivityDiagnostics event before attempting to revalidate the network. This + // forces an ordering of ConnectivityDiagnostics events in the case where hasConnectivity + // does not match the known connectivity of the network - this causes NetworkMonitor to + // revalidate the network and generate a ConnectivityDiagnostics ConnectivityReport event. final NetworkAgentInfo nai; if (network == null) { nai = getDefaultNetwork(); @@ -4330,6 +4332,9 @@ public class ConnectivityService extends IConnectivityManager.Stub ConnectivityDiagnosticsHandler.EVENT_NETWORK_CONNECTIVITY_REPORTED, connectivityInfo, 0, nai)); } + + mHandler.sendMessage( + mHandler.obtainMessage(EVENT_REVALIDATE_NETWORK, uid, connectivityInfo, network)); } private void handleReportNetworkConnectivity( @@ -5136,14 +5141,6 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private void onPackageAdded(String packageName, int uid) { - if (TextUtils.isEmpty(packageName) || uid < 0) { - Slog.wtf(TAG, "Invalid package in onPackageAdded: " + packageName + " | " + uid); - return; - } - mPermissionMonitor.onPackageAdded(packageName, uid); - } - private void onPackageReplaced(String packageName, int uid) { if (TextUtils.isEmpty(packageName) || uid < 0) { Slog.wtf(TAG, "Invalid package in onPackageReplaced: " + packageName + " | " + uid); @@ -5169,7 +5166,6 @@ public class ConnectivityService extends IConnectivityManager.Stub Slog.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid); return; } - mPermissionMonitor.onPackageRemoved(uid); final int userId = UserHandle.getUserId(uid); synchronized (mVpns) { @@ -5219,8 +5215,6 @@ public class ConnectivityService extends IConnectivityManager.Stub onUserRemoved(userId); } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) { onUserUnlocked(userId); - } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { - onPackageAdded(packageName, uid); } else if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) { onPackageReplaced(packageName, uid); } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { diff --git a/services/core/java/com/android/server/RuntimeService.java b/services/core/java/com/android/server/RuntimeService.java index bb39ccc52af2..f4249f844873 100644 --- a/services/core/java/com/android/server/RuntimeService.java +++ b/services/core/java/com/android/server/RuntimeService.java @@ -23,10 +23,9 @@ import android.service.runtime.RuntimeServiceInfoProto; import android.util.Slog; import android.util.proto.ProtoOutputStream; -import libcore.timezone.TimeZoneDataFiles; -import libcore.util.CoreLibraryDebug; -import libcore.util.DebugInfo; - +import com.android.i18n.timezone.DebugInfo; +import com.android.i18n.timezone.I18nModuleDebug; +import com.android.i18n.timezone.TimeZoneDataFiles; import com.android.internal.util.DumpUtils; import com.android.timezone.distro.DistroException; import com.android.timezone.distro.DistroVersion; @@ -61,14 +60,14 @@ public class RuntimeService extends Binder { boolean protoFormat = hasOption(args, "--proto"); ProtoOutputStream proto = null; - DebugInfo coreLibraryDebugInfo = CoreLibraryDebug.getDebugInfo(); - addTimeZoneApkDebugInfo(coreLibraryDebugInfo); + DebugInfo i18nLibraryDebugInfo = I18nModuleDebug.getDebugInfo(); + addTimeZoneApkDebugInfo(i18nLibraryDebugInfo); if (protoFormat) { proto = new ProtoOutputStream(fd); - reportTimeZoneInfoProto(coreLibraryDebugInfo, proto); + reportTimeZoneInfoProto(i18nLibraryDebugInfo, proto); } else { - reportTimeZoneInfo(coreLibraryDebugInfo, pw); + reportTimeZoneInfo(i18nLibraryDebugInfo, pw); } if (protoFormat) { diff --git a/services/core/java/com/android/server/compat/TEST_MAPPING b/services/core/java/com/android/server/compat/TEST_MAPPING index 0c30c790c5dd..dc1e2cb0c803 100644 --- a/services/core/java/com/android/server/compat/TEST_MAPPING +++ b/services/core/java/com/android/server/compat/TEST_MAPPING @@ -15,7 +15,7 @@ }, // CTS tests { - "name": "CtsAppCompatHostTestCases#" + "name": "CtsAppCompatHostTestCases" } ] -}
\ No newline at end of file +} diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java index f6ce2dc68b99..de302fc01f2d 100644 --- a/services/core/java/com/android/server/connectivity/PacManager.java +++ b/services/core/java/com/android/server/connectivity/PacManager.java @@ -196,13 +196,7 @@ public class PacManager { mPacUrl = Uri.EMPTY; mCurrentPac = null; if (mProxyService != null) { - try { - mProxyService.stopPacSystem(); - } catch (RemoteException e) { - Log.w(TAG, "Failed to stop PAC service", e); - } finally { - unbind(); - } + unbind(); } } return DO_SEND_BROADCAST; @@ -327,11 +321,6 @@ public class PacManager { if (mProxyService == null) { Log.e(TAG, "No proxy service"); } else { - try { - mProxyService.startPacSystem(); - } catch (RemoteException e) { - Log.e(TAG, "Unable to reach ProxyService - PAC will not be started", e); - } mNetThreadHandler.post(mPacDownloader); } } diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java index f0b7150dd84f..a75a80a606eb 100644 --- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java +++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java @@ -72,7 +72,7 @@ import java.util.Set; * * @hide */ -public class PermissionMonitor { +public class PermissionMonitor implements PackageManagerInternal.PackageListObserver { private static final String TAG = "PermissionMonitor"; private static final boolean DBG = true; protected static final Boolean SYSTEM = Boolean.TRUE; @@ -82,6 +82,7 @@ public class PermissionMonitor { private final PackageManager mPackageManager; private final UserManager mUserManager; private final INetd mNetd; + private final Dependencies mDeps; // Values are User IDs. @GuardedBy("this") @@ -102,48 +103,30 @@ public class PermissionMonitor { @GuardedBy("this") private final Set<Integer> mAllApps = new HashSet<>(); - private class PackageListObserver implements PackageManagerInternal.PackageListObserver { - - private int getPermissionForUid(int uid) { - int permission = 0; - // Check all the packages for this UID. The UID has the permission if any of the - // packages in it has the permission. - String[] packages = mPackageManager.getPackagesForUid(uid); - if (packages != null && packages.length > 0) { - for (String name : packages) { - final PackageInfo app = getPackageInfo(name); - if (app != null && app.requestedPermissions != null) { - permission |= getNetdPermissionMask(app.requestedPermissions, - app.requestedPermissionsFlags); - } - } - } else { - // The last package of this uid is removed from device. Clean the package up. - permission = INetd.PERMISSION_UNINSTALLED; - } - return permission; - } - - @Override - public void onPackageAdded(String packageName, int uid) { - sendPackagePermissionsForUid(uid, getPermissionForUid(uid)); - } - - @Override - public void onPackageChanged(@NonNull String packageName, int uid) { - sendPackagePermissionsForUid(uid, getPermissionForUid(uid)); + /** + * Dependencies of PermissionMonitor, for injection in tests. + */ + @VisibleForTesting + public static class Dependencies { + /** + * Get device first sdk version. + */ + public int getDeviceFirstSdkInt() { + return Build.VERSION.FIRST_SDK_INT; } + } - @Override - public void onPackageRemoved(String packageName, int uid) { - sendPackagePermissionsForUid(uid, getPermissionForUid(uid)); - } + public PermissionMonitor(@NonNull final Context context, @NonNull final INetd netd) { + this(context, netd, new Dependencies()); } - public PermissionMonitor(Context context, INetd netd) { + @VisibleForTesting + PermissionMonitor(@NonNull final Context context, @NonNull final INetd netd, + @NonNull final Dependencies deps) { mPackageManager = context.getPackageManager(); mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); mNetd = netd; + mDeps = deps; } // Intended to be called only once at startup, after the system is ready. Installs a broadcast @@ -153,7 +136,7 @@ public class PermissionMonitor { PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); if (pmi != null) { - pmi.getPackageList(new PackageListObserver()); + pmi.getPackageList(this); } else { loge("failed to get the PackageManagerInternal service"); } @@ -224,11 +207,6 @@ public class PermissionMonitor { } @VisibleForTesting - protected int getDeviceFirstSdkInt() { - return Build.VERSION.FIRST_SDK_INT; - } - - @VisibleForTesting boolean hasPermission(@NonNull final PackageInfo app, @NonNull final String permission) { if (app.requestedPermissions == null || app.requestedPermissionsFlags == null) { return false; @@ -250,7 +228,7 @@ public class PermissionMonitor { if (app.applicationInfo != null) { // Backward compatibility for b/114245686, on devices that launched before Q daemons // and apps running as the system UID are exempted from this check. - if (app.applicationInfo.uid == SYSTEM_UID && getDeviceFirstSdkInt() < VERSION_Q) { + if (app.applicationInfo.uid == SYSTEM_UID && mDeps.getDeviceFirstSdkInt() < VERSION_Q) { return true; } @@ -363,15 +341,38 @@ public class PermissionMonitor { return currentPermission; } + private int getPermissionForUid(final int uid) { + int permission = INetd.PERMISSION_NONE; + // Check all the packages for this UID. The UID has the permission if any of the + // packages in it has the permission. + final String[] packages = mPackageManager.getPackagesForUid(uid); + if (packages != null && packages.length > 0) { + for (String name : packages) { + final PackageInfo app = getPackageInfo(name); + if (app != null && app.requestedPermissions != null) { + permission |= getNetdPermissionMask(app.requestedPermissions, + app.requestedPermissionsFlags); + } + } + } else { + // The last package of this uid is removed from device. Clean the package up. + permission = INetd.PERMISSION_UNINSTALLED; + } + return permission; + } + /** - * Called when a package is added. See {link #ACTION_PACKAGE_ADDED}. + * Called when a package is added. * * @param packageName The name of the new package. * @param uid The uid of the new package. * * @hide */ - public synchronized void onPackageAdded(String packageName, int uid) { + @Override + public synchronized void onPackageAdded(@NonNull final String packageName, final int uid) { + sendPackagePermissionsForUid(uid, getPermissionForUid(uid)); + // If multiple packages share a UID (cf: android:sharedUserId) and ask for different // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is). final Boolean permission = highestPermissionForUid(mApps.get(uid), packageName); @@ -398,13 +399,17 @@ public class PermissionMonitor { } /** - * Called when a package is removed. See {link #ACTION_PACKAGE_REMOVED}. + * Called when a package is removed. * + * @param packageName The name of the removed package or null. * @param uid containing the integer uid previously assigned to the package. * * @hide */ - public synchronized void onPackageRemoved(int uid) { + @Override + public synchronized void onPackageRemoved(@NonNull final String packageName, final int uid) { + sendPackagePermissionsForUid(uid, getPermissionForUid(uid)); + // If the newly-removed package falls within some VPN's uid range, update Netd with it. // This needs to happen before the mApps update below, since removeBypassingUids() depends // on mApps to check if the package can bypass VPN. @@ -449,6 +454,19 @@ public class PermissionMonitor { } } + /** + * Called when a package is changed. + * + * @param packageName The name of the changed package. + * @param uid The uid of the changed package. + * + * @hide + */ + @Override + public synchronized void onPackageChanged(@NonNull final String packageName, final int uid) { + sendPackagePermissionsForUid(uid, getPermissionForUid(uid)); + } + private static int getNetdPermissionMask(String[] requestedPermissions, int[] requestedPermissionsFlags) { int permissions = 0; diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java index e715890fb211..26cc3ee165f1 100644 --- a/services/core/java/com/android/server/connectivity/ProxyTracker.java +++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java @@ -73,6 +73,8 @@ public class ProxyTracker { @GuardedBy("mProxyLock") private boolean mDefaultProxyEnabled = true; + private final Handler mConnectivityServiceHandler; + // The object responsible for Proxy Auto Configuration (PAC). @NonNull private final PacManager mPacManager; @@ -80,6 +82,7 @@ public class ProxyTracker { public ProxyTracker(@NonNull final Context context, @NonNull final Handler connectivityServiceInternalHandler, final int pacChangedEvent) { mContext = context; + mConnectivityServiceHandler = connectivityServiceInternalHandler; mPacManager = new PacManager(context, connectivityServiceInternalHandler, pacChangedEvent); } @@ -149,6 +152,9 @@ public class ProxyTracker { * Read the global proxy settings and cache them in memory. */ public void loadGlobalProxy() { + if (loadDeprecatedGlobalHttpProxy()) { + return; + } ContentResolver res = mContext.getContentResolver(); String host = Settings.Global.getString(res, GLOBAL_HTTP_PROXY_HOST); int port = Settings.Global.getInt(res, GLOBAL_HTTP_PROXY_PORT, 0); @@ -157,7 +163,7 @@ public class ProxyTracker { if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) { ProxyInfo proxyProperties; if (!TextUtils.isEmpty(pacFileUrl)) { - proxyProperties = new ProxyInfo(pacFileUrl); + proxyProperties = new ProxyInfo(Uri.parse(pacFileUrl)); } else { proxyProperties = new ProxyInfo(host, port, exclList); } @@ -169,20 +175,24 @@ public class ProxyTracker { synchronized (mProxyLock) { mGlobalProxy = proxyProperties; } + + if (!TextUtils.isEmpty(pacFileUrl)) { + mConnectivityServiceHandler.post( + () -> mPacManager.setCurrentProxyScriptUrl(proxyProperties)); + } } - loadDeprecatedGlobalHttpProxy(); - // TODO : shouldn't this function call mPacManager.setCurrentProxyScriptUrl ? } /** * Read the global proxy from the deprecated Settings.Global.HTTP_PROXY setting and apply it. + * Returns {@code true} when global proxy was set successfully from deprecated setting. */ - public void loadDeprecatedGlobalHttpProxy() { + public boolean loadDeprecatedGlobalHttpProxy() { final String proxy = Settings.Global.getString(mContext.getContentResolver(), HTTP_PROXY); if (!TextUtils.isEmpty(proxy)) { String data[] = proxy.split(":"); if (data.length == 0) { - return; + return false; } final String proxyHost = data[0]; @@ -191,12 +201,14 @@ public class ProxyTracker { try { proxyPort = Integer.parseInt(data[1]); } catch (NumberFormatException e) { - return; + return false; } } final ProxyInfo p = new ProxyInfo(proxyHost, proxyPort, ""); setGlobalProxy(p); + return true; } + return false; } /** diff --git a/services/core/java/com/android/server/net/IpConfigStore.java b/services/core/java/com/android/server/net/IpConfigStore.java index e3e02e32ad50..f0bf5c0975f2 100644 --- a/services/core/java/com/android/server/net/IpConfigStore.java +++ b/services/core/java/com/android/server/net/IpConfigStore.java @@ -24,6 +24,7 @@ import android.net.NetworkUtils; import android.net.ProxyInfo; import android.net.RouteInfo; import android.net.StaticIpConfiguration; +import android.net.Uri; import android.util.ArrayMap; import android.util.Log; import android.util.SparseArray; @@ -372,7 +373,7 @@ public class IpConfigStore { config.httpProxy = proxyInfo; break; case PAC: - ProxyInfo proxyPacProperties = new ProxyInfo(pacFileUrl); + ProxyInfo proxyPacProperties = new ProxyInfo(Uri.parse(pacFileUrl)); config.proxySettings = proxySettings; config.httpProxy = proxyPacProperties; break; diff --git a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java index 7711c6a21d20..5f2c4a38b42a 100644 --- a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java +++ b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java @@ -16,12 +16,14 @@ package com.android.server.net; +import static android.net.NetworkTemplate.NETWORK_TYPE_5G_NSA; import static android.net.NetworkTemplate.getCollapsedRatType; import android.annotation.NonNull; import android.content.Context; import android.os.Looper; import android.telephony.Annotation; +import android.telephony.NetworkRegistrationInfo; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SubscriptionManager; @@ -196,7 +198,18 @@ public class NetworkStatsSubscriptionsMonitor extends @Override public void onServiceStateChanged(@NonNull ServiceState ss) { - final int networkType = ss.getDataNetworkType(); + // In 5G SA (Stand Alone) mode, the primary cell itself will be 5G hence telephony + // would report RAT = 5G_NR. + // However, in 5G NSA (Non Stand Alone) mode, the primary cell is still LTE and + // network allocates a secondary 5G cell so telephony reports RAT = LTE along with + // NR state as connected. In such case, attributes the data usage to NR. + // See b/160727498. + final boolean is5GNsa = (ss.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE + || ss.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE_CA) + && ss.getNrState() == NetworkRegistrationInfo.NR_STATE_CONNECTED; + + final int networkType = + (is5GNsa ? NETWORK_TYPE_5G_NSA : ss.getDataNetworkType()); final int collapsedRatType = getCollapsedRatType(networkType); if (collapsedRatType == mLastCollapsedRatType) return; diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS index 6fdde7a196cc..fe6aad70c31e 100644 --- a/services/core/java/com/android/server/pm/OWNERS +++ b/services/core/java/com/android/server/pm/OWNERS @@ -10,8 +10,8 @@ toddke@android.com toddke@google.com # apex support -per-file ApexManager.java = dariofreni@google.com -per-file StagingManager.java = dariofreni@google.com +per-file ApexManager.java = dariofreni@google.com, ioffe@google.com, olilan@google.com +per-file StagingManager.java = dariofreni@google.com, ioffe@google.com, olilan@google.com # dex per-file AbstractStatsBase.java = agampe@google.com, calin@google.com, ngeoffray@google.com diff --git a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java index 2700f9ddc203..494dbad34a1d 100644 --- a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java +++ b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java @@ -49,11 +49,8 @@ import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.server.EventLogTags; import com.android.server.SystemService; -import com.android.server.pm.InstructionSets; import com.android.server.pm.PackageManagerService; -import dalvik.system.VMRuntime; - import java.io.File; import java.io.FileDescriptor; import java.io.IOException; @@ -214,7 +211,7 @@ public class DeviceStorageMonitorService extends SystemService { newLevel = State.LEVEL_FULL; } else if (usableBytes <= lowBytes) { newLevel = State.LEVEL_LOW; - } else if (StorageManager.UUID_DEFAULT.equals(uuid) && !isBootImageOnDisk() + } else if (StorageManager.UUID_DEFAULT.equals(uuid) && usableBytes < BOOT_IMAGE_STORAGE_REQUIREMENT) { newLevel = State.LEVEL_LOW; } else { @@ -261,15 +258,6 @@ public class DeviceStorageMonitorService extends SystemService { }; } - private static boolean isBootImageOnDisk() { - for (String instructionSet : InstructionSets.getAllDexCodeInstructionSets()) { - if (!VMRuntime.isBootClassPathOnDisk(instructionSet)) { - return false; - } - } - return true; - } - @Override public void onStart() { final Context context = getContext(); @@ -467,15 +455,8 @@ public class DeviceStorageMonitorService extends SystemService { final CharSequence title = context.getText( com.android.internal.R.string.low_internal_storage_view_title); - final CharSequence details; - if (StorageManager.UUID_DEFAULT.equals(uuid)) { - details = context.getText(isBootImageOnDisk() - ? com.android.internal.R.string.low_internal_storage_view_text - : com.android.internal.R.string.low_internal_storage_view_text_no_boot); - } else { - details = context.getText( - com.android.internal.R.string.low_internal_storage_view_text); - } + final CharSequence details = context.getText( + com.android.internal.R.string.low_internal_storage_view_text); PendingIntent intent = PendingIntent.getActivityAsUser(context, 0, lowMemIntent, 0, null, UserHandle.CURRENT); diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java index bbd1ae60cb62..fd5c6e91a885 100644 --- a/services/core/java/com/android/server/timezone/RulesManagerService.java +++ b/services/core/java/com/android/server/timezone/RulesManagerService.java @@ -37,6 +37,10 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.Slog; +import com.android.i18n.timezone.TimeZoneDataFiles; +import com.android.i18n.timezone.TimeZoneFinder; +import com.android.i18n.timezone.TzDataSetVersion; +import com.android.i18n.timezone.ZoneInfoDb; import com.android.internal.annotations.VisibleForTesting; import com.android.server.EventLogTags; import com.android.server.SystemService; @@ -46,10 +50,6 @@ import com.android.timezone.distro.StagedDistroOperation; import com.android.timezone.distro.TimeZoneDistro; import com.android.timezone.distro.installer.TimeZoneDistroInstaller; -import libcore.timezone.TimeZoneDataFiles; -import libcore.timezone.TimeZoneFinder; -import libcore.timezone.TzDataSetVersion; -import libcore.timezone.ZoneInfoDb; import java.io.File; import java.io.FileDescriptor; diff --git a/services/core/jni/com_android_server_AlarmManagerService.cpp b/services/core/jni/com_android_server_AlarmManagerService.cpp index a99c0a3fa23e..335040a4b92f 100644 --- a/services/core/jni/com_android_server_AlarmManagerService.cpp +++ b/services/core/jni/com_android_server_AlarmManagerService.cpp @@ -1,5 +1,4 @@ -/* //device/libs/android_runtime/android_server_AlarmManagerService.cpp -** +/* ** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,6 +18,8 @@ #include <nativehelper/JNIHelp.h> #include "jni.h" +#include <android-base/file.h> +#include <android-base/unique_fd.h> #include <utils/Log.h> #include <utils/misc.h> #include <utils/String8.h> @@ -75,8 +76,8 @@ typedef std::array<int, N_ANDROID_TIMERFDS> TimerFds; class AlarmImpl { public: - AlarmImpl(const TimerFds &fds, int epollfd, int rtc_id) : - fds{fds}, epollfd{epollfd}, rtc_id{rtc_id} { } + AlarmImpl(const TimerFds &fds, int epollfd, const std::string& rtc_dev) : + fds{fds}, epollfd{epollfd}, rtc_dev{rtc_dev} { } ~AlarmImpl(); int set(int type, struct timespec *ts); @@ -87,7 +88,7 @@ public: private: const TimerFds fds; const int epollfd; - const int rtc_id; + std::string rtc_dev; }; AlarmImpl::~AlarmImpl() @@ -132,38 +133,24 @@ int AlarmImpl::getTime(int type, struct itimerspec *spec) int AlarmImpl::setTime(struct timeval *tv) { - struct rtc_time rtc; - struct tm tm, *gmtime_res; - int fd; - int res; - - res = settimeofday(tv, NULL); - if (res < 0) { - ALOGV("settimeofday() failed: %s\n", strerror(errno)); + if (settimeofday(tv, NULL) == -1) { + ALOGV("settimeofday() failed: %s", strerror(errno)); return -1; } - if (rtc_id < 0) { - ALOGV("Not setting RTC because wall clock RTC was not found"); - errno = ENODEV; + android::base::unique_fd fd{open(rtc_dev.c_str(), O_RDWR)}; + if (!fd.ok()) { + ALOGE("Unable to open %s: %s", rtc_dev.c_str(), strerror(errno)); return -1; } - android::String8 rtc_dev = String8::format("/dev/rtc%d", rtc_id); - fd = open(rtc_dev.string(), O_RDWR); - if (fd < 0) { - ALOGV("Unable to open %s: %s\n", rtc_dev.string(), strerror(errno)); - return res; - } - - gmtime_res = gmtime_r(&tv->tv_sec, &tm); - if (!gmtime_res) { - ALOGV("gmtime_r() failed: %s\n", strerror(errno)); - res = -1; - goto done; + struct tm tm; + if (!gmtime_r(&tv->tv_sec, &tm)) { + ALOGV("gmtime_r() failed: %s", strerror(errno)); + return -1; } - memset(&rtc, 0, sizeof(rtc)); + struct rtc_time rtc = {}; rtc.tm_sec = tm.tm_sec; rtc.tm_min = tm.tm_min; rtc.tm_hour = tm.tm_hour; @@ -173,12 +160,12 @@ int AlarmImpl::setTime(struct timeval *tv) rtc.tm_wday = tm.tm_wday; rtc.tm_yday = tm.tm_yday; rtc.tm_isdst = tm.tm_isdst; - res = ioctl(fd, RTC_SET_TIME, &rtc); - if (res < 0) - ALOGV("RTC_SET_TIME ioctl failed: %s\n", strerror(errno)); -done: - close(fd); - return res; + if (ioctl(fd, RTC_SET_TIME, &rtc) == -1) { + ALOGV("RTC_SET_TIME ioctl failed: %s", strerror(errno)); + return -1; + } + + return 0; } int AlarmImpl::waitForAlarm() @@ -251,65 +238,6 @@ static jint android_server_AlarmManagerService_setKernelTimezone(JNIEnv*, jobjec return 0; } -static const char rtc_sysfs[] = "/sys/class/rtc"; - -static bool rtc_is_hctosys(unsigned int rtc_id) -{ - android::String8 hctosys_path = String8::format("%s/rtc%u/hctosys", - rtc_sysfs, rtc_id); - FILE *file = fopen(hctosys_path.string(), "re"); - if (!file) { - ALOGE("failed to open %s: %s", hctosys_path.string(), strerror(errno)); - return false; - } - - unsigned int hctosys; - bool ret = false; - int err = fscanf(file, "%u", &hctosys); - if (err == EOF) - ALOGE("failed to read from %s: %s", hctosys_path.string(), - strerror(errno)); - else if (err == 0) - ALOGE("%s did not have expected contents", hctosys_path.string()); - else - ret = hctosys; - - fclose(file); - return ret; -} - -static int wall_clock_rtc() -{ - std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(rtc_sysfs), closedir); - if (!dir.get()) { - ALOGE("failed to open %s: %s", rtc_sysfs, strerror(errno)); - return -1; - } - - struct dirent *dirent; - while (errno = 0, dirent = readdir(dir.get())) { - unsigned int rtc_id; - int matched = sscanf(dirent->d_name, "rtc%u", &rtc_id); - - if (matched < 0) - break; - else if (matched != 1) - continue; - - if (rtc_is_hctosys(rtc_id)) { - ALOGV("found wall clock RTC %u", rtc_id); - return rtc_id; - } - } - - if (errno == 0) - ALOGW("no wall clock RTC found"); - else - ALOGE("failed to enumerate RTCs: %s", strerror(errno)); - - return -1; -} - static void log_timerfd_create_error(clockid_t id) { if (errno == EINVAL) { @@ -343,8 +271,7 @@ static jlong android_server_AlarmManagerService_init(JNIEnv*, jobject) epollfd = epoll_create(fds.size()); if (epollfd < 0) { - ALOGE("epoll_create(%zu) failed: %s", fds.size(), - strerror(errno)); + ALOGE("epoll_create(%zu) failed: %s", fds.size(), strerror(errno)); return 0; } @@ -360,7 +287,19 @@ static jlong android_server_AlarmManagerService_init(JNIEnv*, jobject) } } - AlarmImpl *ret = new AlarmImpl(fds, epollfd, wall_clock_rtc()); + // Find the wall clock RTC. We expect this always to be /dev/rtc0, but + // check the /dev/rtc symlink first so that legacy devices that don't use + // rtc0 can add a symlink rather than need to carry a local patch to this + // code. + // + // TODO: if you're reading this in a world where all devices are using the + // GKI, you can remove the readlink and just assume /dev/rtc0. + std::string dev_rtc; + if (!android::base::Readlink("/dev/rtc", &dev_rtc)) { + dev_rtc = "/dev/rtc0"; + } + + std::unique_ptr<AlarmImpl> alarm{new AlarmImpl(fds, epollfd, dev_rtc)}; for (size_t i = 0; i < fds.size(); i++) { epoll_event event; @@ -370,13 +309,11 @@ static jlong android_server_AlarmManagerService_init(JNIEnv*, jobject) int err = epoll_ctl(epollfd, EPOLL_CTL_ADD, fds[i], &event); if (err < 0) { ALOGE("epoll_ctl(EPOLL_CTL_ADD) failed: %s", strerror(errno)); - delete ret; return 0; } } - struct itimerspec spec; - memset(&spec, 0, sizeof(spec)); + struct itimerspec spec = {}; /* 0 = disarmed; the timerfd doesn't need to be armed to get RTC change notifications, just set up as cancelable */ @@ -384,11 +321,10 @@ static jlong android_server_AlarmManagerService_init(JNIEnv*, jobject) TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &spec, NULL); if (err < 0) { ALOGE("timerfd_settime() failed: %s", strerror(errno)); - delete ret; return 0; } - return reinterpret_cast<jlong>(ret); + return reinterpret_cast<jlong>(alarm.release()); } static jlong android_server_AlarmManagerService_getNextAlarm(JNIEnv*, jobject, jlong nativeData, jint type) diff --git a/services/core/jni/com_android_server_SerialService.cpp b/services/core/jni/com_android_server_SerialService.cpp index c9459994a658..6600c981b68d 100644 --- a/services/core/jni/com_android_server_SerialService.cpp +++ b/services/core/jni/com_android_server_SerialService.cpp @@ -18,7 +18,7 @@ #include "utils/Log.h" #include "jni.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include "android_runtime/AndroidRuntime.h" #include <sys/types.h> diff --git a/services/core/jni/com_android_server_UsbDeviceManager.cpp b/services/core/jni/com_android_server_UsbDeviceManager.cpp index 72dce4dca0da..3ab5920d8b59 100644 --- a/services/core/jni/com_android_server_UsbDeviceManager.cpp +++ b/services/core/jni/com_android_server_UsbDeviceManager.cpp @@ -18,7 +18,7 @@ #include "utils/Log.h" #include "jni.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <nativehelper/ScopedUtfChars.h> #include "android_runtime/AndroidRuntime.h" #include "android_runtime/Log.h" diff --git a/services/core/jni/com_android_server_UsbHostManager.cpp b/services/core/jni/com_android_server_UsbHostManager.cpp index a40bcd0d7c07..a629b69c1c27 100644 --- a/services/core/jni/com_android_server_UsbHostManager.cpp +++ b/services/core/jni/com_android_server_UsbHostManager.cpp @@ -18,7 +18,7 @@ #include "utils/Log.h" #include "jni.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include "android_runtime/AndroidRuntime.h" #include "android_runtime/Log.h" diff --git a/services/core/jni/com_android_server_UsbMidiDevice.cpp b/services/core/jni/com_android_server_UsbMidiDevice.cpp index 8ac2c4f37ecd..d6b5bed173eb 100644 --- a/services/core/jni/com_android_server_UsbMidiDevice.cpp +++ b/services/core/jni/com_android_server_UsbMidiDevice.cpp @@ -19,7 +19,7 @@ #include "utils/Log.h" #include "jni.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <nativehelper/ScopedLocalRef.h> #include "android_runtime/AndroidRuntime.h" #include "android_runtime/Log.h" diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 7aaa0745e5c6..1f445c9ca3bd 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -233,7 +233,7 @@ public: /* --- InputReaderPolicyInterface implementation --- */ virtual void getReaderConfiguration(InputReaderConfiguration* outConfig); - virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId); + virtual std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId); virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices); virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier& identifier); virtual std::string getDeviceAlias(const InputDeviceIdentifier& identifier); @@ -306,7 +306,7 @@ private: sp<SpriteController> spriteController; // Pointer controller singleton, created and destroyed as needed. - wp<PointerController> pointerController; + std::weak_ptr<PointerController> pointerController; // Input devices to be disabled SortedVector<int32_t> disabledInputDevices; @@ -551,15 +551,16 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon } // release lock } -sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32_t /* deviceId */) { +std::shared_ptr<PointerControllerInterface> NativeInputManager::obtainPointerController( + int32_t /* deviceId */) { ATRACE_CALL(); AutoMutex _l(mLock); - sp<PointerController> controller = mLocked.pointerController.promote(); + std::shared_ptr<PointerController> controller = mLocked.pointerController.lock(); if (controller == nullptr) { ensureSpriteControllerLocked(); - controller = new PointerController(this, mLooper, mLocked.spriteController); + controller = PointerController::create(this, mLooper, mLocked.spriteController); mLocked.pointerController = controller; updateInactivityTimeoutLocked(); } @@ -840,15 +841,14 @@ void NativeInputManager::setSystemUiVisibility(int32_t visibility) { } void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) { - sp<PointerController> controller = mLocked.pointerController.promote(); + std::shared_ptr<PointerController> controller = mLocked.pointerController.lock(); if (controller == nullptr) { return; } bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN; - controller->setInactivityTimeout(lightsOut - ? PointerController::INACTIVITY_TIMEOUT_SHORT - : PointerController::INACTIVITY_TIMEOUT_NORMAL); + controller->setInactivityTimeout(lightsOut ? PointerController::InactivityTimeout::SHORT + : PointerController::InactivityTimeout::NORMAL); } void NativeInputManager::setPointerSpeed(int32_t speed) { @@ -928,7 +928,7 @@ void NativeInputManager::reloadCalibration() { void NativeInputManager::setPointerIconType(int32_t iconId) { AutoMutex _l(mLock); - sp<PointerController> controller = mLocked.pointerController.promote(); + std::shared_ptr<PointerController> controller = mLocked.pointerController.lock(); if (controller != nullptr) { controller->updatePointerIcon(iconId); } @@ -936,7 +936,7 @@ void NativeInputManager::setPointerIconType(int32_t iconId) { void NativeInputManager::reloadPointerIcons() { AutoMutex _l(mLock); - sp<PointerController> controller = mLocked.pointerController.promote(); + std::shared_ptr<PointerController> controller = mLocked.pointerController.lock(); if (controller != nullptr) { controller->reloadPointerResources(); } @@ -944,7 +944,7 @@ void NativeInputManager::reloadPointerIcons() { void NativeInputManager::setCustomPointerIcon(const SpriteIcon& icon) { AutoMutex _l(mLock); - sp<PointerController> controller = mLocked.pointerController.promote(); + std::shared_ptr<PointerController> controller = mLocked.pointerController.lock(); if (controller != nullptr) { controller->setCustomPointerIcon(icon); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 03e71f976c5b..a363f9ba5264 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -5672,7 +5672,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { KeyChain.bindAsUser(mContext, UserHandle.getUserHandleForUid(callingUid)); try { IKeyChainService keyChain = keyChainConnection.getService(); - if (!keyChain.installKeyPair(privKey, cert, chain, alias)) { + if (!keyChain.installKeyPair(privKey, cert, chain, alias, KeyStore.UID_SELF)) { return false; } if (requestAccess) { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 23b1512081da..1131d37b381e 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -74,6 +74,7 @@ import android.view.WindowManager; import android.view.contentcapture.ContentCaptureManager; import android.view.inputmethod.InputMethodSystemProperty; +import com.android.i18n.timezone.ZoneInfoDb; import com.android.internal.R; import com.android.internal.logging.MetricsLogger; import com.android.internal.notification.SystemNotificationChannels; @@ -165,8 +166,6 @@ import com.android.server.wm.ActivityTaskManagerService; import com.android.server.wm.WindowManagerGlobalLock; import com.android.server.wm.WindowManagerService; -import libcore.timezone.ZoneInfoDb; - import dalvik.system.VMRuntime; import java.io.File; @@ -556,12 +555,6 @@ public final class SystemServer { } } - // Diagnostic to ensure that the system is in a base healthy state. Done here as a common - // non-zygote process. - if (!VMRuntime.hasBootImageSpaces()) { - Slog.wtf(TAG, "Runtime is not running with a boot image!"); - } - // Loop forever. Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); diff --git a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java index 5c6fe0fc4cad..2c4c4d0ee91f 100644 --- a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java @@ -43,13 +43,13 @@ import android.app.timezone.RulesManager; import android.app.timezone.RulesState; import android.os.ParcelFileDescriptor; +import com.android.i18n.timezone.TzDataSetVersion; import com.android.timezone.distro.DistroVersion; import com.android.timezone.distro.StagedDistroOperation; import com.android.timezone.distro.TimeZoneDistro; import com.android.timezone.distro.installer.TimeZoneDistroInstaller; import libcore.io.IoUtils; -import libcore.timezone.TzDataSetVersion; import org.junit.Before; import org.junit.Test; diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index 9a47ae15e64a..0965249cdad3 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -273,6 +273,11 @@ interface ITelecomService { boolean setDefaultDialer(in String packageName); /** + * Stop suppressing blocked numbers after a call to emergency services. Shell only. + */ + void stopBlockSuppression(); + + /** * @see TelecomServiceImpl#createManageBlockedNumbersIntent **/ Intent createManageBlockedNumbersIntent(); diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java index 3c1e707ab1dd..4af19b5489ca 100644 --- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java +++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java @@ -127,6 +127,14 @@ public final class CarrierAppUtils { return userContext.getContentResolver(); } + private static boolean isUpdatedSystemApp(ApplicationInfo ai) { + if ((ai.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { + return true; + } + + return false; + } + /** * Disable carrier apps until they are privileged * Must be public b/c framework unit tests can't access package-private methods. @@ -138,7 +146,7 @@ public final class CarrierAppUtils { ContentResolver contentResolver, int userId, ArraySet<String> systemCarrierAppsDisabledUntilUsed, ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed) { - List<ApplicationInfo> candidates = getDefaultNotUpdatedCarrierAppCandidatesHelper( + List<ApplicationInfo> candidates = getDefaultCarrierAppCandidatesHelper( packageManager, userId, systemCarrierAppsDisabledUntilUsed); if (candidates == null || candidates.isEmpty()) { return; @@ -180,7 +188,7 @@ public final class CarrierAppUtils { if (hasPrivileges) { // Only update enabled state for the app on /system. Once it has been // updated we shouldn't touch it. - if (enabledSetting + if (!isUpdatedSystemApp(ai) && enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT || enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED @@ -232,7 +240,7 @@ public final class CarrierAppUtils { } else { // No carrier privileges // Only update enabled state for the app on /system. Once it has been // updated we shouldn't touch it. - if (enabledSetting + if (!isUpdatedSystemApp(ai) && enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0) { Log.i(TAG, "Update state(" + packageName @@ -363,31 +371,6 @@ public final class CarrierAppUtils { return apps; } - private static List<ApplicationInfo> getDefaultNotUpdatedCarrierAppCandidatesHelper( - IPackageManager packageManager, - int userId, - ArraySet<String> systemCarrierAppsDisabledUntilUsed) { - if (systemCarrierAppsDisabledUntilUsed == null) { - return null; - } - - int size = systemCarrierAppsDisabledUntilUsed.size(); - if (size == 0) { - return null; - } - - List<ApplicationInfo> apps = new ArrayList<>(size); - for (int i = 0; i < size; i++) { - String packageName = systemCarrierAppsDisabledUntilUsed.valueAt(i); - ApplicationInfo ai = - getApplicationInfoIfNotUpdatedSystemApp(packageManager, userId, packageName); - if (ai != null) { - apps.add(ai); - } - } - return apps; - } - private static Map<String, List<ApplicationInfo>> getDefaultCarrierAssociatedAppsHelper( IPackageManager packageManager, int userId, @@ -400,11 +383,11 @@ public final class CarrierAppUtils { systemCarrierAssociatedAppsDisabledUntilUsed.valueAt(i); for (int j = 0; j < associatedAppPackages.size(); j++) { ApplicationInfo ai = - getApplicationInfoIfNotUpdatedSystemApp( + getApplicationInfoIfSystemApp( packageManager, userId, associatedAppPackages.get(j)); // Only update enabled state for the app on /system. Once it has been updated we // shouldn't touch it. - if (ai != null) { + if (ai != null && !isUpdatedSystemApp(ai)) { List<ApplicationInfo> appList = associatedApps.get(carrierAppPackage); if (appList == null) { appList = new ArrayList<>(); @@ -418,26 +401,6 @@ public final class CarrierAppUtils { } @Nullable - private static ApplicationInfo getApplicationInfoIfNotUpdatedSystemApp( - IPackageManager packageManager, - int userId, - String packageName) { - try { - ApplicationInfo ai = packageManager.getApplicationInfo(packageName, - PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS - | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS - | PackageManager.MATCH_SYSTEM_ONLY - | PackageManager.MATCH_FACTORY_ONLY, userId); - if (ai != null) { - return ai; - } - } catch (RemoteException e) { - Log.w(TAG, "Could not reach PackageManager", e); - } - return null; - } - - @Nullable private static ApplicationInfo getApplicationInfoIfSystemApp( IPackageManager packageManager, int userId, diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java index 1a049e6c517e..d69282579b77 100644 --- a/telephony/common/com/android/internal/telephony/SmsApplication.java +++ b/telephony/common/com/android/internal/telephony/SmsApplication.java @@ -542,13 +542,16 @@ public final class SmsApplication { // Assign permission to special system apps assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps, - PHONE_PACKAGE_NAME); + PHONE_PACKAGE_NAME, true); assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps, - BLUETOOTH_PACKAGE_NAME); + BLUETOOTH_PACKAGE_NAME, true); assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps, - MMS_SERVICE_PACKAGE_NAME); + MMS_SERVICE_PACKAGE_NAME, true); assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps, - TELEPHONY_PROVIDER_PACKAGE_NAME); + TELEPHONY_PROVIDER_PACKAGE_NAME, true); + // CellbroadcastReceiver is a mainline module thus skip signature match. + assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps, + CellBroadcastUtils.getDefaultCellBroadcastReceiverPackageName(context), false); // Give AppOps permission to UID 1001 which contains multiple // apps, all of them should be able to write to telephony provider. @@ -738,17 +741,23 @@ public final class SmsApplication { * @param packageManager The package manager instance * @param appOps The AppOps manager instance * @param packageName The package name of the system app + * @param sigatureMatch whether to check signature match */ private static void assignExclusiveSmsPermissionsToSystemApp(Context context, - PackageManager packageManager, AppOpsManager appOps, String packageName) { + PackageManager packageManager, AppOpsManager appOps, String packageName, + boolean sigatureMatch) { // First check package signature matches the caller's package signature. // Since this class is only used internally by the system, this check makes sure // the package signature matches system signature. - final int result = packageManager.checkSignatures(context.getPackageName(), packageName); - if (result != PackageManager.SIGNATURE_MATCH) { - Log.e(LOG_TAG, packageName + " does not have system signature"); - return; + if (sigatureMatch) { + final int result = packageManager.checkSignatures(context.getPackageName(), + packageName); + if (result != PackageManager.SIGNATURE_MATCH) { + Log.e(LOG_TAG, packageName + " does not have system signature"); + return; + } } + try { PackageInfo info = packageManager.getPackageInfo(packageName, 0); int mode = appOps.unsafeCheckOp(AppOpsManager.OPSTR_WRITE_SMS, info.applicationInfo.uid, diff --git a/telephony/java/android/service/euicc/EuiccProfileInfo.java b/telephony/java/android/service/euicc/EuiccProfileInfo.java index 8450a9018634..92e419707970 100644 --- a/telephony/java/android/service/euicc/EuiccProfileInfo.java +++ b/telephony/java/android/service/euicc/EuiccProfileInfo.java @@ -29,6 +29,7 @@ import android.text.TextUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Objects; @@ -231,7 +232,9 @@ public final class EuiccProfileInfo implements Parcelable { mState = baseProfile.mState; mCarrierIdentifier = baseProfile.mCarrierIdentifier; mPolicyRules = baseProfile.mPolicyRules; - mAccessRules = Arrays.asList(baseProfile.mAccessRules); + mAccessRules = baseProfile.mAccessRules == null + ? Collections.emptyList() + : Arrays.asList(baseProfile.mAccessRules); } /** Builds the profile instance. */ diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index d319e37c4ba6..b75f9a15f66f 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -2495,15 +2495,15 @@ public class CarrierConfigManager { /** * List of 4 customized 5G SS reference signal received quality (SSRSRQ) thresholds. * <p> - * Reference: 3GPP TS 38.215 + * Reference: 3GPP TS 38.215; 3GPP TS 38.133 section 10 * <p> - * 4 threshold integers must be within the boundaries [-20 dB, -3 dB], and the levels are: + * 4 threshold integers must be within the boundaries [-43 dB, 20 dB], and the levels are: * <UL> - * <LI>"NONE: [-20, threshold1]"</LI> + * <LI>"NONE: [-43, threshold1]"</LI> * <LI>"POOR: (threshold1, threshold2]"</LI> * <LI>"MODERATE: (threshold2, threshold3]"</LI> * <LI>"GOOD: (threshold3, threshold4]"</LI> - * <LI>"EXCELLENT: (threshold4, -3]"</LI> + * <LI>"EXCELLENT: (threshold4, 20]"</LI> * </UL> * <p> * This key is considered invalid if the format is violated. If the key is invalid or @@ -4188,12 +4188,12 @@ public class CarrierConfigManager { -65, /* SIGNAL_STRENGTH_GREAT */ }); sDefaults.putIntArray(KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY, - // Boundaries: [-20 dB, -3 dB] + // Boundaries: [-43 dB, 20 dB] new int[] { - -16, /* SIGNAL_STRENGTH_POOR */ - -12, /* SIGNAL_STRENGTH_MODERATE */ - -9, /* SIGNAL_STRENGTH_GOOD */ - -6 /* SIGNAL_STRENGTH_GREAT */ + -31, /* SIGNAL_STRENGTH_POOR */ + -19, /* SIGNAL_STRENGTH_MODERATE */ + -7, /* SIGNAL_STRENGTH_GOOD */ + 6 /* SIGNAL_STRENGTH_GREAT */ }); sDefaults.putIntArray(KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY, // Boundaries: [-23 dB, 40 dB] diff --git a/telephony/java/android/telephony/CellLocation.java b/telephony/java/android/telephony/CellLocation.java index 64776e377fa4..61f68ce32287 100644 --- a/telephony/java/android/telephony/CellLocation.java +++ b/telephony/java/android/telephony/CellLocation.java @@ -16,7 +16,9 @@ package android.telephony; +import android.app.ActivityThread; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Context; import android.os.Bundle; import android.os.RemoteException; import android.os.ServiceManager; @@ -32,15 +34,36 @@ import com.android.internal.telephony.PhoneConstants; public abstract class CellLocation { /** - * Request an update of the current location. If the location has changed, - * a broadcast will be sent to everyone registered with {@link - * PhoneStateListener#LISTEN_CELL_LOCATION}. + * This method will not do anything. + * + * Whenever location changes, a callback will automatically be be sent to + * all registrants of {@link PhoneStateListener#LISTEN_CELL_LOCATION}. + * + * <p>This method is a no-op for callers targeting SDK level 31 or greater. + * <p>This method is a no-op for callers that target SDK level 29 or 30 and lack + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. + * <p>This method is a no-op for callers that target SDK level 28 or below and lack + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. + * + * Callers wishing to request a single location update should use + * {@link TelephonyManager#requestCellInfoUpdate}. + * + * @deprecated this method has undesirable side-effects, and it calls into the OS without + * access to a {@link android.content.Context Context}, meaning that certain safety checks and + * attribution are error-prone. Given that this method has numerous downsides, and given that + * there are long-available superior alternatives, callers are strongly discouraged from using + * this method. */ + @Deprecated public static void requestLocationUpdate() { + // Since this object doesn't have a context, this is the best we can do. + final Context appContext = ActivityThread.currentApplication(); + if (appContext == null) return; // should never happen + try { ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.getService("phone")); if (phone != null) { - phone.updateServiceLocation(); + phone.updateServiceLocationWithPackageName(appContext.getOpPackageName()); } } catch (RemoteException ex) { // ignore it diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java index 95fe90a47654..8e50bba38e84 100644 --- a/telephony/java/android/telephony/CellSignalStrengthNr.java +++ b/telephony/java/android/telephony/CellSignalStrengthNr.java @@ -54,12 +54,12 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa }; // Lifted from Default carrier configs and max range of SSRSRQ - // Boundaries: [-20 dB, -3 dB] + // Boundaries: [-43 dB, 20 dB] private int[] mSsRsrqThresholds = new int[] { - -16, /* SIGNAL_STRENGTH_POOR */ - -12, /* SIGNAL_STRENGTH_MODERATE */ - -9, /* SIGNAL_STRENGTH_GOOD */ - -6 /* SIGNAL_STRENGTH_GREAT */ + -31, /* SIGNAL_STRENGTH_POOR */ + -19, /* SIGNAL_STRENGTH_MODERATE */ + -7, /* SIGNAL_STRENGTH_GOOD */ + 6 /* SIGNAL_STRENGTH_GREAT */ }; // Lifted from Default carrier configs and max range of SSSINR @@ -183,8 +183,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa } /** - * Reference: 3GPP TS 38.215. - * Range: -20 dB to -3 dB. + * Reference: 3GPP TS 38.215; 3GPP TS 38.133 section 10 + * Range: -43 dB to 20 dB. * @return SS reference signal received quality, {@link CellInfo#UNAVAILABLE} means unreported * value. */ diff --git a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java index c667165e7a0e..e91d6fc9d801 100644 --- a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java +++ b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java @@ -72,28 +72,20 @@ public final class DataSpecificRegistrationInfo implements Parcelable { /** * Provides network support info for LTE VoPS and LTE Emergency bearer support */ + @Nullable private final LteVopsSupportInfo mLteVopsSupportInfo; /** - * Indicates if it's using carrier aggregation - * - * @hide - */ - public boolean mIsUsingCarrierAggregation; - - /** * @hide */ DataSpecificRegistrationInfo( int maxDataCalls, boolean isDcNrRestricted, boolean isNrAvailable, - boolean isEnDcAvailable, LteVopsSupportInfo lteVops, - boolean isUsingCarrierAggregation) { + boolean isEnDcAvailable, @Nullable LteVopsSupportInfo lteVops) { this.maxDataCalls = maxDataCalls; this.isDcNrRestricted = isDcNrRestricted; this.isNrAvailable = isNrAvailable; this.isEnDcAvailable = isEnDcAvailable; this.mLteVopsSupportInfo = lteVops; - this.mIsUsingCarrierAggregation = isUsingCarrierAggregation; } /** @@ -102,32 +94,29 @@ public final class DataSpecificRegistrationInfo implements Parcelable { * @param dsri another data specific registration info * @hide */ - DataSpecificRegistrationInfo(DataSpecificRegistrationInfo dsri) { + DataSpecificRegistrationInfo(@NonNull DataSpecificRegistrationInfo dsri) { maxDataCalls = dsri.maxDataCalls; isDcNrRestricted = dsri.isDcNrRestricted; isNrAvailable = dsri.isNrAvailable; isEnDcAvailable = dsri.isEnDcAvailable; mLteVopsSupportInfo = dsri.mLteVopsSupportInfo; - mIsUsingCarrierAggregation = dsri.mIsUsingCarrierAggregation; } - private DataSpecificRegistrationInfo(Parcel source) { + private DataSpecificRegistrationInfo(/* @NonNull */ Parcel source) { maxDataCalls = source.readInt(); isDcNrRestricted = source.readBoolean(); isNrAvailable = source.readBoolean(); isEnDcAvailable = source.readBoolean(); mLteVopsSupportInfo = LteVopsSupportInfo.CREATOR.createFromParcel(source); - mIsUsingCarrierAggregation = source.readBoolean(); } @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(/* @NonNull */ Parcel dest, int flags) { dest.writeInt(maxDataCalls); dest.writeBoolean(isDcNrRestricted); dest.writeBoolean(isNrAvailable); dest.writeBoolean(isEnDcAvailable); mLteVopsSupportInfo.writeToParcel(dest, flags); - dest.writeBoolean(mIsUsingCarrierAggregation); } @Override @@ -144,8 +133,7 @@ public final class DataSpecificRegistrationInfo implements Parcelable { .append(" isDcNrRestricted = " + isDcNrRestricted) .append(" isNrAvailable = " + isNrAvailable) .append(" isEnDcAvailable = " + isEnDcAvailable) - .append(" " + mLteVopsSupportInfo.toString()) - .append(" mIsUsingCarrierAggregation = " + mIsUsingCarrierAggregation) + .append(" " + mLteVopsSupportInfo) .append(" }") .toString(); } @@ -153,7 +141,7 @@ public final class DataSpecificRegistrationInfo implements Parcelable { @Override public int hashCode() { return Objects.hash(maxDataCalls, isDcNrRestricted, isNrAvailable, isEnDcAvailable, - mLteVopsSupportInfo, mIsUsingCarrierAggregation); + mLteVopsSupportInfo); } @Override @@ -167,8 +155,7 @@ public final class DataSpecificRegistrationInfo implements Parcelable { && this.isDcNrRestricted == other.isDcNrRestricted && this.isNrAvailable == other.isNrAvailable && this.isEnDcAvailable == other.isEnDcAvailable - && this.mLteVopsSupportInfo.equals(other.mLteVopsSupportInfo) - && this.mIsUsingCarrierAggregation == other.mIsUsingCarrierAggregation; + && Objects.equals(mLteVopsSupportInfo, other.mLteVopsSupportInfo); } public static final @NonNull Parcelable.Creator<DataSpecificRegistrationInfo> CREATOR = @@ -192,23 +179,4 @@ public final class DataSpecificRegistrationInfo implements Parcelable { return mLteVopsSupportInfo; } - /** - * Set the flag indicating if using carrier aggregation. - * - * @param isUsingCarrierAggregation {@code true} if using carrier aggregation. - * @hide - */ - public void setIsUsingCarrierAggregation(boolean isUsingCarrierAggregation) { - mIsUsingCarrierAggregation = isUsingCarrierAggregation; - } - - /** - * Get whether network has configured carrier aggregation or not. - * - * @return {@code true} if using carrier aggregation. - * @hide - */ - public boolean isUsingCarrierAggregation() { - return mIsUsingCarrierAggregation; - } } diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java index 31a83c9334d5..68753e11d395 100644 --- a/telephony/java/android/telephony/NetworkRegistrationInfo.java +++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java @@ -218,6 +218,9 @@ public final class NetworkRegistrationInfo implements Parcelable { @NonNull private String mRplmn; + // Updated based on the accessNetworkTechnology + private boolean mIsUsingCarrierAggregation; + /** * @param domain Network domain. Must be a {@link Domain}. For transport type * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, this must set to {@link #DOMAIN_PS}. @@ -251,7 +254,7 @@ public final class NetworkRegistrationInfo implements Parcelable { mRegistrationState = registrationState; mRoamingType = (registrationState == REGISTRATION_STATE_ROAMING) ? ServiceState.ROAMING_TYPE_UNKNOWN : ServiceState.ROAMING_TYPE_NOT_ROAMING; - mAccessNetworkTechnology = accessNetworkTechnology; + setAccessNetworkTechnology(accessNetworkTechnology); mRejectCause = rejectCause; mAvailableServices = (availableServices != null) ? new ArrayList<>(availableServices) : new ArrayList<>(); @@ -290,13 +293,11 @@ public final class NetworkRegistrationInfo implements Parcelable { @Nullable CellIdentity cellIdentity, @Nullable String rplmn, int maxDataCalls, boolean isDcNrRestricted, boolean isNrAvailable, boolean isEndcAvailable, - LteVopsSupportInfo lteVopsSupportInfo, - boolean isUsingCarrierAggregation) { + LteVopsSupportInfo lteVopsSupportInfo) { this(domain, transportType, registrationState, accessNetworkTechnology, rejectCause, emergencyOnly, availableServices, cellIdentity, rplmn); mDataSpecificInfo = new DataSpecificRegistrationInfo( - maxDataCalls, isDcNrRestricted, isNrAvailable, isEndcAvailable, lteVopsSupportInfo, - isUsingCarrierAggregation); + maxDataCalls, isDcNrRestricted, isNrAvailable, isEndcAvailable, lteVopsSupportInfo); updateNrState(); } @@ -317,6 +318,7 @@ public final class NetworkRegistrationInfo implements Parcelable { DataSpecificRegistrationInfo.class.getClassLoader()); mNrState = source.readInt(); mRplmn = source.readString(); + mIsUsingCarrierAggregation = source.readBoolean(); } /** @@ -331,6 +333,7 @@ public final class NetworkRegistrationInfo implements Parcelable { mRegistrationState = nri.mRegistrationState; mRoamingType = nri.mRoamingType; mAccessNetworkTechnology = nri.mAccessNetworkTechnology; + mIsUsingCarrierAggregation = nri.mIsUsingCarrierAggregation; mRejectCause = nri.mRejectCause; mEmergencyOnly = nri.mEmergencyOnly; mAvailableServices = new ArrayList<>(nri.mAvailableServices); @@ -484,9 +487,7 @@ public final class NetworkRegistrationInfo implements Parcelable { if (tech == TelephonyManager.NETWORK_TYPE_LTE_CA) { // For old device backward compatibility support tech = TelephonyManager.NETWORK_TYPE_LTE; - if (mDataSpecificInfo != null) { - mDataSpecificInfo.setIsUsingCarrierAggregation(true); - } + mIsUsingCarrierAggregation = true; } mAccessNetworkTechnology = tech; } @@ -511,6 +512,27 @@ public final class NetworkRegistrationInfo implements Parcelable { } /** + * Set whether network has configured carrier aggregation or not. + * + * @param isUsingCarrierAggregation set whether or not carrier aggregation is used. + * + * @hide + */ + public void setIsUsingCarrierAggregation(boolean isUsingCarrierAggregation) { + mIsUsingCarrierAggregation = isUsingCarrierAggregation; + } + + /** + * Get whether network has configured carrier aggregation or not. + * + * @return {@code true} if using carrier aggregation. + * @hide + */ + public boolean isUsingCarrierAggregation() { + return mIsUsingCarrierAggregation; + } + + /** * @hide */ @Nullable @@ -616,6 +638,7 @@ public final class NetworkRegistrationInfo implements Parcelable { .append(" dataSpecificInfo=").append(mDataSpecificInfo) .append(" nrState=").append(nrStateToString(mNrState)) .append(" rRplmn=").append(mRplmn) + .append(" isUsingCarrierAggregation=").append(mIsUsingCarrierAggregation) .append("}").toString(); } @@ -623,7 +646,8 @@ public final class NetworkRegistrationInfo implements Parcelable { public int hashCode() { return Objects.hash(mDomain, mTransportType, mRegistrationState, mRoamingType, mAccessNetworkTechnology, mRejectCause, mEmergencyOnly, mAvailableServices, - mCellIdentity, mVoiceSpecificInfo, mDataSpecificInfo, mNrState, mRplmn); + mCellIdentity, mVoiceSpecificInfo, mDataSpecificInfo, mNrState, mRplmn, + mIsUsingCarrierAggregation); } @Override @@ -643,6 +667,7 @@ public final class NetworkRegistrationInfo implements Parcelable { && mRejectCause == other.mRejectCause && mEmergencyOnly == other.mEmergencyOnly && mAvailableServices.equals(other.mAvailableServices) + && mIsUsingCarrierAggregation == other.mIsUsingCarrierAggregation && Objects.equals(mCellIdentity, other.mCellIdentity) && Objects.equals(mVoiceSpecificInfo, other.mVoiceSpecificInfo) && Objects.equals(mDataSpecificInfo, other.mDataSpecificInfo) @@ -669,6 +694,7 @@ public final class NetworkRegistrationInfo implements Parcelable { dest.writeParcelable(mDataSpecificInfo, 0); dest.writeInt(mNrState); dest.writeString(mRplmn); + dest.writeBoolean(mIsUsingCarrierAggregation); } /** diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java index a9abe89abab0..dd37ec3b6e54 100644 --- a/telephony/java/android/telephony/PreciseDataConnectionState.java +++ b/telephony/java/android/telephony/PreciseDataConnectionState.java @@ -266,7 +266,6 @@ public final class PreciseDataConnectionState implements Parcelable { * * @return the ApnSetting that was used to configure this data connection. */ - // FIXME: This shouldn't be nullable; update once the ApnSetting is supplied correctly public @Nullable ApnSetting getApnSetting() { return mApnSetting; } diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 5c84297d3b86..243b24b667c7 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -1392,29 +1392,14 @@ public class ServiceState implements Parcelable { /** @hide */ public boolean isUsingCarrierAggregation() { - boolean isUsingCa = false; - NetworkRegistrationInfo nri = getNetworkRegistrationInfo( - NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); - if (nri != null) { - DataSpecificRegistrationInfo dsri = nri.getDataSpecificInfo(); - if (dsri != null) { - isUsingCa = dsri.isUsingCarrierAggregation(); - } - } - return isUsingCa || getCellBandwidths().length > 1; - } + if (getCellBandwidths().length > 1) return true; - /** @hide */ - public void setIsUsingCarrierAggregation(boolean ca) { - NetworkRegistrationInfo nri = getNetworkRegistrationInfo( - NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); - if (nri != null) { - DataSpecificRegistrationInfo dsri = nri.getDataSpecificInfo(); - if (dsri != null) { - dsri.setIsUsingCarrierAggregation(ca); - addNetworkRegistrationInfo(nri); + synchronized (mNetworkRegistrationInfos) { + for (NetworkRegistrationInfo nri : mNetworkRegistrationInfos) { + if (nri.isUsingCarrierAggregation()) return true; } } + return false; } /** diff --git a/telephony/java/android/telephony/SmsCbEtwsInfo.java b/telephony/java/android/telephony/SmsCbEtwsInfo.java index 2a7f7ad81e3b..a98916d715e4 100644 --- a/telephony/java/android/telephony/SmsCbEtwsInfo.java +++ b/telephony/java/android/telephony/SmsCbEtwsInfo.java @@ -27,6 +27,7 @@ import com.android.internal.telephony.uicc.IccUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.time.DateTimeException; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.Arrays; @@ -173,7 +174,7 @@ public final class SmsCbEtwsInfo implements Parcelable { /** * Returns the Warning-Security-Information timestamp (GSM primary notifications only). * As of Release 10, 3GPP TS 23.041 states that the UE shall ignore this value if received. - * @return a UTC timestamp in System.currentTimeMillis() format, or 0 if not present + * @return a UTC timestamp in System.currentTimeMillis() format, or 0 if not present or invalid. */ public long getPrimaryNotificationTimestamp() { if (mWarningSecurityInformation == null || mWarningSecurityInformation.length < 7) { @@ -201,18 +202,23 @@ public final class SmsCbEtwsInfo implements Parcelable { // timezoneOffset is in quarter hours. int timeZoneOffsetSeconds = timezoneOffset * 15 * 60; - LocalDateTime localDateTime = LocalDateTime.of( - // We only need to support years above 2000. - year + 2000, - month /* 1-12 */, - day, - hour, - minute, - second); - - long epochSeconds = localDateTime.toEpochSecond(ZoneOffset.UTC) - timeZoneOffsetSeconds; - // Convert to milliseconds, ignore overflow. - return epochSeconds * 1000; + try { + LocalDateTime localDateTime = LocalDateTime.of( + // We only need to support years above 2000. + year + 2000, + month /* 1-12 */, + day, + hour, + minute, + second); + + long epochSeconds = localDateTime.toEpochSecond(ZoneOffset.UTC) - timeZoneOffsetSeconds; + // Convert to milliseconds, ignore overflow. + return epochSeconds * 1000; + } catch (DateTimeException ex) { + // No-op + } + return 0; } /** diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index 7456aabc8e19..abcc82bcdfe7 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -615,10 +615,12 @@ public final class SmsManager { } if (priority < 0x00 || priority > 0x03) { + Log.e(TAG, "Invalid Priority " + priority); priority = SMS_MESSAGE_PRIORITY_NOT_SPECIFIED; } if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) { + Log.e(TAG, "Invalid Validity Period " + validityPeriod); validityPeriod = SMS_MESSAGE_PERIOD_NOT_SPECIFIED; } @@ -1174,10 +1176,12 @@ public final class SmsManager { } if (priority < 0x00 || priority > 0x03) { + Log.e(TAG, "Invalid Priority " + priority); priority = SMS_MESSAGE_PRIORITY_NOT_SPECIFIED; } if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) { + Log.e(TAG, "Invalid Validity Period " + validityPeriod); validityPeriod = SMS_MESSAGE_PERIOD_NOT_SPECIFIED; } diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index 7266fc05cffa..392670ba992c 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -255,28 +255,6 @@ public class SmsMessage { } /** - * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the - * +CMT unsolicited response (PDU mode, of course) - * +CMT: [<alpha>],<length><CR><LF><pdu> - * - * Only public for debugging and for RIL - * - * {@hide} - */ - public static SmsMessage newFromCMT(byte[] pdu) { - // received SMS in 3GPP format - SmsMessageBase wrappedMessage = - com.android.internal.telephony.gsm.SmsMessage.newFromCMT(pdu); - - if (wrappedMessage != null) { - return new SmsMessage(wrappedMessage); - } else { - Rlog.e(LOG_TAG, "newFromCMT(): wrappedMessage is null"); - return null; - } - } - - /** * Creates an SmsMessage from an SMS EF record. * * @param index Index of SMS EF record. diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 6b498b5f8697..4c7253ce76c6 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -110,8 +110,6 @@ import com.android.internal.telephony.RILConstants; import com.android.internal.telephony.SmsApplication; import com.android.telephony.Rlog; -import java.io.FileInputStream; -import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -124,8 +122,6 @@ import java.util.Objects; import java.util.UUID; import java.util.concurrent.Executor; import java.util.function.Consumer; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * Provides access to information about the telephony services on @@ -2189,58 +2185,6 @@ public class TelephonyManager { } /** - * Enables location update notifications. {@link PhoneStateListener#onCellLocationChanged - * PhoneStateListener.onCellLocationChanged} will be called on location updates. - * - * @hide - */ - @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES) - public void enableLocationUpdates() { - enableLocationUpdates(getSubId()); - } - - /** - * Enables location update notifications for a subscription. - * {@link PhoneStateListener#onCellLocationChanged - * PhoneStateListener.onCellLocationChanged} will be called on location updates. - * - * @param subId for which the location updates are enabled - * @hide - */ - @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES) - public void enableLocationUpdates(int subId) { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) - telephony.enableLocationUpdatesForSubscriber(subId); - } catch (RemoteException ex) { - } catch (NullPointerException ex) { - } - } - - /** - * Disables location update notifications. {@link PhoneStateListener#onCellLocationChanged - * PhoneStateListener.onCellLocationChanged} will be called on location updates. - * - * @hide - */ - @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES) - public void disableLocationUpdates() { - disableLocationUpdates(getSubId()); - } - - /** @hide */ - public void disableLocationUpdates(int subId) { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) - telephony.disableLocationUpdatesForSubscriber(subId); - } catch (RemoteException ex) { - } catch (NullPointerException ex) { - } - } - - /** * Returns the neighboring cell information of the device. * * @return List of NeighboringCellInfo or null if info unavailable. @@ -2440,7 +2384,8 @@ public class TelephonyManager { return PhoneConstants.PHONE_TYPE_CDMA; case RILConstants.NETWORK_MODE_LTE_ONLY: - if (getLteOnCdmaModeStatic() == PhoneConstants.LTE_ON_CDMA_TRUE) { + if (TelephonyProperties.lte_on_cdma_device().orElse( + PhoneConstants.LTE_ON_CDMA_FALSE) == PhoneConstants.LTE_ON_CDMA_TRUE) { return PhoneConstants.PHONE_TYPE_CDMA; } else { return PhoneConstants.PHONE_TYPE_GSM; @@ -2451,35 +2396,6 @@ public class TelephonyManager { } /** - * The contents of the /proc/cmdline file - */ - @UnsupportedAppUsage - private static String getProcCmdLine() - { - String cmdline = ""; - FileInputStream is = null; - try { - is = new FileInputStream("/proc/cmdline"); - byte [] buffer = new byte[2048]; - int count = is.read(buffer); - if (count > 0) { - cmdline = new String(buffer, 0, count); - } - } catch (IOException e) { - Rlog.d(TAG, "No /proc/cmdline exception=" + e); - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - } - } - } - Rlog.d(TAG, "/proc/cmdline=" + cmdline); - return cmdline; - } - - /** * @return The max value for the timeout passed in {@link #requestNumberVerification}. * @hide */ @@ -2488,56 +2404,6 @@ public class TelephonyManager { return MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS; } - /** Kernel command line */ - private static final String sKernelCmdLine = getProcCmdLine(); - - /** Pattern for selecting the product type from the kernel command line */ - private static final Pattern sProductTypePattern = - Pattern.compile("\\sproduct_type\\s*=\\s*(\\w+)"); - - /** The ProductType used for LTE on CDMA devices */ - private static final String sLteOnCdmaProductType = - TelephonyProperties.lte_on_cdma_product_type().orElse(""); - - /** - * Return if the current radio is LTE on CDMA. This - * is a tri-state return value as for a period of time - * the mode may be unknown. - * - * @return {@link PhoneConstants#LTE_ON_CDMA_UNKNOWN}, {@link PhoneConstants#LTE_ON_CDMA_FALSE} - * or {@link PhoneConstants#LTE_ON_CDMA_TRUE} - * - * @hide - */ - @UnsupportedAppUsage - public static int getLteOnCdmaModeStatic() { - int retVal; - int curVal; - String productType = ""; - - curVal = TelephonyProperties.lte_on_cdma_device().orElse( - PhoneConstants.LTE_ON_CDMA_UNKNOWN); - retVal = curVal; - if (retVal == PhoneConstants.LTE_ON_CDMA_UNKNOWN) { - Matcher matcher = sProductTypePattern.matcher(sKernelCmdLine); - if (matcher.find()) { - productType = matcher.group(1); - if (sLteOnCdmaProductType.equals(productType)) { - retVal = PhoneConstants.LTE_ON_CDMA_TRUE; - } else { - retVal = PhoneConstants.LTE_ON_CDMA_FALSE; - } - } else { - retVal = PhoneConstants.LTE_ON_CDMA_FALSE; - } - } - - Rlog.d(TAG, "getLteOnCdmaMode=" + retVal + " curVal=" + curVal + - " product_type='" + productType + - "' lteOnCdmaProductType='" + sLteOnCdmaProductType + "'"); - return retVal; - } - // // // Current Network @@ -8109,6 +7975,140 @@ public class TelephonyManager { return false; } + /** @hide */ + @IntDef({ + ALLOWED_NETWORK_TYPES_REASON_POWER + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AllowedNetworkTypesReason{} + + /** + * To indicate allowed network type change is requested by power manager. + * Power Manger configuration won't affect the settings configured through + * {@link setAllowedNetworkTypes} and will result in allowing network types that are in both + * configurations (i.e intersection of both sets). + * @hide + */ + public static final int ALLOWED_NETWORK_TYPES_REASON_POWER = 0; + + /** + * Set the allowed network types of the device and + * provide the reason triggering the allowed network change. + * This can be called for following reasons + * <ol> + * <li>Allowed network types control by power manager + * {@link #ALLOWED_NETWORK_TYPES_REASON_POWER} + * </ol> + * This API will result in allowing an intersection of allowed network types for all reasons, + * including the configuration done through {@link setAllowedNetworkTypes}. + * While this API and {@link setAllowedNetworkTypes} is controlling allowed network types + * on device, user preference will still be set through {@link #setPreferredNetworkTypeBitmask}. + * Thus resultant network type configured on modem will be an intersection of the network types + * from setAllowedNetworkTypesForReason, {@link setAllowedNetworkTypes} + * and {@link #setPreferredNetworkTypeBitmask}. + * + * @param reason the reason the allowed network type change is taking place + * @param allowedNetworkTypes The bitmask of allowed network types. + * @throws IllegalStateException if the Telephony process is not currently available. + * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed. + * @hide + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void setAllowedNetworkTypesForReason(@AllowedNetworkTypesReason int reason, + @NetworkTypeBitMask long allowedNetworkTypes) { + if (reason != ALLOWED_NETWORK_TYPES_REASON_POWER) { + throw new IllegalArgumentException("invalid AllowedNetworkTypesReason."); + } + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.setAllowedNetworkTypesForReason(getSubId(), reason, + allowedNetworkTypes); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "setAllowedNetworkTypesForReason RemoteException", ex); + ex.rethrowFromSystemServer(); + } + } + + /** + * Get the allowed network types for certain reason. + * + * {@link #getAllowedNetworkTypesForReason} returns allowed network type for a + * specific reason. For effective allowed network types configured on device, + * query {@link getEffectiveAllowedNetworkTypes} + * + * <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}). + *s + * @param reason the reason the allowed network type change is taking place + * @return the allowed network type bitmask + * @throws IllegalStateException if the Telephony process is not currently available. + * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed. + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public @NetworkTypeBitMask long getAllowedNetworkTypesForReason( + @AllowedNetworkTypesReason int reason) { + if (reason != ALLOWED_NETWORK_TYPES_REASON_POWER) { + throw new IllegalArgumentException("invalid AllowedNetworkTypesReason."); + } + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getAllowedNetworkTypesForReason(getSubId(), reason); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "getAllowedNetworkTypesForReason RemoteException", ex); + ex.rethrowFromSystemServer(); + } + return -1; + } + + /** + * Get bit mask of all network types. + * + * @return bit mask of all network types + * @hide + */ + public static @NetworkTypeBitMask long getAllNetworkTypesBitmask() { + return NETWORK_STANDARDS_FAMILY_BITMASK_3GPP | NETWORK_STANDARDS_FAMILY_BITMASK_3GPP2; + } + + /** + * Get the allowed network types configured on the device. + * This API will return an intersection of allowed network types for all reasons, + * including the configuration done through setAllowedNetworkTypes + * + * <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 allowed network type bitmask + * @throws IllegalStateException if the Telephony process is not currently available. + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public @NetworkTypeBitMask long getEffectiveAllowedNetworkTypes() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getEffectiveAllowedNetworkTypes(getSubId()); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "getEffectiveAllowedNetworkTypes RemoteException", ex); + ex.rethrowFromSystemServer(); + } + return -1; + } + /** * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA. * @@ -8969,17 +8969,14 @@ public class TelephonyManager { return RADIO_POWER_UNAVAILABLE; } - /** @hide */ + /** + * This method should not be used due to privacy and stability concerns. + * + * @hide + */ @SystemApi - @SuppressLint("Doclava125") public void updateServiceLocation() { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) - telephony.updateServiceLocation(); - } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#updateServiceLocation", e); - } + Log.e(TAG, "Do not call TelephonyManager#updateServiceLocation()"); } /** @hide */ @@ -12608,7 +12605,6 @@ public class TelephonyManager { @Nullable String mvnoMatchData) { try { if (!mccmnc.equals(getSimOperator())) { - Log.d(TAG, "The mccmnc does not match"); return false; } ITelephony service = getITelephony(); diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java index 359d08d294c3..3f8fd9d68a4f 100644 --- a/telephony/java/android/telephony/TelephonyScanManager.java +++ b/telephony/java/android/telephony/TelephonyScanManager.java @@ -31,6 +31,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.util.SparseArray; +import com.android.internal.annotations.GuardedBy; import com.android.internal.telephony.ITelephony; import com.android.telephony.Rlog; @@ -56,6 +57,8 @@ public final class TelephonyScanManager { public static final int CALLBACK_SCAN_COMPLETE = 3; /** @hide */ public static final int CALLBACK_RESTRICTED_SCAN_RESULTS = 4; + /** @hide */ + public static final int CALLBACK_TELEPHONY_DIED = 5; /** @hide */ public static final int INVALID_SCAN_ID = -1; @@ -104,17 +107,44 @@ public final class TelephonyScanManager { } private final Looper mLooper; + private final Handler mHandler; private final Messenger mMessenger; private final SparseArray<NetworkScanInfo> mScanInfo = new SparseArray<NetworkScanInfo>(); + private final Binder.DeathRecipient mDeathRecipient; public TelephonyScanManager() { HandlerThread thread = new HandlerThread(TAG); thread.start(); mLooper = thread.getLooper(); - mMessenger = new Messenger(new Handler(mLooper) { + mHandler = new Handler(mLooper) { @Override public void handleMessage(Message message) { checkNotNull(message, "message cannot be null"); + if (message.what == CALLBACK_TELEPHONY_DIED) { + // If there are no objects in mScanInfo then binder death will simply return. + synchronized (mScanInfo) { + for (int i = 0; i < mScanInfo.size(); i++) { + NetworkScanInfo nsi = mScanInfo.valueAt(i); + // At this point we go into panic mode and ignore errors that would + // normally stop the show in order to try and clean up as gracefully + // as possible. + if (nsi == null) continue; // shouldn't be possible + Executor e = nsi.mExecutor; + NetworkScanCallback cb = nsi.mCallback; + if (e == null || cb == null) continue; + try { + e.execute( + () -> cb.onError(NetworkScan.ERROR_MODEM_UNAVAILABLE)); + } catch (java.util.concurrent.RejectedExecutionException ignore) { + // ignore so that we can continue + } + } + + mScanInfo.clear(); + } + return; + } + NetworkScanInfo nsi; synchronized (mScanInfo) { nsi = mScanInfo.get(message.arg2); @@ -159,6 +189,9 @@ public final class TelephonyScanManager { Rlog.d(TAG, "onError: " + errorCode); callback.onError(errorCode); }); + synchronized (mScanInfo) { + mScanInfo.remove(message.arg2); + } } catch (Exception e) { Rlog.e(TAG, "Exception in networkscan callback onError", e); } @@ -169,7 +202,9 @@ public final class TelephonyScanManager { Rlog.d(TAG, "onComplete"); callback.onComplete(); }); - mScanInfo.remove(message.arg2); + synchronized (mScanInfo) { + mScanInfo.remove(message.arg2); + } } catch (Exception e) { Rlog.e(TAG, "Exception in networkscan callback onComplete", e); } @@ -179,7 +214,14 @@ public final class TelephonyScanManager { break; } } - }); + }; + mMessenger = new Messenger(mHandler); + mDeathRecipient = new Binder.DeathRecipient() { + @Override + public void binderDied() { + mHandler.obtainMessage(CALLBACK_TELEPHONY_DIED).sendToTarget(); + } + }; } /** @@ -190,7 +232,7 @@ public final class TelephonyScanManager { * * <p> * Requires Permission: - * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} and + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} * Or the calling app has carrier privileges. @see #hasCarrierPrivileges * @@ -203,19 +245,26 @@ public final class TelephonyScanManager { NetworkScanRequest request, Executor executor, NetworkScanCallback callback, String callingPackage, String callingFeatureId) { try { - ITelephony telephony = getITelephony(); - if (telephony != null) { - synchronized (mScanInfo) { - int scanId = telephony.requestNetworkScan( - subId, request, mMessenger, new Binder(), callingPackage, - callingFeatureId); - if (scanId == INVALID_SCAN_ID) { - Rlog.e(TAG, "Failed to initiate network scan"); - return null; - } - saveScanInfo(scanId, request, executor, callback); - return new NetworkScan(scanId, subId); - } + final ITelephony telephony = getITelephony(); + if (telephony == null) return null; + + int scanId = telephony.requestNetworkScan( + subId, request, mMessenger, new Binder(), callingPackage, + callingFeatureId); + if (scanId == INVALID_SCAN_ID) { + Rlog.e(TAG, "Failed to initiate network scan"); + return null; + } + synchronized (mScanInfo) { + // We link to death whenever a scan is started to ensure that we are linked + // at the point that phone process death might matter. + // We never unlink because: + // - Duplicate links to death with the same callback do not result in + // extraneous callbacks (the tracking de-dupes). + // - Receiving binderDeath() when no scans are active is a no-op. + telephony.asBinder().linkToDeath(mDeathRecipient, 0); + saveScanInfo(scanId, request, executor, callback); + return new NetworkScan(scanId, subId); } } catch (RemoteException ex) { Rlog.e(TAG, "requestNetworkScan RemoteException", ex); @@ -225,6 +274,7 @@ public final class TelephonyScanManager { return null; } + @GuardedBy("mScanInfo") private void saveScanInfo( int id, NetworkScanRequest request, Executor executor, NetworkScanCallback callback) { mScanInfo.put(id, new NetworkScanInfo(request, executor, callback)); diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java index 6840e8eed318..5ee6ec957b50 100644 --- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java +++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java @@ -346,7 +346,6 @@ public class MmTelFeature extends ImsFeature { * @hide */ @Override - @SystemApi @TestApi public void onIncomingCall(IImsCallSession c, Bundle extras) { } @@ -358,7 +357,6 @@ public class MmTelFeature extends ImsFeature { * @hide */ @Override - @SystemApi @TestApi public void onRejectedCall(ImsCallProfile callProfile, ImsReasonInfo reason) { } @@ -369,7 +367,6 @@ public class MmTelFeature extends ImsFeature { * @hide */ @Override - @SystemApi @TestApi public void onVoiceMessageCountUpdate(int count) { } diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java index 7069e0ab9b1e..2cdf70e6cf4c 100644 --- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java @@ -29,6 +29,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.util.RemoteCallbackListExt; +import com.android.internal.util.ArrayUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -105,6 +106,11 @@ public class ImsRegistrationImplBase { // Locked on mLock, create unspecified disconnect cause. private ImsReasonInfo mLastDisconnectCause = new ImsReasonInfo(); + // We hold onto the uris each time they change so that we can send it to a callback when its + // first added. + private Uri[] mUris = new Uri[0]; + private boolean mUrisSet = false; + /** * @hide */ @@ -208,19 +214,27 @@ public class ImsRegistrationImplBase { } /** - * The this device's subscriber associated {@link Uri}s have changed, which are used to filter - * out this device's {@link Uri}s during conference calling. - * @param uris + * Invoked when the {@link Uri}s associated to this device's subscriber have changed. + * These {@link Uri}s' are filtered out during conference calls. + * + * The {@link Uri}s are not guaranteed to be different between subsequent calls. + * @param uris changed uris */ public final void onSubscriberAssociatedUriChanged(Uri[] uris) { - mCallbacks.broadcastAction((c) -> { - try { - c.onSubscriberAssociatedUriChanged(uris); - } catch (RemoteException e) { - Log.w(LOG_TAG, e + " " + "onSubscriberAssociatedUriChanged() - Skipping " + - "callback."); - } - }); + synchronized (mLock) { + mUris = ArrayUtils.cloneOrNull(uris); + mUrisSet = true; + } + mCallbacks.broadcastAction((c) -> onSubscriberAssociatedUriChanged(c, uris)); + } + + private void onSubscriberAssociatedUriChanged(IImsRegistrationCallback callback, Uri[] uris) { + try { + callback.onSubscriberAssociatedUriChanged(uris); + } catch (RemoteException e) { + Log.w(LOG_TAG, e + " " + "onSubscriberAssociatedUriChanged() - Skipping " + + "callback."); + } } private void updateToState(@ImsRegistrationTech int connType, int newState) { @@ -233,6 +247,10 @@ public class ImsRegistrationImplBase { private void updateToDisconnectedState(ImsReasonInfo info) { synchronized (mLock) { + //We don't want to send this info over if we are disconnected + mUrisSet = false; + mUris = null; + updateToState(REGISTRATION_TECH_NONE, RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED); if (info != null) { @@ -260,12 +278,17 @@ public class ImsRegistrationImplBase { * @param c the newly registered callback that will be updated with the current registration * state. */ - private void updateNewCallbackWithState(IImsRegistrationCallback c) throws RemoteException { + private void updateNewCallbackWithState(IImsRegistrationCallback c) + throws RemoteException { int state; ImsReasonInfo disconnectInfo; + boolean urisSet; + Uri[] uris; synchronized (mLock) { state = mRegistrationState; disconnectInfo = mLastDisconnectCause; + urisSet = mUrisSet; + uris = mUris; } switch (state) { case RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED: { @@ -285,5 +308,8 @@ public class ImsRegistrationImplBase { break; } } + if (urisSet) { + onSubscriberAssociatedUriChanged(c, uris); + } } } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 5a6b997f0723..bf81ddcb0c15 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -222,42 +222,29 @@ interface ITelephony { boolean setRadioPower(boolean turnOn); /** - * Request to update location information in service state + * This method has been removed due to security and stability issues. */ @UnsupportedAppUsage void updateServiceLocation(); /** - * Request to update location information for a subscrition in service state - * @param subId user preferred subId. + * Version of updateServiceLocation that records the caller and validates permissions. */ - void updateServiceLocationForSubscriber(int subId); + void updateServiceLocationWithPackageName(String callingPkg); /** - * Enable location update notifications. + * This method has been removed due to security and stability issues. */ @UnsupportedAppUsage void enableLocationUpdates(); /** - * Enable location update notifications. - * @param subId user preferred subId. - */ - void enableLocationUpdatesForSubscriber(int subId); - - /** - * Disable location update notifications. + * This method has been removed due to security and stability issues. */ @UnsupportedAppUsage void disableLocationUpdates(); /** - * Disable location update notifications. - * @param subId user preferred subId. - */ - void disableLocationUpdatesForSubscriber(int subId); - - /** * Allow mobile data connections. */ @UnsupportedAppUsage @@ -956,6 +943,35 @@ interface ITelephony { boolean setAllowedNetworkTypes(int subId, long allowedNetworkTypes); /** + * Get the allowed network types for certain reason. + * + * @param subId the id of the subscription. + * @param reason the reason the allowed network type change is taking place + * @return allowedNetworkTypes the allowed network types. + */ + long getAllowedNetworkTypesForReason(int subId, int reason); + + /** + * Get the effective allowed network types on the device. This API will + * return an intersection of allowed network types for all reasons, + * including the configuration done through setAllowedNetworkTypes + * + * @param subId the id of the subscription. + * @return allowedNetworkTypes the allowed network types. + */ + long getEffectiveAllowedNetworkTypes(int subId); + + /** + * Set the allowed network types and provide the reason triggering the allowed network change. + * + * @param subId the id of the subscription. + * @param reason the reason the allowed network type change is taking place + * @param allowedNetworkTypes the allowed network types. + * @return true on success; false on any failure. + */ + boolean setAllowedNetworkTypesForReason(int subId, int reason, long allowedNetworkTypes); + + /** * Set the preferred network type. * Used for device configuration by some CDMA operators. * diff --git a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java index 3bd8cdd23df3..f8ab87d042eb 100644 --- a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java +++ b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java @@ -65,13 +65,7 @@ public class Sms7BitEncodingTranslator { return ""; } - if (!mIs7BitTranslationTableLoaded) { - mTranslationTableCommon = new SparseIntArray(); - mTranslationTableGSM = new SparseIntArray(); - mTranslationTableCDMA = new SparseIntArray(); - load7BitTranslationTableFromXml(); - mIs7BitTranslationTableLoaded = true; - } + ensure7BitTranslationTableLoaded(); if ((mTranslationTableCommon != null && mTranslationTableCommon.size() > 0) || (mTranslationTableGSM != null && mTranslationTableGSM.size() > 0) || @@ -115,6 +109,8 @@ public class Sms7BitEncodingTranslator { */ int translation = -1; + ensure7BitTranslationTableLoaded(); + if (mTranslationTableCommon != null) { translation = mTranslationTableCommon.get(c, -1); } @@ -155,6 +151,18 @@ public class Sms7BitEncodingTranslator { } } + private static void ensure7BitTranslationTableLoaded() { + synchronized (Sms7BitEncodingTranslator.class) { + if (!mIs7BitTranslationTableLoaded) { + mTranslationTableCommon = new SparseIntArray(); + mTranslationTableGSM = new SparseIntArray(); + mTranslationTableCDMA = new SparseIntArray(); + load7BitTranslationTableFromXml(); + mIs7BitTranslationTableLoaded = true; + } + } + } + /** * Load the whole translation table file from the framework resource * encoded in XML. diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java index 3f85de358ea2..109dd3b30827 100644 --- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java +++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java @@ -35,6 +35,7 @@ import com.android.internal.util.BitwiseOutputStream; import com.android.telephony.Rlog; import java.io.ByteArrayOutputStream; +import java.time.DateTimeException; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; @@ -315,10 +316,16 @@ public final class BearerData { } public long toMillis() { - LocalDateTime localDateTime = - LocalDateTime.of(year, monthOrdinal, monthDay, hour, minute, second); - Instant instant = localDateTime.toInstant(mZoneId.getRules().getOffset(localDateTime)); - return instant.toEpochMilli(); + try { + LocalDateTime localDateTime = + LocalDateTime.of(year, monthOrdinal, monthDay, hour, minute, second); + Instant instant = + localDateTime.toInstant(mZoneId.getRules().getOffset(localDateTime)); + return instant.toEpochMilli(); + } catch (DateTimeException ex) { + Rlog.e(LOG_TAG, "Invalid timestamp", ex); + } + return 0; } @@ -1093,7 +1100,7 @@ public final class BearerData { bData.hasUserDataHeader = (inStream.read(1) == 1); inStream.skip(3); } - if ((! decodeSuccess) || (paramBits > 0)) { + if ((!decodeSuccess) || (paramBits > 0)) { Rlog.d(LOG_TAG, "MESSAGE_IDENTIFIER decode " + (decodeSuccess ? "succeeded" : "failed") + " (extra bits = " + paramBits + ")"); @@ -1462,7 +1469,7 @@ public final class BearerData { bData.reportReq = (inStream.read(1) == 1); inStream.skip(4); } - if ((! decodeSuccess) || (paramBits > 0)) { + if ((!decodeSuccess) || (paramBits > 0)) { Rlog.d(LOG_TAG, "REPLY_OPTION decode " + (decodeSuccess ? "succeeded" : "failed") + " (extra bits = " + paramBits + ")"); @@ -1481,7 +1488,7 @@ public final class BearerData { decodeSuccess = true; bData.numberOfMessages = IccUtils.cdmaBcdByteToInt((byte)inStream.read(8)); } - if ((! decodeSuccess) || (paramBits > 0)) { + if ((!decodeSuccess) || (paramBits > 0)) { Rlog.d(LOG_TAG, "NUMBER_OF_MESSAGES decode " + (decodeSuccess ? "succeeded" : "failed") + " (extra bits = " + paramBits + ")"); @@ -1500,7 +1507,7 @@ public final class BearerData { decodeSuccess = true; bData.depositIndex = (inStream.read(8) << 8) | inStream.read(8); } - if ((! decodeSuccess) || (paramBits > 0)) { + if ((!decodeSuccess) || (paramBits > 0)) { Rlog.d(LOG_TAG, "MESSAGE_DEPOSIT_INDEX decode " + (decodeSuccess ? "succeeded" : "failed") + " (extra bits = " + paramBits + ")"); @@ -1587,7 +1594,7 @@ public final class BearerData { bData.errorClass = inStream.read(2); bData.messageStatus = inStream.read(6); } - if ((! decodeSuccess) || (paramBits > 0)) { + if ((!decodeSuccess) || (paramBits > 0)) { Rlog.d(LOG_TAG, "MESSAGE_STATUS decode " + (decodeSuccess ? "succeeded" : "failed") + " (extra bits = " + paramBits + ")"); @@ -1607,7 +1614,7 @@ public final class BearerData { decodeSuccess = true; bData.msgCenterTimeStamp = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8)); } - if ((! decodeSuccess) || (paramBits > 0)) { + if ((!decodeSuccess) || (paramBits > 0)) { Rlog.d(LOG_TAG, "MESSAGE_CENTER_TIME_STAMP decode " + (decodeSuccess ? "succeeded" : "failed") + " (extra bits = " + paramBits + ")"); @@ -1626,7 +1633,7 @@ public final class BearerData { decodeSuccess = true; bData.validityPeriodAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8)); } - if ((! decodeSuccess) || (paramBits > 0)) { + if ((!decodeSuccess) || (paramBits > 0)) { Rlog.d(LOG_TAG, "VALIDITY_PERIOD_ABSOLUTE decode " + (decodeSuccess ? "succeeded" : "failed") + " (extra bits = " + paramBits + ")"); @@ -1646,7 +1653,7 @@ public final class BearerData { bData.deferredDeliveryTimeAbsolute = TimeStamp.fromByteArray( inStream.readByteArray(6 * 8)); } - if ((! decodeSuccess) || (paramBits > 0)) { + if ((!decodeSuccess) || (paramBits > 0)) { Rlog.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_ABSOLUTE decode " + (decodeSuccess ? "succeeded" : "failed") + " (extra bits = " + paramBits + ")"); @@ -1665,7 +1672,7 @@ public final class BearerData { decodeSuccess = true; bData.deferredDeliveryTimeRelative = inStream.read(8); } - if ((! decodeSuccess) || (paramBits > 0)) { + if ((!decodeSuccess) || (paramBits > 0)) { Rlog.d(LOG_TAG, "VALIDITY_PERIOD_RELATIVE decode " + (decodeSuccess ? "succeeded" : "failed") + " (extra bits = " + paramBits + ")"); @@ -1685,7 +1692,7 @@ public final class BearerData { decodeSuccess = true; bData.validityPeriodRelative = inStream.read(8); } - if ((! decodeSuccess) || (paramBits > 0)) { + if ((!decodeSuccess) || (paramBits > 0)) { Rlog.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_RELATIVE decode " + (decodeSuccess ? "succeeded" : "failed") + " (extra bits = " + paramBits + ")"); @@ -1706,7 +1713,7 @@ public final class BearerData { bData.privacy = inStream.read(2); inStream.skip(6); } - if ((! decodeSuccess) || (paramBits > 0)) { + if ((!decodeSuccess) || (paramBits > 0)) { Rlog.d(LOG_TAG, "PRIVACY_INDICATOR decode " + (decodeSuccess ? "succeeded" : "failed") + " (extra bits = " + paramBits + ")"); @@ -1726,7 +1733,7 @@ public final class BearerData { decodeSuccess = true; bData.language = inStream.read(8); } - if ((! decodeSuccess) || (paramBits > 0)) { + if ((!decodeSuccess) || (paramBits > 0)) { Rlog.d(LOG_TAG, "LANGUAGE_INDICATOR decode " + (decodeSuccess ? "succeeded" : "failed") + " (extra bits = " + paramBits + ")"); @@ -1747,7 +1754,7 @@ public final class BearerData { bData.displayMode = inStream.read(2); inStream.skip(6); } - if ((! decodeSuccess) || (paramBits > 0)) { + if ((!decodeSuccess) || (paramBits > 0)) { Rlog.d(LOG_TAG, "DISPLAY_MODE decode " + (decodeSuccess ? "succeeded" : "failed") + " (extra bits = " + paramBits + ")"); @@ -1768,7 +1775,7 @@ public final class BearerData { bData.priority = inStream.read(2); inStream.skip(6); } - if ((! decodeSuccess) || (paramBits > 0)) { + if ((!decodeSuccess) || (paramBits > 0)) { Rlog.d(LOG_TAG, "PRIORITY_INDICATOR decode " + (decodeSuccess ? "succeeded" : "failed") + " (extra bits = " + paramBits + ")"); @@ -1789,7 +1796,7 @@ public final class BearerData { bData.alert = inStream.read(2); inStream.skip(6); } - if ((! decodeSuccess) || (paramBits > 0)) { + if ((!decodeSuccess) || (paramBits > 0)) { Rlog.d(LOG_TAG, "ALERT_ON_MESSAGE_DELIVERY decode " + (decodeSuccess ? "succeeded" : "failed") + " (extra bits = " + paramBits + ")"); @@ -1809,7 +1816,7 @@ public final class BearerData { decodeSuccess = true; bData.userResponseCode = inStream.read(8); } - if ((! decodeSuccess) || (paramBits > 0)) { + if ((!decodeSuccess) || (paramBits > 0)) { Rlog.d(LOG_TAG, "USER_RESPONSE_CODE decode " + (decodeSuccess ? "succeeded" : "failed") + " (extra bits = " + paramBits + ")"); @@ -1871,7 +1878,7 @@ public final class BearerData { decodeSuccess = true; } - if ((! decodeSuccess) || (paramBits > 0)) { + if ((!decodeSuccess) || (paramBits > 0)) { Rlog.d(LOG_TAG, "SERVICE_CATEGORY_PROGRAM_DATA decode " + (decodeSuccess ? "succeeded" : "failed") + " (extra bits = " + paramBits + ')'); diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java index 122f0851cf37..7d5710e32194 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java @@ -43,6 +43,7 @@ import com.android.telephony.Rlog; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.text.ParseException; +import java.time.DateTimeException; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; @@ -91,14 +92,15 @@ public class SmsMessage extends SmsMessageBase { private int mVoiceMailCount = 0; + /** TP-Validity-Period-Format (TP-VPF). See TS 23.040, 9.2.3.3 */ private static final int VALIDITY_PERIOD_FORMAT_NONE = 0x00; private static final int VALIDITY_PERIOD_FORMAT_ENHANCED = 0x01; private static final int VALIDITY_PERIOD_FORMAT_RELATIVE = 0x02; private static final int VALIDITY_PERIOD_FORMAT_ABSOLUTE = 0x03; - //Validity Period min - 5 mins + // Validity Period min - 5 mins private static final int VALIDITY_PERIOD_MIN = 5; - //Validity Period max - 63 weeks + // Validity Period max - 63 weeks private static final int VALIDITY_PERIOD_MAX = 635040; private static final int INVALID_VALIDITY_PERIOD = -1; @@ -140,38 +142,6 @@ public class SmsMessage extends SmsMessageBase { } /** - * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the - * +CMT unsolicited response (PDU mode, of course) - * +CMT: [<alpha>],<length><CR><LF><pdu> - * - * Only public for debugging - * - * {@hide} - */ - public static SmsMessage newFromCMT(byte[] pdu) { - try { - SmsMessage msg = new SmsMessage(); - msg.parsePdu(pdu); - return msg; - } catch (RuntimeException ex) { - Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex); - return null; - } - } - - /** @hide */ - public static SmsMessage newFromCDS(byte[] pdu) { - try { - SmsMessage msg = new SmsMessage(); - msg.parsePdu(pdu); - return msg; - } catch (RuntimeException ex) { - Rlog.e(LOG_TAG, "CDS SMS PDU parsing failed: ", ex); - return null; - } - } - - /** * Creates an SmsMessage from an SMS EF record. * * @param index Index of SMS EF record. @@ -225,20 +195,20 @@ public class SmsMessage extends SmsMessageBase { } /** - * Get Encoded Relative Validty Period Value from Validity period in mins. + * Gets Encoded Relative Validity Period Value from Validity period in mins. * * @param validityPeriod Validity period in mins. * * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1. - * ||relValidityPeriod (TP-VP) || || validityPeriod || - * - * 0 to 143 ---> (TP-VP + 1) x 5 minutes - * - * 144 to 167 ---> 12 hours + ((TP-VP -143) x 30 minutes) - * - * 168 to 196 ---> (TP-VP - 166) x 1 day - * - * 197 to 255 ---> (TP-VP - 192) x 1 week + * ------------------------------------------------------------ + * TP-VP | Validity period + * (Relative format) | value + * ------------------------------------------------------------ + * 0 to 143 | (TP-VP + 1) x 5 minutes + * 144 to 167 | 12 hours + ((TP-VP -143) x 30 minutes) + * 168 to 196 | (TP-VP - 166) x 1 day + * 197 to 255 | (TP-VP - 192) x 1 week + * ------------------------------------------------------------ * * @return relValidityPeriod Encoded Relative Validity Period Value. * @hide @@ -246,19 +216,16 @@ public class SmsMessage extends SmsMessageBase { public static int getRelativeValidityPeriod(int validityPeriod) { int relValidityPeriod = INVALID_VALIDITY_PERIOD; - if (validityPeriod < VALIDITY_PERIOD_MIN || validityPeriod > VALIDITY_PERIOD_MAX) { - Rlog.e(LOG_TAG,"Invalid Validity Period" + validityPeriod); - return relValidityPeriod; - } - - if (validityPeriod <= 720) { - relValidityPeriod = (validityPeriod / 5) - 1; - } else if (validityPeriod <= 1440) { - relValidityPeriod = ((validityPeriod - 720) / 30) + 143; - } else if (validityPeriod <= 43200) { - relValidityPeriod = (validityPeriod / 1440) + 166; - } else if (validityPeriod <= 635040) { - relValidityPeriod = (validityPeriod / 10080) + 192; + if (validityPeriod >= VALIDITY_PERIOD_MIN) { + if (validityPeriod <= 720) { + relValidityPeriod = (validityPeriod / 5) - 1; + } else if (validityPeriod <= 1440) { + relValidityPeriod = ((validityPeriod - 720) / 30) + 143; + } else if (validityPeriod <= 43200) { + relValidityPeriod = (validityPeriod / 1440) + 166; + } else if (validityPeriod <= VALIDITY_PERIOD_MAX) { + relValidityPeriod = (validityPeriod / 10080) + 192; + } } return relValidityPeriod; } @@ -368,17 +335,19 @@ public class SmsMessage extends SmsMessageBase { SubmitPdu ret = new SubmitPdu(); - int validityPeriodFormat = VALIDITY_PERIOD_FORMAT_NONE; - int relativeValidityPeriod = INVALID_VALIDITY_PERIOD; + int relativeValidityPeriod = getRelativeValidityPeriod(validityPeriod); - // TP-Validity-Period-Format (TP-VPF) in 3GPP TS 23.040 V6.8.1 section 9.2.3.3 - //bit 4:3 = 10 - TP-VP field present - relative format - if((relativeValidityPeriod = getRelativeValidityPeriod(validityPeriod)) >= 0) { - validityPeriodFormat = VALIDITY_PERIOD_FORMAT_RELATIVE; + byte mtiByte = 0x01; // SMS-SUBMIT + + if (header != null) { + // Set TP-UDHI + mtiByte |= 0x40; } - byte mtiByte = (byte)(0x01 | (validityPeriodFormat << 0x03) | - (header != null ? 0x40 : 0x00)); + if (relativeValidityPeriod != INVALID_VALIDITY_PERIOD) { + // Set TP-Validity-Period-Format (TP-VPF) + mtiByte |= VALIDITY_PERIOD_FORMAT_RELATIVE << 3; + } ByteArrayOutputStream bo = getSubmitPduHead( scAddress, destinationAddress, mtiByte, @@ -450,8 +419,8 @@ public class SmsMessage extends SmsMessageBase { bo.write(0x08); } - if (validityPeriodFormat == VALIDITY_PERIOD_FORMAT_RELATIVE) { - // ( TP-Validity-Period - relative format) + // TP-Validity-Period (TP-VP) + if (relativeValidityPeriod != INVALID_VALIDITY_PERIOD) { bo.write(relativeValidityPeriod); } @@ -888,10 +857,9 @@ public class SmsMessage extends SmsMessageBase { } /** - * Parses an SC timestamp and returns a currentTimeMillis()-style - * timestamp + * Parses an SC timestamp and returns a currentTimeMillis()-style timestamp, or 0 if + * invalid. */ - long getSCTimestampMillis() { // TP-Service-Centre-Time-Stamp int year = IccUtils.gsmBcdByteToInt(mPdu[mCur++]); @@ -917,16 +885,22 @@ public class SmsMessage extends SmsMessageBase { // It's 2006. Should I really support years < 2000? int fullYear = year >= 90 ? year + 1900 : year + 2000; - LocalDateTime localDateTime = LocalDateTime.of( - fullYear, - month /* 1-12 */, - day, - hour, - minute, - second); - long epochSeconds = localDateTime.toEpochSecond(ZoneOffset.UTC) - timeZoneOffsetSeconds; - // Convert to milliseconds. - return epochSeconds * 1000; + try { + LocalDateTime localDateTime = LocalDateTime.of( + fullYear, + month /* 1-12 */, + day, + hour, + minute, + second); + long epochSeconds = + localDateTime.toEpochSecond(ZoneOffset.UTC) - timeZoneOffsetSeconds; + // Convert to milliseconds. + return epochSeconds * 1000; + } catch (DateTimeException ex) { + Rlog.e(LOG_TAG, "Invalid timestamp", ex); + } + return 0; } /** @@ -1277,6 +1251,7 @@ public class SmsMessage extends SmsMessageBase { mRecipientAddress = p.getAddress(); // TP-Service-Centre-Time-Stamp mScTimeMillis = p.getSCTimestampMillis(); + // TP-Discharge-Time p.getSCTimestampMillis(); // TP-Status mStatus = p.getByte(); @@ -1335,6 +1310,7 @@ public class SmsMessage extends SmsMessageBase { + " data coding scheme: " + mDataCodingScheme); } + // TP-Service-Centre-Time-Stamp mScTimeMillis = p.getSCTimestampMillis(); if (VDBG) Rlog.d(LOG_TAG, "SMS SC timestamp: " + mScTimeMillis); @@ -1377,23 +1353,17 @@ public class SmsMessage extends SmsMessageBase { // TP-Validity-Period-Format int validityPeriodLength = 0; - int validityPeriodFormat = ((firstByte>>3) & 0x3); - if (0x0 == validityPeriodFormat) /* 00, TP-VP field not present*/ - { + int validityPeriodFormat = ((firstByte >> 3) & 0x3); + if (validityPeriodFormat == VALIDITY_PERIOD_FORMAT_NONE) { validityPeriodLength = 0; - } - else if (0x2 == validityPeriodFormat) /* 10, TP-VP: relative format*/ - { + } else if (validityPeriodFormat == VALIDITY_PERIOD_FORMAT_RELATIVE) { validityPeriodLength = 1; - } - else /* other case, 11 or 01, TP-VP: absolute or enhanced format*/ - { + } else { // VALIDITY_PERIOD_FORMAT_ENHANCED or VALIDITY_PERIOD_FORMAT_ABSOLUTE validityPeriodLength = 7; } // TP-Validity-Period is not used on phone, so just ignore it for now. - while (validityPeriodLength-- > 0) - { + while (validityPeriodLength-- > 0) { p.getByte(); } diff --git a/test-base/Android.bp b/test-base/Android.bp index 69c296e7ee9c..c7c9fc739189 100644 --- a/test-base/Android.bp +++ b/test-base/Android.bp @@ -38,6 +38,7 @@ java_sdk_library { ], compile_dex: true, + default_to_stubs: true, } // Build the android.test.base_static library diff --git a/test-base/src/android/test/InstrumentationTestCase.java b/test-base/src/android/test/InstrumentationTestCase.java index 6b79314a4385..9f7a2fa44dc2 100644 --- a/test-base/src/android/test/InstrumentationTestCase.java +++ b/test-base/src/android/test/InstrumentationTestCase.java @@ -34,9 +34,9 @@ import junit.framework.TestCase; * A test case that has access to {@link Instrumentation}. * * @deprecated Use - * <a href="{@docRoot}reference/android/support/test/InstrumentationRegistry.html"> + * <a href="{@docRoot}reference/androidx/test/platform/app/InstrumentationRegistry.html"> * InstrumentationRegistry</a> instead. New tests should be written using the - * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>. + * <a href="{@docRoot}training/testing/index.html">AndroidX Test Library</a>. */ @Deprecated public class InstrumentationTestCase extends TestCase { diff --git a/test-mock/Android.bp b/test-mock/Android.bp index 248c117d2e03..7d0f92fac4c7 100644 --- a/test-mock/Android.bp +++ b/test-mock/Android.bp @@ -37,6 +37,7 @@ java_sdk_library { "android.test.mock", ], compile_dex: true, + default_to_stubs: true, } // Make the current.txt available for use by the cts/tests/signature tests. diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java index e9a5ff70a7cc..3206adc31b98 100644 --- a/test-mock/src/android/test/mock/MockContentProvider.java +++ b/test-mock/src/android/test/mock/MockContentProvider.java @@ -29,6 +29,7 @@ import android.content.pm.ProviderInfo; import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.net.Uri; +import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.ICancellationSignal; @@ -282,7 +283,7 @@ public class MockContentProvider extends ContentProvider { * @hide */ public IBinder getIContentProviderBinder() { - throw new UnsupportedOperationException("unimplemented mock method"); + return new Binder(); } /** diff --git a/test-runner/Android.bp b/test-runner/Android.bp index 75f5b5a96eb1..1f6db8403eee 100644 --- a/test-runner/Android.bp +++ b/test-runner/Android.bp @@ -41,6 +41,7 @@ java_sdk_library { ], compile_dex: true, + default_to_stubs: true, } // Build the android.test.runner-minus-junit library diff --git a/test-runner/api/current.txt b/test-runner/api/current.txt index 2c19a2e85410..5407b685bb34 100644 --- a/test-runner/api/current.txt +++ b/test-runner/api/current.txt @@ -78,6 +78,7 @@ package android.test { @Deprecated public class InstrumentationTestRunner extends android.app.Instrumentation implements android.test.TestSuiteProvider { ctor @Deprecated public InstrumentationTestRunner(); + method @Deprecated protected void addTestListener(junit.framework.TestListener); method @Deprecated public junit.framework.TestSuite getAllTests(); method @Deprecated protected android.test.AndroidTestRunner getAndroidTestRunner(); method @Deprecated public android.os.Bundle getArguments(); diff --git a/test-runner/src/android/test/InstrumentationTestRunner.java b/test-runner/src/android/test/InstrumentationTestRunner.java index b2582c19b548..07e3f8736cc8 100644 --- a/test-runner/src/android/test/InstrumentationTestRunner.java +++ b/test-runner/src/android/test/InstrumentationTestRunner.java @@ -410,7 +410,6 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu /** * Add a {@link TestListener} - * @hide */ protected void addTestListener(TestListener listener){ if(mTestRunner!=null && listener!=null){ diff --git a/tests/DynamicCodeLoggerIntegrationTests/Android.mk b/tests/DynamicCodeLoggerIntegrationTests/Android.mk index 62c1ba89653c..2d58ce8baddc 100644 --- a/tests/DynamicCodeLoggerIntegrationTests/Android.mk +++ b/tests/DynamicCodeLoggerIntegrationTests/Android.mk @@ -36,8 +36,7 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_MODULE := DynamicCodeLoggerNativeTestLibrary LOCAL_SRC_FILES := src/cpp/com_android_dcl_Jni.cpp -LOCAL_C_INCLUDES += \ - $(JNI_H_INCLUDE) +LOCAL_HEADER_LIBRARIES := jni_headers LOCAL_SDK_VERSION := 28 LOCAL_NDK_STL_VARIANT := c++_static diff --git a/tests/net/java/android/net/DnsPacketTest.java b/tests/net/java/android/net/DnsPacketTest.java deleted file mode 100644 index 975abf416944..000000000000 --- a/tests/net/java/android/net/DnsPacketTest.java +++ /dev/null @@ -1,159 +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.net; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.Arrays; -import java.util.List; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class DnsPacketTest { - private void assertHeaderParses(DnsPacket.DnsHeader header, int id, int flag, - int qCount, int aCount, int nsCount, int arCount) { - assertEquals(header.id, id); - assertEquals(header.flags, flag); - assertEquals(header.getRecordCount(DnsPacket.QDSECTION), qCount); - assertEquals(header.getRecordCount(DnsPacket.ANSECTION), aCount); - assertEquals(header.getRecordCount(DnsPacket.NSSECTION), nsCount); - assertEquals(header.getRecordCount(DnsPacket.ARSECTION), arCount); - } - - private void assertRecordParses(DnsPacket.DnsRecord record, String dname, - int dtype, int dclass, int ttl, byte[] rr) { - assertEquals(record.dName, dname); - assertEquals(record.nsType, dtype); - assertEquals(record.nsClass, dclass); - assertEquals(record.ttl, ttl); - assertTrue(Arrays.equals(record.getRR(), rr)); - } - - class TestDnsPacket extends DnsPacket { - TestDnsPacket(byte[] data) throws ParseException { - super(data); - } - - public DnsHeader getHeader() { - return mHeader; - } - public List<DnsRecord> getRecordList(int secType) { - return mRecords[secType]; - } - } - - @Test - public void testNullDisallowed() { - try { - new TestDnsPacket(null); - fail("Exception not thrown for null byte array"); - } catch (ParseException e) { - } - } - - @Test - public void testV4Answer() throws Exception { - final byte[] v4blob = new byte[] { - /* Header */ - 0x55, 0x66, /* Transaction ID */ - (byte) 0x81, (byte) 0x80, /* Flags */ - 0x00, 0x01, /* Questions */ - 0x00, 0x01, /* Answer RRs */ - 0x00, 0x00, /* Authority RRs */ - 0x00, 0x00, /* Additional RRs */ - /* Queries */ - 0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65, - 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */ - 0x00, 0x01, /* Type */ - 0x00, 0x01, /* Class */ - /* Answers */ - (byte) 0xc0, 0x0c, /* Name */ - 0x00, 0x01, /* Type */ - 0x00, 0x01, /* Class */ - 0x00, 0x00, 0x01, 0x2b, /* TTL */ - 0x00, 0x04, /* Data length */ - (byte) 0xac, (byte) 0xd9, (byte) 0xa1, (byte) 0x84 /* Address */ - }; - TestDnsPacket packet = new TestDnsPacket(v4blob); - - // Header part - assertHeaderParses(packet.getHeader(), 0x5566, 0x8180, 1, 1, 0, 0); - - // Record part - List<DnsPacket.DnsRecord> qdRecordList = - packet.getRecordList(DnsPacket.QDSECTION); - assertEquals(qdRecordList.size(), 1); - assertRecordParses(qdRecordList.get(0), "www.google.com", 1, 1, 0, null); - - List<DnsPacket.DnsRecord> anRecordList = - packet.getRecordList(DnsPacket.ANSECTION); - assertEquals(anRecordList.size(), 1); - assertRecordParses(anRecordList.get(0), "www.google.com", 1, 1, 0x12b, - new byte[]{ (byte) 0xac, (byte) 0xd9, (byte) 0xa1, (byte) 0x84 }); - } - - @Test - public void testV6Answer() throws Exception { - final byte[] v6blob = new byte[] { - /* Header */ - 0x77, 0x22, /* Transaction ID */ - (byte) 0x81, (byte) 0x80, /* Flags */ - 0x00, 0x01, /* Questions */ - 0x00, 0x01, /* Answer RRs */ - 0x00, 0x00, /* Authority RRs */ - 0x00, 0x00, /* Additional RRs */ - /* Queries */ - 0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65, - 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */ - 0x00, 0x1c, /* Type */ - 0x00, 0x01, /* Class */ - /* Answers */ - (byte) 0xc0, 0x0c, /* Name */ - 0x00, 0x1c, /* Type */ - 0x00, 0x01, /* Class */ - 0x00, 0x00, 0x00, 0x37, /* TTL */ - 0x00, 0x10, /* Data length */ - 0x24, 0x04, 0x68, 0x00, 0x40, 0x05, 0x08, 0x0d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x04 /* Address */ - }; - TestDnsPacket packet = new TestDnsPacket(v6blob); - - // Header part - assertHeaderParses(packet.getHeader(), 0x7722, 0x8180, 1, 1, 0, 0); - - // Record part - List<DnsPacket.DnsRecord> qdRecordList = - packet.getRecordList(DnsPacket.QDSECTION); - assertEquals(qdRecordList.size(), 1); - assertRecordParses(qdRecordList.get(0), "www.google.com", 28, 1, 0, null); - - List<DnsPacket.DnsRecord> anRecordList = - packet.getRecordList(DnsPacket.ANSECTION); - assertEquals(anRecordList.size(), 1); - assertRecordParses(anRecordList.get(0), "www.google.com", 28, 1, 0x37, - new byte[]{ 0x24, 0x04, 0x68, 0x00, 0x40, 0x05, 0x08, 0x0d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x04 }); - } -} diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/tests/net/java/android/net/NetworkTemplateTest.kt index 5dd0fda4da28..9ba56e44fe88 100644 --- a/tests/net/java/android/net/NetworkTemplateTest.kt +++ b/tests/net/java/android/net/NetworkTemplateTest.kt @@ -26,6 +26,7 @@ import android.net.NetworkStats.METERED_ALL import android.net.NetworkStats.ROAMING_ALL import android.net.NetworkTemplate.MATCH_MOBILE import android.net.NetworkTemplate.MATCH_WIFI +import android.net.NetworkTemplate.NETWORK_TYPE_5G_NSA import android.net.NetworkTemplate.NETWORK_TYPE_ALL import android.net.NetworkTemplate.buildTemplateMobileWithRatType import android.telephony.TelephonyManager @@ -145,11 +146,13 @@ class NetworkTemplateTest { assertParcelSane(templateWifi, 8) } - // Verify NETWORK_TYPE_ALL does not conflict with TelephonyManager#NETWORK_TYPE_* constants. + // Verify NETWORK_TYPE_* constants in NetworkTemplate do not conflict with + // TelephonyManager#NETWORK_TYPE_* constants. @Test - fun testNetworkTypeAll() { + fun testNetworkTypeConstants() { for (ratType in TelephonyManager.getAllNetworkTypes()) { assertNotEquals(NETWORK_TYPE_ALL, ratType) + assertNotEquals(NETWORK_TYPE_5G_NSA, ratType) } } } diff --git a/tests/net/java/android/net/NetworkUtilsTest.java b/tests/net/java/android/net/NetworkUtilsTest.java index 7748288aeb05..3158cc8637e4 100644 --- a/tests/net/java/android/net/NetworkUtilsTest.java +++ b/tests/net/java/android/net/NetworkUtilsTest.java @@ -16,10 +16,24 @@ package android.net; +import static android.system.OsConstants.AF_INET; +import static android.system.OsConstants.AF_INET6; +import static android.system.OsConstants.AF_UNIX; +import static android.system.OsConstants.EPERM; +import static android.system.OsConstants.SOCK_DGRAM; +import static android.system.OsConstants.SOCK_STREAM; + import static junit.framework.Assert.assertEquals; +import static org.junit.Assert.fail; + +import android.system.ErrnoException; +import android.system.Os; + import androidx.test.runner.AndroidJUnit4; +import libcore.io.IoUtils; + import org.junit.Test; import org.junit.runner.RunWith; @@ -125,4 +139,50 @@ public class NetworkUtilsTest { assertEquals(BigInteger.valueOf(7l - 4 + 4 + 16 + 65536), NetworkUtils.routedIPv6AddressCount(set)); } + + private static void expectSocketSuccess(String msg, int domain, int type) { + try { + IoUtils.closeQuietly(Os.socket(domain, type, 0)); + } catch (ErrnoException e) { + fail(msg + e.getMessage()); + } + } + + private static void expectSocketPemissionError(String msg, int domain, int type) { + try { + IoUtils.closeQuietly(Os.socket(domain, type, 0)); + fail(msg); + } catch (ErrnoException e) { + assertEquals(msg, e.errno, EPERM); + } + } + + private static void expectHasNetworking() { + expectSocketSuccess("Creating a UNIX socket should not have thrown ErrnoException", + AF_UNIX, SOCK_STREAM); + expectSocketSuccess("Creating a AF_INET socket shouldn't have thrown ErrnoException", + AF_INET, SOCK_DGRAM); + expectSocketSuccess("Creating a AF_INET6 socket shouldn't have thrown ErrnoException", + AF_INET6, SOCK_DGRAM); + } + + private static void expectNoNetworking() { + expectSocketSuccess("Creating a UNIX socket should not have thrown ErrnoException", + AF_UNIX, SOCK_STREAM); + expectSocketPemissionError( + "Creating a AF_INET socket should have thrown ErrnoException(EPERM)", + AF_INET, SOCK_DGRAM); + expectSocketPemissionError( + "Creating a AF_INET6 socket should have thrown ErrnoException(EPERM)", + AF_INET6, SOCK_DGRAM); + } + + @Test + public void testSetAllowNetworkingForProcess() { + expectHasNetworking(); + NetworkUtils.setAllowNetworkingForProcess(false); + expectNoNetworking(); + NetworkUtils.setAllowNetworkingForProcess(true); + expectHasNetworking(); + } } diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java index 76e3e2fced6f..fdc6084884f6 100644 --- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java @@ -76,7 +76,6 @@ import com.android.server.LocalServices; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; @@ -88,7 +87,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Set; - @RunWith(AndroidJUnit4.class) @SmallTest public class PermissionMonitorTest { @@ -116,8 +114,8 @@ public class PermissionMonitorTest { @Mock private INetd mNetdService; @Mock private PackageManagerInternal mMockPmi; @Mock private UserManager mUserManager; + @Mock private PermissionMonitor.Dependencies mDeps; - private PackageManagerInternal.PackageListObserver mObserver; private PermissionMonitor mPermissionMonitor; @Before @@ -131,7 +129,7 @@ public class PermissionMonitorTest { new UserInfo(MOCK_USER2, "", 0), })); - mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService)); + mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps)); LocalServices.removeServiceForTest(PackageManagerInternal.class); LocalServices.addService(PackageManagerInternal.class, mMockPmi); @@ -139,11 +137,7 @@ public class PermissionMonitorTest { /* observer */ null)); when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(/* empty app list */ null); mPermissionMonitor.startMonitoring(); - - final ArgumentCaptor<PackageManagerInternal.PackageListObserver> observerCaptor = - ArgumentCaptor.forClass(PackageManagerInternal.PackageListObserver.class); - verify(mMockPmi).getPackageList(observerCaptor.capture()); - mObserver = observerCaptor.getValue(); + verify(mMockPmi).getPackageList(mPermissionMonitor); } private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion, int uid, @@ -290,14 +284,14 @@ public class PermissionMonitorTest { @Test public void testHasRestrictedNetworkPermissionSystemUid() { - doReturn(VERSION_P).when(mPermissionMonitor).getDeviceFirstSdkInt(); + doReturn(VERSION_P).when(mDeps).getDeviceFirstSdkInt(); assertTrue(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID)); assertTrue(hasRestrictedNetworkPermission( PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_INTERNAL)); assertTrue(hasRestrictedNetworkPermission( PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); - doReturn(VERSION_Q).when(mPermissionMonitor).getDeviceFirstSdkInt(); + doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt(); assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID)); assertFalse(hasRestrictedNetworkPermission( PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CONNECTIVITY_INTERNAL)); @@ -450,13 +444,13 @@ public class PermissionMonitorTest { new int[]{MOCK_UID1}); // Remove MOCK_UID1, expect no permission left for all user. - mPermissionMonitor.onPackageRemoved(MOCK_UID1); - removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_UID1); + mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1); + removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1); mNetdMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2}, new int[]{MOCK_UID1}); // Remove SYSTEM_PACKAGE1, expect permission downgrade. when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{SYSTEM_PACKAGE2}); - removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, SYSTEM_UID); + removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, SYSTEM_PACKAGE1, SYSTEM_UID); mNetdMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2}, new int[]{SYSTEM_UID}); @@ -465,7 +459,7 @@ public class PermissionMonitorTest { // Remove all packages, expect no permission left. when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{}); - removePackageForUsers(new int[]{MOCK_USER2}, SYSTEM_UID); + removePackageForUsers(new int[]{MOCK_USER2}, SYSTEM_PACKAGE2, SYSTEM_UID); mNetdMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2}, new int[]{SYSTEM_UID, MOCK_UID1}); @@ -501,7 +495,8 @@ public class PermissionMonitorTest { reset(mNetdService); // When MOCK_UID1 package is uninstalled and reinstalled, expect Netd to be updated - mPermissionMonitor.onPackageRemoved(UserHandle.getUid(MOCK_USER1, MOCK_UID1)); + mPermissionMonitor.onPackageRemoved( + MOCK_PACKAGE1, UserHandle.getUid(MOCK_USER1, MOCK_UID1)); verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID1})); mPermissionMonitor.onPackageAdded(MOCK_PACKAGE1, UserHandle.getUid(MOCK_USER1, MOCK_UID1)); verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), @@ -545,7 +540,8 @@ public class PermissionMonitorTest { aryEq(new int[] {MOCK_UID1})); // Removed package should have its uid rules removed - mPermissionMonitor.onPackageRemoved(UserHandle.getUid(MOCK_USER1, MOCK_UID1)); + mPermissionMonitor.onPackageRemoved( + MOCK_PACKAGE1, UserHandle.getUid(MOCK_USER1, MOCK_UID1)); verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID1})); } @@ -559,9 +555,9 @@ public class PermissionMonitorTest { } } - private void removePackageForUsers(int[] users, int uid) { + private void removePackageForUsers(int[] users, String packageName, int uid) { for (final int user : users) { - mPermissionMonitor.onPackageRemoved(UserHandle.getUid(user, uid)); + mPermissionMonitor.onPackageRemoved(packageName, UserHandle.getUid(user, uid)); } } @@ -647,7 +643,7 @@ public class PermissionMonitorTest { private PackageInfo addPackage(String packageName, int uid, String[] permissions) throws Exception { PackageInfo packageInfo = setPackagePermissions(packageName, uid, permissions); - mObserver.onPackageAdded(packageName, uid); + mPermissionMonitor.onPackageAdded(packageName, uid); return packageInfo; } @@ -678,7 +674,7 @@ public class PermissionMonitorTest { when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE2), anyInt())).thenReturn(packageInfo2); when(mPackageManager.getPackagesForUid(MOCK_UID1)) .thenReturn(new String[]{MOCK_PACKAGE1, MOCK_PACKAGE2}); - mObserver.onPackageAdded(MOCK_PACKAGE2, MOCK_UID1); + mPermissionMonitor.onPackageAdded(MOCK_PACKAGE2, MOCK_UID1); mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); } @@ -692,7 +688,7 @@ public class PermissionMonitorTest { | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{}); - mObserver.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1); + mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1); mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[]{MOCK_UID1}); } @@ -705,7 +701,7 @@ public class PermissionMonitorTest { | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{}); - mObserver.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1); + mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1); mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[]{MOCK_UID1}); addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET}); @@ -719,10 +715,7 @@ public class PermissionMonitorTest { addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {}); mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{MOCK_UID1}); - // When updating a package, the broadcast receiver gets two broadcasts (a remove and then an - // add), but the observer sees only one callback (an update). - setPackagePermissions(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET}); - mObserver.onPackageChanged(MOCK_PACKAGE1, MOCK_UID1); + addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET}); mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1}); } @@ -740,7 +733,7 @@ public class PermissionMonitorTest { when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{ MOCK_PACKAGE2}); - mObserver.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1); + mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1); mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1}); } diff --git a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java index c813269744ef..9531b0a5bb66 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java @@ -17,6 +17,7 @@ package com.android.server.net; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; @@ -30,7 +31,9 @@ import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.content.Context; +import android.net.NetworkTemplate; import android.os.test.TestLooper; +import android.telephony.NetworkRegistrationInfo; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SubscriptionManager; @@ -61,7 +64,6 @@ public final class NetworkStatsSubscriptionsMonitorTest { private static final String TEST_IMSI3 = "466929999999999"; @Mock private Context mContext; - @Mock private PhoneStateListener mPhoneStateListener; @Mock private SubscriptionManager mSubscriptionManager; @Mock private TelephonyManager mTelephonyManager; @Mock private NetworkStatsSubscriptionsMonitor.Delegate mDelegate; @@ -215,4 +217,55 @@ public final class NetworkStatsSubscriptionsMonitorTest { verify(mTelephonyManager, times(2)).listen(any(), eq(PhoneStateListener.LISTEN_NONE)); assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); } + + + @Test + public void test5g() { + mMonitor.start(); + // Insert sim1, verify RAT type is NETWORK_TYPE_UNKNOWN, and never get any callback + // before changing RAT type. Also capture listener for later use. + addTestSub(TEST_SUBID1, TEST_IMSI1); + assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); + final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor = + ArgumentCaptor.forClass(RatTypeListener.class); + verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor.capture(), + eq(PhoneStateListener.LISTEN_SERVICE_STATE)); + final RatTypeListener listener = CollectionUtils + .find(ratTypeListenerCaptor.getAllValues(), it -> it.getSubId() == TEST_SUBID1); + assertNotNull(listener); + + // Set RAT type to 5G NSA (non-standalone) mode, verify the monitor outputs + // NETWORK_TYPE_5G_NSA. + final ServiceState serviceState = mock(ServiceState.class); + when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE); + when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED); + listener.onServiceStateChanged(serviceState); + assertRatTypeChangedForSub(TEST_IMSI1, NetworkTemplate.NETWORK_TYPE_5G_NSA); + reset(mDelegate); + + // Set RAT type to LTE without NR connected, the RAT type should be downgraded to LTE. + when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_NONE); + listener.onServiceStateChanged(serviceState); + assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_LTE); + reset(mDelegate); + + // Verify NR connected with other RAT type does not take effect. + when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_UMTS); + when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED); + listener.onServiceStateChanged(serviceState); + assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS); + reset(mDelegate); + + // Set RAT type to 5G standalone mode, the RAT type should be NR. + setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1, + TelephonyManager.NETWORK_TYPE_NR); + assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_NR); + reset(mDelegate); + + // Set NR state to none in standalone mode does not change anything. + when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_NR); + when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_NONE); + listener.onServiceStateChanged(serviceState); + assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_NR); + } } |